Wires up the prod deployment per architecture-doc conventions:
- api → nikola.kosherinata.internal, loopback bind 127.0.0.1:42424
(less-common port, registered with SELinux as http_port_t).
- worker → frootmig.kosherinata.internal, no listening port.
- web (static ui/dist + nginx server_name rob.tn) → nikola, with
/api/* reverse-proxied to the loopback API.
- db → existing magrathea cluster via mTLS, hostname-baked DATABASE_URL
rendered into /etc/moments/{api,worker}.env at deploy time.
Cert rotation: step-ca renews host certs every 24h; .path units watch
/etc/pki/tls/misc/<host>.pem and trigger systemctl restart of the
relevant service. Both binaries hold cert state in rustls and read
once at startup, so restart is the right reload semantics.
deploy.sh contract matches the architecture doc: positional env arg,
component list (or `all` / `default`), --dry-run support. Renders
config templates from `pass`, rsyncs over ssh+sudo, runs sysusers /
restorecon / semanage / systemctl / nginx -t idempotently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72 lines
3.4 KiB
Markdown
72 lines
3.4 KiB
Markdown
# moments
|
|
|
|
Personal activity timeline for [rob.tn](https://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](https://github.com/grenade/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](https://git.lair.cafe/grenade/architecture/src/branch/main/generic.md).
|
|
|
|
## Local development
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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
|
|
|
|
```sh
|
|
./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 `127.0.0.1:42424`, fronted by local nginx |
|
|
| worker | `frootmig.kosherinata.internal` | no listening port; pollers only |
|
|
| web | `nikola.kosherinata.internal` | static `ui/dist/` under `/var/www/moments` |
|
|
| 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.internal` → `moments_ro` and `frootmig.kosherinata.internal` → `moments_rw`. See `asset/sql/bootstrap-moments.sql` and `asset/postgres/ident.conf.tmpl`.
|
|
|
|
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](https://github.com/grenade/grenade-events-react) pointing at this repo, and archive the GitHub repo.
|