Files
moments/readme.md
rob thijssen 52b7d0be9b fix(deploy): split ingress to oolon, expose api on nikola interface
The per-site nginx ingress for rob.tn lives on oolon (the host the
external router forwards 443 traffic to), not on nikola. Adjust the
topology so:

- web (static ui + nginx) → oolon.hanzalova.internal
- api binds 0.0.0.0:42424 on nikola.kosherinata.internal so oolon
  can reverse-proxy across the WG mesh
- new firewalld service moments-api opens 42424 in the default zone
  on nikola
- oolon labels port 42424 http_port_t so httpd_t may name_connect
  outbound to it (httpd_can_network_connect was already set)
- nginx ssl_certificate switched to oolon's host cert; upstream
  rewritten to nikola.kosherinata.internal:42424

Plaintext between oolon and nikola for now — the WG mesh provides
the encryption layer and the data is already public. Documented
the deferral so a future move to per-hop mTLS is obvious.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:20:07 +03:00

3.8 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 nikola.kosherinata.internal binds 0.0.0.0:42424; firewalld service moments-api
worker frootmig.kosherinata.internal no listening port; pollers only
web oolon.hanzalova.internal per-site nginx ingress for rob.tn; /api/* → nikola across the WG
db magrathea.kosherinata.internal postgres mTLS, passwordless

Postgres roles moments_rw and moments_ro must exist on the primary, with pg_ident.conf mappings in place for nikola.kosherinata.internalmoments_ro and frootmig.kosherinata.internalmoments_rw. 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://nikola.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:

  1. Update Cloudflare DNS for rob.tn to the WAN IP that fronts nikola (unproxied — see architecture doc §11).
  2. Confirm curl -fsS https://rob.tn/api/v1/healthz returns ok.
  3. Add an archival notice to the top of grenade-events-react/readme.md pointing at this repo, and archive the GitHub repo.