The checkpoint on archi-dev-box proved rotation alone doesn't close FED-07: the credential file went unique while the RUNNING container kept serving the compromised one, because the Quadlet path rewrites a unit without restarting it and fedimint-gateway is classified restart-sensitive, so drift was detected and deliberately ignored on every tick. Rotation now records the app id, and the drift check consumes that flag to recreate even a restart-sensitive app, with a WARN naming the reason. This mirrors the published-port carve-out a few lines above, which already makes the same trade for the same reason: a container that is already broken (there) or already compromised (here) is not protected by leaving it running. Restart-sensitivity protects working services. A gateway answering to a credential published in this repository is not working, it is compromised, and gateway admin can drain Lightning liquidity — indefinite exposure loses to a few seconds of restart. Rotating-but-only-alerting was rejected: the monitoring system fires on metric thresholds only, so it would have needed new event-alert plumbing to deliver something strictly weaker. Re-verified on the same node, same scenario: rotation at 06:39:23, recreate at 06:39:27, PID 3923125 -> 148426, running credential now matches the file, container healthy with the same name and ports, gatewayd.db intact at 18 files with IDENTITY present, 32 containers untouched, no repeat rotation. 3 new tests. Also lands the missing 01-19 and 01-20 SUMMARYs: both had code committed 2026-07-31 but no summary and no roadmap tick, so they read as unstarted. Phase 1 is 11/20. FED-09 carries 15h of Tor uptime and 0 permission-fixes across 542 doctor runs on archi-dev-box. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
157 lines
14 KiB
Markdown
157 lines
14 KiB
Markdown
# Requirements: Archipelago (v1.8.0 — Developer-Ready App Platform)
|
||
|
||
**Defined:** 2026-07-29
|
||
**Core Value:** A third-party developer can publish an app via the signed/decentralized registry and a user can install it on their node — manifest-driven, rootless, secure, robust.
|
||
|
||
No PRDs existed in the ingest set; these requirements are derived from the master plan's
|
||
declared exit criteria (multinode pass + workstreams B/C/F), `.planning/codebase/CONCERNS.md`,
|
||
`docs/UNIFIED-TASK-TRACKER.md`, and the user-chosen success metric. Constraints from
|
||
`docs/app-manifest-spec.md` and the locked ADRs (see PROJECT.md) bound how each is built.
|
||
|
||
## v1 Requirements
|
||
|
||
### Federation & Mesh Hardening (FED)
|
||
|
||
- [ ] **FED-01**: Removing a federation node sticks — it disappears from every UI surface, tombstones propagate, it never reappears via later sync cycles, and a failed removal surfaces an error (never a silent no-op)
|
||
- [ ] **FED-02**: Federation sync converges and is observable — after sync settles, fleet nodes agree on the node list with fresh status; stale entries, duplicates, and silent sync failures are eliminated and sync errors are operator-visible
|
||
- [ ] **FED-03**: A structured code review of the federation/fleet area (`core/archipelago/src/federation`, node sync, FIPS/transport dial layer) and mesh area (`core/archipelago/src/mesh`, mesh RPC surface) is completed, with every finding fixed or explicitly deferred with a reason
|
||
- [x] **FED-04**: Mesh messaging parity — attachment send (and the rest of the mesh chat surface) behaves identically on the demo and on real nodes: the demo backend implements the same RPC surface the UI calls, transport decisions mirror the real size-based tier logic, and no demo-only modals exist
|
||
- [ ] **FED-05**: Inter-node Lightning channel opening UX — the UI shows the node's shareable Lightning URI; lists trusted (federated) nodes by hostname for one-click channel opening; and lets the user browse/request channels with public nodes — using the existing design system and components, verified on the :8100 dev preview against archi-dev before deploy
|
||
- [x] **FED-06**: On-brand payment success animation — the invoice "paid" tick's circle uses the screensaver-style ring with outer EQ-segment lines (reuse `ScreensaverRing.vue`'s compact size) in place of the current success burst, applied consistently everywhere the paid tick shows
|
||
- [x] **FED-08**: Lightning invoices created by the wallet embed route hints (LND `private` flag) so nodes whose channels are unannounced can actually receive payments — diagnosed on archy-x250-mad2 2026-07-31, where every wallet-UI invoice had `route_hints: []` and was unroutable; the bug is unconditional and affects any node without a public channel
|
||
- [x] **FED-09**: The container doctor does not restart Tor on every run — it recognises Tor's own setgid `2700` hidden-service directory mode as correct rather than "fixing" it to `700` and restarting, a loop that reset Tor every ~5 minutes, starved it of its consensus/HSDir cache (`No more HSDir available to query`), and broke the mesh's Tor fallback entirely; genuinely permissive modes are still corrected, and a restart backoff makes the failure class non-recurring
|
||
- [x] **FED-07**: Fedimint gateway never installs with a pre-set password — gateway credentials are generated per-install via manifest-declared `generated_secrets` (or explicitly set by the user), never baked into the image/manifest; existing installs with the default password get a migration path (BLOCKER — default credentials are a security hole)
|
||
|
||
### UI Fixes (UIFIX) — user-reported blockers, added 2026-07-30
|
||
|
||
- [ ] **UIFIX-01**: The FIPS/Tor pills on cloud files are kept (never removed by cleanups) and render at mobile widths — on mobile, users can see each file's security/transport state (BLOCKER)
|
||
- [x] **UIFIX-02**: The connected-nodes list scrolls at row-matched height — its height tracks the taller right-hand sibling in the row and the inner list scrolls within it, never growing to fit all rows scroll-free (BLOCKER)
|
||
- [x] **UIFIX-03**: On short viewports the onboarding confirmation tickbox is discoverably visible — an on-brand affordance (scroll cue, sticky footer, or equivalent) makes it obvious without altering tall-screen appearance (BLOCKER)
|
||
- [ ] **UIFIX-04**: Paid Files pictures open in the app's lightbox, not a browser tab — consistent with the rest of the app's media UX
|
||
- [x] **UIFIX-05**: Picture-in-picture is robust — entering PiP closes the lightbox with a fluid on-brand animation, and an active PiP session survives main-tab changes and video buffering pauses (only an explicit user stop ends it)
|
||
- [ ] **UIFIX-06**: Surfaces with genuinely slow opens show house-style loader states — no dead-feeling clicks (cached revisits stay spinner-free per PERF-02)
|
||
|
||
### UI Performance (PERF)
|
||
|
||
- [x] **PERF-01**: The slowest tab switches and secondary-screen opens are profiled with causes named (remount storms, serial RPC waterfalls, uncached fetches) — fixes are targeted, not guessed
|
||
- [x] **PERF-02**: Main-tab switches render immediately from cached state with background refresh — no blank screens or long spinners on tabs already visited this session
|
||
- [x] **PERF-03**: Secondary screens (screens reached from a tab's main page) open without a blocking full reload and are instant on repeat visits — verified on real node hardware, not just the dev box
|
||
|
||
### Multinode Verification (MNODE)
|
||
|
||
- [ ] **MNODE-01**: The 5× destructive lifecycle gate passes on a second fleet node (archy-x250-beta) with 0 failures, run on-node per gate policy
|
||
- [ ] **MNODE-02**: Cross-node federation/mesh/transport suites (`tests/multinode/smoke.sh`, `meshtastic.sh`) pass between fleet nodes, with all harness RPC calls time-bounded (no indefinite curl hangs)
|
||
- [ ] **MNODE-03**: Removing a federation peer sticks — tombstone-write failures are surfaced (not swallowed) and a removed peer never silently reappears after subsequent sync cycles
|
||
|
||
### Lifecycle Perfection (LIFE)
|
||
|
||
- [ ] **LIFE-01**: Quadlet backends are the default — restarting `archipelago.service` leaves every app container running (no SIGKILL-the-world, no multi-minute rebuild storm)
|
||
- [ ] **LIFE-02**: The reconciler self-heals failed Quadlet units — a `.service` in `failed` state (and not user-stopped) is reset-failed + started automatically, with backoff against busy-looping
|
||
- [ ] **LIFE-03**: Per-app restart/flap observability — restart counters, a threshold log line when an app restarts >N times in M minutes, and restart counts surfaced in health/status RPC output
|
||
- [ ] **LIFE-04**: Cascade uninstall→reinstall is gate-verified for multi-container stacks and installed apps — no ghost entries, no orphan containers, data preserved per policy, reinstall returns healthy
|
||
- [ ] **LIFE-05**: Install and uninstall report real, monotonic progress driven by backend progress events, always reaching a terminal success/failure state — asserted in the gate, never a fake or stuck bar
|
||
|
||
### Registry-Distributed Manifests (REG)
|
||
|
||
- [ ] **REG-01**: The published signed catalog embeds full app manifests; nodes install/update from signature-verified catalog manifests (disk manifests remain the fallback for build-source apps); tampered catalogs are rejected with safe fallback
|
||
- [ ] **REG-02**: The fleet is flipped to registry-distributed manifests — adding or bumping an image-only app requires only a re-signed catalog publish, no binary OTA or disk rsync
|
||
|
||
### Security Enforcement (SEC)
|
||
|
||
- [ ] **SEC-01**: `AppManifest::validate()` enforces the full ADR-009 mandate set — non-root UID, pinned image tags (no `latest`), capability allow-list, seccomp — with explicit, documented, auditable overrides
|
||
- [ ] **SEC-02**: Generated AppArmor/seccomp security profiles are actually applied at container creation (`--security-opt`) and verified effective on running apps
|
||
|
||
### Developer Tooling (DEV)
|
||
|
||
- [ ] **DEV-01**: `archy app validate` checks a manifest locally and returns the same pass/fail verdict the node enforces (schema + security rules)
|
||
- [ ] **DEV-02**: `archy app render` previews the exact Quadlet/podman configuration a manifest produces
|
||
- [ ] **DEV-03**: A developer can local-install and lifecycle-test an app against a dev node from the CLI (`archy app local-install` / `lifecycle-test`)
|
||
- [ ] **DEV-04**: The developer guide walks a new third-party developer from an empty directory to an installed, running app using only the CLI and docs
|
||
|
||
### Decentralized Marketplace (MKT)
|
||
|
||
- [ ] **MKT-01**: A third-party developer can publish a DID-signed app manifest to public Nostr relays (NIP-78, kind 30078) via the tooling
|
||
- [ ] **MKT-02**: A node discovers marketplace apps from multiple relays and displays each app's trust tier (Verified / Community / Unverified) per ADR-006 trust scoring
|
||
- [ ] **MKT-03**: Manifest signatures are verified before installation; tampered or invalid marketplace manifests cannot be installed
|
||
- [ ] **MKT-04**: End-to-end north star: a user installs a third-party marketplace-published app on their node and it runs healthy under the standard lifecycle guarantees
|
||
|
||
## v2 Requirements
|
||
|
||
Deferred to a future milestone. Tracked but not in the current roadmap.
|
||
|
||
### Distribution Backbone (DIST)
|
||
|
||
- **DIST-01**: BLAKE3 content-addressed catalog distribution via iroh swarm, origin-always-wins (workstream D — design-only today, tracker-marked backlog)
|
||
|
||
### Fleet & Hardening (FLEET)
|
||
|
||
- **FLEET-01**: Bitcoin multi-version fleet-wide OTA rollout (user-gated on timing per `docs/bitcoin-version-bulletproof-rollout.md`)
|
||
- **FLEET-02**: App-specific health assertions for the ~34 apps with only baseline lifecycle coverage
|
||
- **FLEET-03**: LUKS2 full-partition encryption for `/var/lib/archipelago/`
|
||
- **FLEET-04**: Dynamic per-app resource rebalancing (cgroup-stats feedback loop)
|
||
|
||
## Out of Scope
|
||
|
||
| Feature | Reason |
|
||
|---------|--------|
|
||
| Rootful/privileged containers, Docker | Invariant — ADR-001/ADR-009 |
|
||
| Per-app Rust installers / host provisioning | The anti-pattern workstream A deleted |
|
||
| Centralized gatekept app store | ADR-006 chose decentralized Nostr marketplace |
|
||
| Web5 DWN spec compliance | ADR-011 — deprioritized after TBD shutdown |
|
||
| Custom live voice-call protocol | Deprioritized 2026-07-01 per user; no scope decided |
|
||
|
||
## Traceability
|
||
|
||
Which phases cover which requirements. Updated during roadmap creation.
|
||
|
||
| Requirement | Phase | Status |
|
||
|-------------|-------|--------|
|
||
| FED-01 | Phase 1 | Pending |
|
||
| FED-02 | Phase 1 | Pending |
|
||
| FED-03 | Phase 1 | Pending |
|
||
| FED-04 | Phase 1 | Complete |
|
||
| FED-05 | Phase 1 | Pending |
|
||
| FED-06 | Phase 1 | Complete |
|
||
| FED-07 | Phase 1 | Complete — rotation + recreate verified on archi-dev-box 2026-08-02 |
|
||
| FED-08 | Phase 1 | Code complete + unit-pinned; post-OTA check on the user device pending |
|
||
| FED-09 | Phase 1 | Complete — 15h Tor uptime / 0 permission-fixes on archi-dev-box; onion-resolution check post-OTA |
|
||
| UIFIX-01 | Phase 1 | Pending |
|
||
| UIFIX-02 | Phase 1 | Complete |
|
||
| UIFIX-03 | Phase 1 | Complete |
|
||
| UIFIX-04 | Phase 1 | Pending |
|
||
| UIFIX-05 | Phase 1 | Complete |
|
||
| UIFIX-06 | Phase 1 | Pending |
|
||
| PERF-01 | Phase 2 | Complete |
|
||
| PERF-02 | Phase 2 | Complete. 02-11 (`02-FINDINGS.md` § Client-Side Render Cost Root Cause + § Task 3) named and fixed the real cause of Web5/Server's revisit-ms regressions — three leaked background pollers (`useFleetData.ts`, `FipsNetworkCard.vue`, `Web5Monitoring.vue`) armed in `onMounted` and never disarmed once their owning views joined `KEEP_ALIVE_PATHS`, gated to activate/deactivate. Web5 now fixed (275ms, below both its 566ms pre-phase-2 baseline and the 300ms pass bar); Server's regression is closed (574ms, below its 738ms baseline) though not yet under the 300ms stretch target — residual named as real, un-eliminated per-resource reactivation cost, not a new defect |
|
||
| PERF-03 | Phase 2 | Complete. 02-11 fixed Fleet's leaked `useFleetData.ts` poll (790ms, down from a 2631ms regression, substantially closing the gap to its 330ms baseline). AppDetails restored to at/near its own baseline (1231ms vs. 1204ms) — residual is the already-documented `useCachedResource` per-mount setup cost, not fixed further. Discover (1389ms) has a SECOND, distinct, evidenced cause found this session (CSS entrance-animation replay on KeepAlive reactivation, `card-stagger`/`showStagger` never removed from the DOM) — named with full profiling/diagnostic evidence but NOT fixed (blast radius spans 5+ files outside this plan's scope, needs its own real-device verification budget) — recommended as a dedicated follow-up. OpenWrtGateway: not measurable this pass (Chromium crash cascading from an unrelated surface); prior numbers stand, confirmed to reflect a real (not empty) disconnected-device UI render, not retracted |
|
||
| MNODE-01 | Phase 3 | Pending |
|
||
| MNODE-02 | Phase 3 | Pending |
|
||
| MNODE-03 | Phase 3 | Pending |
|
||
| LIFE-01 | Phase 4 | Pending |
|
||
| LIFE-02 | Phase 4 | Pending |
|
||
| LIFE-03 | Phase 4 | Pending |
|
||
| LIFE-04 | Phase 4 | Pending |
|
||
| LIFE-05 | Phase 4 | Pending |
|
||
| REG-01 | Phase 5 | Pending |
|
||
| REG-02 | Phase 5 | Pending |
|
||
| SEC-01 | Phase 6 | Pending |
|
||
| SEC-02 | Phase 6 | Pending |
|
||
| DEV-01 | Phase 7 | Pending |
|
||
| DEV-02 | Phase 7 | Pending |
|
||
| DEV-03 | Phase 7 | Pending |
|
||
| DEV-04 | Phase 7 | Pending |
|
||
| MKT-01 | Phase 8 | Pending |
|
||
| MKT-02 | Phase 8 | Pending |
|
||
| MKT-03 | Phase 8 | Pending |
|
||
| MKT-04 | Phase 8 | Pending |
|
||
|
||
**Coverage:**
|
||
|
||
- v1 requirements: 29 total
|
||
- Mapped to phases: 29
|
||
- Unmapped: 0
|
||
|
||
---
|
||
*Requirements defined: 2026-07-29*
|
||
*Last updated: 2026-07-29 — added FED (federation/mesh hardening) and PERF (UI performance) requirement groups; phases renumbered after inserting them as Phases 1–2*
|