feat(container): manifest-declared generated secrets + companion/quadlet hardening
Generated-secrets system: apps declare `generated_secrets` in their manifest (kinds hex16/hex32/bcrypt); `container::secrets::ensure_generated_secrets` materialises them 0600/rootless in resolve_dynamic_env — idempotent and self-healing (recovers wrongly root-owned secrets with no privilege). Replaces per-app Rust (deletes ensure_fmcd_password). fedimint-clientd/gateway manifests now declare fmcd-password / fedimint-gateway-hash. companion.rs: rebuild the auto-built :latest image when its build context changes (staleness check) so baked-in fixes (e.g. guardian-UI CSS) actually reach nodes. quadlet.rs: skip PublishPort under Network=host (podman rejects the combo, exit 125) + regression tests. UI: "Fedimint Guardian" rename, fedimint-clientd/nostr-rs-relay/meshtastic tagged as Services (headless backends), gateway icon fallback. Deployed + verified on .228 (generated-secrets fixed fedimint-gateway start; grafana/strfry orphan crash-loop units removed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
db7d424bff
commit
03a4ee1b30
@@ -12,6 +12,102 @@ This document is the live tracker for whether we're meeting that bar.
|
||||
Every PR that touches the container subsystem updates the scoreboard
|
||||
below. **If you can't honestly tick the box, the change isn't ready.**
|
||||
|
||||
---
|
||||
|
||||
## Production-quality pass — 2026-06-21 (current, v1.7.99-alpha)
|
||||
|
||||
The migration's aim, restated as **five pillars** (every app must satisfy all five):
|
||||
|
||||
1. **Quadlet-everywhere** — every container is a declarative systemd Quadlet
|
||||
unit under `user.slice`, never inside `archipelago.service`'s cgroup. Kills
|
||||
FM3 (restarting/updating archipelago SIGKILLs every container in its cgroup);
|
||||
systemd becomes the per-app supervisor.
|
||||
2. **Level-triggered reconciler** — a 30s idempotent reconcile loop drives
|
||||
desired→current from manifests + secrets. Self-healing, not edge-triggered.
|
||||
3. **Lifecycle bulletproof** — every app passes the full matrix
|
||||
(install / UI reachable / stop / start / restart / reinstall / reboot-survive
|
||||
/ archipelago-restart-survive / uninstall) **20× green on .228 AND .198**
|
||||
before any release.
|
||||
4. **Data-driven apps** — install/uninstall needs only the app's manifest +
|
||||
catalog entry. **No host OS changes** (no apt, no /etc, no host units) and
|
||||
**no archipelago binary code per app**. Only *core* apps (bitcoin, lnd,
|
||||
electrumx, fedimint + gateway/clientd) may carry bespoke handling if truly
|
||||
unavoidable.
|
||||
5. **Rootless + security-first (non-negotiable)** — containers run in the
|
||||
unprivileged `archipelago` user namespace; never root, no `--privileged`,
|
||||
drop-all-caps + add-back only what a manifest declares. Secrets are `0600`,
|
||||
owned by the service user. Security is king.
|
||||
|
||||
**Per-app definition of done:** all five pillars hold → lifecycle matrix 20×
|
||||
green on .228 then .198 → catalog/registry updated (`app-catalog/catalog.json`
|
||||
+ `releases/app-catalog.json`, rebuilt image pushed to the mirror) → tracker
|
||||
cell ticked. Only then move to the next app.
|
||||
|
||||
**.228 testing constraint:** do NOT touch `bitcoin-knots`, `electrumx`, or
|
||||
`lnd` on .228 — they are synced and healthy; destructive cycles there would
|
||||
cost hours of resync.
|
||||
|
||||
### Session work log
|
||||
|
||||
| Date | App | Change | State |
|
||||
|---|---|---|---|
|
||||
| 2026-06-21 | fedimint-gateway / -clientd | **Generated-secrets system** (Pillar 4+5). New `generated_secrets:` manifest field (`hex16`/`hex32`/`bcrypt`); materialised generically at the `resolve_dynamic_env` chokepoint — atomic `0600`, rootless-owned, idempotent, and **self-healing** (recreates a wrongly `root:root`-owned secret via the service-owned dir, no chown/privilege). Removed per-app `ensure_fmcd_password` (−30 LoC). Fixes gateway never starting (`resolving secret_env` → missing/unreadable `fedimint-gateway-hash`). | ◐ code complete, `cargo check` + 3 unit tests green; **not yet deployed/validated on .228** |
|
||||
| 2026-06-21 | fedimint-gateway | Icon placeholder | ○ investigating: marketplace catalog has title+icon (fedimint.png, shared); `BUNDLED_APPS` frontend list omits fedimint → installed view falls back to 📦 |
|
||||
|
||||
### ⏯ RESUME POINT (2026-06-21, mid-session)
|
||||
|
||||
**Done (working tree, NOT git-committed):**
|
||||
- Generated-secrets system — all files below written, `cargo check` clean, 3 unit tests green.
|
||||
- Manifests declare `generated_secrets` (fmcd-password hex16; fedimint-gateway-hash bcrypt).
|
||||
- Tracker refreshed with 5 pillars + this log.
|
||||
|
||||
**In flight:**
|
||||
- Local release build RUNNING (`cd core && cargo build --release -p archipelago`,
|
||||
log `/tmp/archy-local-build.log`, output `core/target/release/archipelago`).
|
||||
⚠️ **.228 has NO cargo and NO rsync** — build LOCALLY on .116, ship binary + files
|
||||
via **tar-over-ssh** (`tar -cf - … | ssh … 'tar -xf -'`).
|
||||
|
||||
**Next steps (in order):**
|
||||
1. Wait for local build → `Finished`. scp/tar `core/target/release/archipelago` → .228.
|
||||
2. Ship updated manifests to **`/opt/archipelago/apps/fedimint-{gateway,clientd}/`** (canonical runtime dir; cwd-relative `apps` doesn't resolve — WorkingDirectory is empty).
|
||||
3. **Binary swap is SAFE for protected backends:** `archipelago.service` is
|
||||
`KillMode=control-group` BUT bitcoin-knots/electrumx/lnd conmons live under
|
||||
`user.slice/.../libpod-*.scope`, NOT the service cgroup. Only fedimint-clientd +
|
||||
immich conmons are in-cgroup (non-protected, reconciled back). `systemctl stop
|
||||
archipelago` → `cp` binary → `start`.
|
||||
4. Validate: install fedimint-gateway → assert `fedimint-gateway-hash` (0600,
|
||||
archipelago-owned) + `.pw` generated → container starts healthy.
|
||||
5. Run `tests/lifecycle/run-20x.sh` for the gateway (do NOT touch knots/electrumx/lnd).
|
||||
6. Frontend fixes (separate from binary): see icon/rename below; rebuild neode-ui,
|
||||
ship `dist + catalog.json + assets` to `/opt/archipelago/web-ui` (chown 1000:1000).
|
||||
|
||||
**Icon / naming (frontend, user-confirmed):**
|
||||
- Gateway icon = **reuse fedimint.png** (user choice). Static catalogs already map all 3
|
||||
→ fedimint.png; deployed `/catalog.json` on .228 also correct; `/api/app-catalog`
|
||||
(decoupled, dict form) returns no fedimint → frontend falls through to `/catalog.json`.
|
||||
Placeholder is therefore a **stale deployed bundle** and/or the **hardcoded fallback gap**:
|
||||
`getCuratedAppList()` in `neode-ui/src/views/discover/curatedApps.ts` omits
|
||||
fedimint-gateway + fedimint-clientd entirely — add both (icon fedimint.png).
|
||||
- Base **`fedimint` → display "Fedimint Guardian"** (user ask). Edit name/title in:
|
||||
`apps/fedimint/manifest.yml`, `app-catalog/catalog.json`,
|
||||
`neode-ui/public/catalog.json`, `web/dist/neode-ui/catalog.json`,
|
||||
`curatedApps.ts:101`. (`INSTALLED_ALIASES.fedimint = ['fedimint-gateway']` in curatedApps.ts.)
|
||||
|
||||
**.228 access:** `sshpass -p archipelago ssh archipelago@192.168.1.228`; UI/RPC pw
|
||||
`password123` (https). Binary `/usr/local/bin/archipelago` (v1.7.99-alpha).
|
||||
|
||||
### Generated-secrets — files touched
|
||||
|
||||
- `core/container/src/manifest.rs` — `GeneratedSecret` + `SecretGenKind` types, `ContainerConfig.generated_secrets`, validation (bare-filename, unique target files).
|
||||
- `core/container/src/lib.rs` — re-export the new types.
|
||||
- `core/archipelago/src/container/secrets.rs` — **new** generator module (atomic write, idempotent, self-heal) + 3 unit tests.
|
||||
- `core/archipelago/src/container/mod.rs` — register module.
|
||||
- `core/archipelago/src/container/prod_orchestrator.rs` — call `ensure_generated_secrets` in `resolve_dynamic_env`; drop fmcd special-case.
|
||||
- `core/archipelago/src/wallet/fedimint_client.rs` — delete orphaned `ensure_fmcd_password` (reader keeps `FMCD_PASSWORD_SECRET`).
|
||||
- `apps/fedimint-clientd/manifest.yml`, `apps/fedimint-gateway/manifest.yml` — declare `generated_secrets`.
|
||||
|
||||
---
|
||||
|
||||
## Test layers
|
||||
|
||||
| Layer | What it asserts | Toolchain | Latency / iteration |
|
||||
|
||||
Reference in New Issue
Block a user