nikola and frootmig are flagging power events and drive warnings on the iLO interface and need drive replacement. Move both moments components onto anjie.kosherinata.internal until those hosts are back in service. Update the nginx upstream and the readme topology table to match; the postgres pg_ident.conf on magrathea now needs to map anjie's cert CN to both moments_ro and moments_rw (two lines for the same cert_cn). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.9 KiB
moments
Personal activity timeline for rob.tn. Polls public sources (GitHub, Gitea, hg-edge.mozilla.org, bugzilla.mozilla.org), stores raw payloads in Postgres, and serves a reshaped timeline to a static React frontend.
Successor to the now-defunct grenade-events-react, which depended on MongoDB Stitch (retired by MongoDB in September 2022).
Layout
crates/
moments-entities/ # types and DTOs
moments-core/ # ingestion + reshape logic
moments-data/ # postgres adapter + migrations
moments-api/ # axum read-only HTTP API (binary)
moments-worker/ # ingestion daemon (binary)
ui/ # vite + react + swc + ts frontend
asset/ # systemd, nginx, firewalld, manifest.yml
script/deploy.sh
Architectural conventions follow grenade/architecture/generic.md.
Local development
cargo build --workspace
cargo run -p moments-api # serves on 127.0.0.1:8080
cargo run -p moments-worker # one-shot ingest tick (until --interval is wired up)
The API expects a Postgres reachable at DATABASE_URL. For magrathea, that's an mTLS connection using the host cert. For local dev against a throwaway database:
DATABASE_URL=postgres://localhost/moments cargo run -p moments-api
Migrations live in crates/moments-data/migrations/ and run automatically on worker startup. The API connects as moments_ro and never runs migrations — the worker (as moments_rw) is the schema owner.
Deployment
./script/deploy.sh prod all # api + worker + web
./script/deploy.sh prod api worker # subset
./script/deploy.sh prod default # api + web only (worker untouched)
./script/deploy.sh prod all --dry-run
Topology:
| Component | Host | Notes |
|---|---|---|
| api | anjie.kosherinata.internal |
binds 0.0.0.0:42424; firewalld service moments-api |
| worker | anjie.kosherinata.internal |
no listening port; pollers only |
| web | oolon.kosherinata.internal |
per-site nginx ingress for rob.tn; /api/* → anjie across the WG |
| db | magrathea.kosherinata.internal |
postgres mTLS, passwordless |
api and worker are co-located on anjie while nikola and frootmig are out for drive replacement.
Postgres roles moments_rw and moments_ro must exist on the primary, with pg_ident.conf mapping anjie.kosherinata.internal to both roles (one cert_cn line per mapping). See asset/sql/bootstrap-moments.sql and asset/postgres/ident.conf.tmpl.
Inter-host traffic over the WG mesh: oolon's nginx connects to http://anjie.kosherinata.internal:42424 in plaintext. The mesh provides the encryption layer; per-hop TLS for an internal HTTP read-only API on already-public data is deferred. If that changes, swap the api binary to rustls + the host cert pair, and update the nginx upstream to https://.
Secrets resolved by deploy.sh via pass:
github.com/grenade/admin-token— GitHub PAT for events + search APIs (worker only).
Optional, set if needed in worker.env: GITEA_TOKEN, BUGZILLA_API_KEY.
DNS cutover
rob.tn currently resolves to GitHub Pages. After the first successful prod deploy:
- Update Cloudflare DNS for
rob.tnto the WAN IP that frontsoolon(unproxied — see architecture doc §11). - Confirm
curl -fsS https://rob.tn/api/v1/healthzreturnsok. - Add an archival notice to the top of grenade-events-react/readme.md pointing at this repo, and archive the GitHub repo.