The F-10a scope correction committed hours earlier asserted semantics its evidence did not support. The KEY-05 planner caught it against the code: - mesh/x3dh.rs:100/:114 are u32 prekey IDENTIFIERS (spk_id, otk_id), not key agreement material. The X25519 secrets come from crypto::generate_x25519_ephemeral() at :99/:113 and were never in scope. - session.rs's 16 raw matches read as 16 production token sites; #[cfg(test)] begins at :470, so it is 4 production + 12 test. - wallet/bdhke.rs is 2 production of 4 (#[cfg(test)] at :143) — and those two ARE genuine key material: generate_secret() :133 and random_blinding_factor() :139. The Medium rating still holds, on narrower grounds: bdhke's two production sites plus storage_crypto.rs:39's AEAD nonce. It no longer rests on x3dh. Struck rather than silently rewritten. F-10 was corrected on the grounds that understatement misleads the next reader; overstatement does the same, and this table managed both within a day. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
310 lines
29 KiB
Markdown
310 lines
29 KiB
Markdown
# Roadmap: Archipelago — v1.8.0 Developer-Ready App Platform
|
||
|
||
## Overview
|
||
|
||
Brownfield milestone starting from a green single-node production gate (5/5 on .228,
|
||
2026-06-23). The journey: make federation and mesh rock-solid (node removal, sync,
|
||
messaging parity), fix the UI slowness users feel on every tab switch, prove the platform
|
||
across the fleet (multinode pass), make the container lifecycle bulletproof (Quadlet
|
||
default, self-healing, honest progress, no ghosts), flip manifest distribution from OTA
|
||
disk files to the signed registry, harden manifest security enforcement to the full
|
||
ADR-009 bar, ship the `archy app` developer CLI, and land the decentralized Nostr
|
||
marketplace — ending at the north star: a third-party developer publishes an app via the
|
||
signed/decentralized registry and a user installs it on their node.
|
||
|
||
## Phases
|
||
|
||
**Phase Numbering:**
|
||
|
||
- Integer phases (1, 2, 3): Planned milestone work
|
||
- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)
|
||
|
||
- [ ] **Phase 1: Federation & Mesh Hardening** - Deep review of federation/fleet + mesh code; node removal sticks, sync converges, mesh messaging behaves identically on demo and real nodes
|
||
- [x] **Phase 2: UI Performance** - Tab switches and secondary screens render fast; worst transitions measured and fixed (completed 2026-07-31)
|
||
- [ ] **Phase 3: Multinode Verification Pass** - Lifecycle gate green on a second node; cross-node federation/mesh/transport suites pass; federation removal sticks
|
||
- [ ] **Phase 4: Lifecycle Perfection & Quadlet Default** - Quadlet backends default, failed-unit self-healing, flap observability, cascade gate, truthful progress
|
||
- [ ] **Phase 5: Registry-Distributed Manifests** - Signed catalog carries full manifests; fleet flipped off OTA disk-file distribution
|
||
- [ ] **Phase 6: Manifest Security Enforcement** - Validation matches ADR-009 mandates; generated security profiles actually applied
|
||
- [ ] **Phase 7: Developer Tooling CLI** - `archy app validate/render/local-install/lifecycle-test` + developer guide
|
||
- [ ] **Phase 8: Decentralized Marketplace** - DID-signed publish to Nostr relays, trust-tier discovery, verified third-party install end-to-end
|
||
- [ ] **Phase 9: BotFights Platform Upgrade** - Native nostr signer login, one self-contained AI bot-setup prompt, shared public VPS2 match endpoint so all nodes see all fighters, registry updated
|
||
|
||
## Phase Details
|
||
|
||
### Phase 1: Federation & Mesh Hardening
|
||
|
||
**Goal**: Federation and mesh are tight — a structured review of the fleet/federation and mesh code feeds fixes so node removal sticks, sync converges, and mesh messaging (including attachments) behaves identically everywhere it runs
|
||
**Depends on**: Nothing (first phase)
|
||
**Requirements**: FED-01, FED-02, FED-03, FED-04, FED-05, FED-06, FED-07, UIFIX-01, UIFIX-02, UIFIX-03, UIFIX-04, UIFIX-05, UIFIX-06, FED-08, FED-09
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A structured code review of the federation/fleet area (`core/archipelago/src/federation`, node sync, FIPS/transport dial layer) and the mesh area (`core/archipelago/src/mesh`, mesh RPC surface) produces a findings list, and every finding is fixed or explicitly deferred with a reason
|
||
2. Removing a federation node removes it everywhere — it disappears from all UI surfaces, tombstones propagate, and it never reappears after later sync cycles; a failed removal surfaces an error instead of silently no-opping
|
||
3. Federation sync converges: after sync settles, fleet nodes agree on the node list and node status is fresh — stale entries, duplicates, and silent sync failures are gone, and sync errors are visible to the operator
|
||
4. Mesh attachment send works identically on the demo and on real nodes — same modals, same transport decisions, same success — with the demo backend implementing the same RPC surface the UI calls (no "Method not found", no demo-only chooser modal)
|
||
5. Channel-opening between nodes is first-class UI: a user can copy/share their node's Lightning URI; sees a list of trusted (federated) nodes by hostname to open a channel with in one flow; and can browse/request channels with public nodes — built with the existing design system (Teleport-to-body modals, house style), tested live on the :8100 dev preview against archi-dev, and fixed there before any deploy
|
||
6. The invoice/payment "paid" success animation is on-brand: the tick's circle is the screensaver-style ring with the outer EQ-segment lines (reuse `neode-ui/src/components/ScreensaverRing.vue`, which already ships a `compact` overlay size), replacing the current burst in the payment success pane (`neode-ui/src/components/SendBitcoinModal.vue`) and matching wherever else the paid tick appears
|
||
7. Fedimint gateway installs have no pre-set password (BLOCKER, added 2026-07-30): a fresh install generates its gateway credentials per-install via manifest-declared `generated_secrets` (per the repo secrets invariant) or requires the user to set one — never a baked-in default; existing installs carrying the default password are migrated or flagged. NOTE: phase 1's 10 plans predate this criterion — an additional gap plan is required before phase 1 execution completes
|
||
8. The FIPS/Tor pills on cloud files are kept and visible at mobile widths (UIFIX-01, BLOCKER, added 2026-07-30 — see `.planning/todos/pending/2026-07-30-keep-fips-tor-pills-on-cloud-files-and-show-them-on-mobile.md`)
|
||
9. The connected-nodes list scrolls at row-matched height instead of growing to fit (UIFIX-02, BLOCKER, added 2026-07-30 — see `.planning/todos/pending/2026-07-30-connected-nodes-list-must-scroll-at-row-matched-height.md`)
|
||
10. The onboarding tickbox is discoverably visible on short viewports via an on-brand affordance (UIFIX-03, BLOCKER, added 2026-07-30 — see `.planning/todos/pending/2026-07-30-onboarding-tickbox-hidden-below-fold-on-short-screens.md`)
|
||
11. Paid Files pictures open in the app lightbox, not a browser tab (UIFIX-04, added 2026-07-30 — see `.planning/todos/pending/2026-07-30-peer-files-pictures-open-in-tab-not-lightbox.md`)
|
||
12. Picture-in-picture closes the lightbox with a fluid on-brand animation (UIFIX-05, added 2026-07-30 — see `.planning/todos/pending/2026-07-30-pip-should-close-lightbox-with-fluid-animation.md`)
|
||
13. Genuinely slow opens show loader states (UIFIX-06, added 2026-07-30 — see `.planning/todos/pending/2026-07-30-missing-loader-states-on-slow-opens.md`; 02-08's flagged timing regressions are the starting inventory)
|
||
NOTE for criteria 7–13: all were added after phase 1's 10 plans were written — before phase 1 execution completes, create gap plan(s) covering FED-07 + UIFIX-01..06 (existing desktop visuals must remain untouched per the standing visual-invisibility rule; UIFIX items themselves are user-approved visual changes)
|
||
|
||
**Plans**: 11/20 plans executed
|
||
|
||
Plans:
|
||
|
||
- [x] 01-20-PLAN.md — URGENT wave 1: doctor stops restarting Tor every 5min (mesh Tor fallback) (FED-09)
|
||
- [x] 01-19-PLAN.md — URGENT wave 1: wallet invoices embed route hints so private-channel nodes can receive (FED-08)
|
||
- [x] 01-01-PLAN.md — Serialize the federation node store and make removal stick (FED-01)
|
||
- [x] 01-02-PLAN.md — Demo mesh/federation RPC parity + automated parity harness (FED-04)
|
||
- [x] 01-03-PLAN.md — On-brand paid tick: ScreensaverRing badge variant on both success surfaces (FED-06)
|
||
- [ ] 01-04-PLAN.md — Lightning identity: own-node URI + meshed Lightning peer discovery (FED-05)
|
||
- [ ] 01-05-PLAN.md — Federation sync convergence and operator-visible sync errors (FED-02)
|
||
- [ ] 01-06-PLAN.md — Lightning URI on the federation sync payload, sharing default decided (FED-05)
|
||
- [ ] 01-07-PLAN.md — Channel-open request messaging over the mesh (FED-05)
|
||
- [ ] 01-08-PLAN.md — Channel-open UX: own URI, trusted-node picker, meshed-peer requests (FED-05)
|
||
- [ ] 01-09-PLAN.md — Structured federation/mesh review + dev-pair deploy (FED-03)
|
||
- [ ] 01-10-PLAN.md — Consolidated phase verification on the dev pair (FED-01/02/05/06)
|
||
|
||
**Wave 7** *(gap closure — criteria 7–13, added 2026-07-30 after the original 10 plans were written)*
|
||
|
||
- [x] 01-11-PLAN.md — No baked-in Fedimint gateway credential: per-install secret on every path (FED-07)
|
||
- [x] 01-12-PLAN.md — Connected-nodes list scrolls at row-matched height instead of growing (UIFIX-02)
|
||
- [x] 01-13-PLAN.md — On-brand scroll cue makes the onboarding tickbox findable on short screens (UIFIX-03)
|
||
- [x] 01-14-PLAN.md — Paid Files open in the app lightbox, with a visible wait and a real error path (UIFIX-04/06)
|
||
- [x] 01-15-PLAN.md — PiP hands off from the lightbox and survives tab changes and buffering (UIFIX-05)
|
||
|
||
**Wave 8** *(blocked on Wave 7 completion)*
|
||
|
||
- [x] 01-16-PLAN.md — Migrate existing installs off the default gateway credential, data preserved (FED-07)
|
||
- [ ] 01-17-PLAN.md — FIPS/Tor pills pinned against removal and readable at phone widths (UIFIX-01)
|
||
|
||
**Wave 9** *(blocked on Wave 8 completion)*
|
||
|
||
- [ ] 01-18-PLAN.md — Six-fix sign-off on archi-dev-box (UIFIX-01/02/03/04/05/06)
|
||
|
||
**UI hint**: yes
|
||
|
||
### Phase 2: UI Performance
|
||
|
||
**Goal**: The UI feels fast — switching tabs and opening secondary screens (screens reached from a tab's main page) renders promptly instead of stalling on refetches and remounts
|
||
**Depends on**: Nothing (frontend-focused; parallelizable with Phase 1)
|
||
**Requirements**: PERF-01, PERF-02, PERF-03
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. The slowest tab switches and secondary-screen opens are profiled and the causes named (remount storms, serial RPC waterfalls, uncached fetches) before fixes land
|
||
2. Switching between main tabs renders the target view immediately from cached state, refreshing data in the background — no blank screens or long spinners on tabs already visited this session
|
||
3. Secondary screens open without a blocking full reload; repeat visits are instant
|
||
4. The fixes are verified on real node hardware (not just the dev box) — the sluggishness the user reported is gone on-device
|
||
|
||
**Plans**: 11/11 plans executed
|
||
|
||
Plans:
|
||
**Wave 1**
|
||
|
||
- [x] 02-01-PLAN.md — Profile every D-09 surface on archi-dev-box and commit the findings doc (PERF-01)
|
||
|
||
**Wave 2** *(blocked on Wave 1 completion)*
|
||
|
||
- [x] 02-02-PLAN.md — TRACER: KeepAlive host, hook reactivation, app-store tab, refresh indicator (PERF-02)
|
||
- [x] 02-03-PLAN.md — Secondary screens: per-item cache, parallel loads, purge on logout (PERF-03)
|
||
|
||
**Wave 3** *(blocked on Wave 2 completion)*
|
||
|
||
- [x] 02-04-PLAN.md — Keep every main tab alive safely: lifecycle audit + full registration (PERF-02)
|
||
|
||
**Wave 4** *(blocked on Wave 3 completion)*
|
||
|
||
- [x] 02-05-PLAN.md — Mesh: cache the six fetch groups, bound the D3 graph and Leaflet map (PERF-02)
|
||
- [x] 02-06-PLAN.md — Server and Home: cache the uncached fan-out, guarantee wallet freshness (PERF-02)
|
||
- [x] 02-07-PLAN.md — Chat/AIUI: stable embed URL + the two D-14 UX defaults (PERF-02)
|
||
|
||
**Wave 5** *(blocked on Wave 4 completion)*
|
||
|
||
- [x] 02-08-PLAN.md — Dev-pair deploy, on-device re-measure, D-11 pass bar (PERF-01/02/03)
|
||
|
||
**Wave 6** *(gap closure — blocked on Wave 5 completion)*
|
||
|
||
- [x] 02-09-PLAN.md — Server.vue KeepAlive remount: name the cause, fix it, pin it (PERF-02)
|
||
|
||
**Wave 7** *(gap closure — blocked on Wave 6 completion)*
|
||
|
||
- [x] 02-10-PLAN.md — Timing-regression verdict: three-way re-measure, clear or name each surface (PERF-02/03)
|
||
|
||
**Wave 8** *(gap closure — blocked on Wave 7 completion)*
|
||
|
||
- [x] 02-11-PLAN.md — Profile the real cause of the six confirmed regressions, fix what's fixable, re-measure (PERF-02/03)
|
||
|
||
**UI hint**: yes
|
||
|
||
### Phase 3: Multinode Verification Pass
|
||
|
||
**Goal**: The platform's lifecycle and federation guarantees are proven across the fleet, not just on .228 — the declared next exit criterion
|
||
**Depends on**: Phase 1 (proves the federation/mesh fixes hold fleet-wide)
|
||
**Requirements**: MNODE-01, MNODE-02, MNODE-03
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. The 5× destructive lifecycle gate reports 0 failures on a second fleet node (archy-x250-beta), run on-node
|
||
2. The cross-node smoke suite (federation pairing both directions, FIPS anchors, peer content browse) passes between two fleet nodes with every harness RPC time-bounded — a slow node produces a test failure, never an indefinite hang
|
||
3. An operator who removes a federation peer never sees it reappear in the peer list after later sync cycles; a tombstone-write failure is surfaced as an error instead of silently swallowed
|
||
4. The on-air mesh suite passes between two radio-equipped nodes over real RF
|
||
|
||
**Plans**: TBD
|
||
|
||
### Phase 4: Lifecycle Perfection & Quadlet Default
|
||
|
||
**Goal**: An insanely-reliable container environment — every app installs, runs, restarts, uninstalls, and reinstalls cleanly with honest progress, no ghosts, and automatic recovery
|
||
**Depends on**: Phase 3 (Quadlet default-flip is gated on the second-node gate reporting clean)
|
||
**Requirements**: LIFE-01, LIFE-02, LIFE-03, LIFE-04, LIFE-05
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. Restarting `archipelago.service` on a fleet node leaves every app container running — no SIGKILL-the-world, no multi-minute reconciler rebuild
|
||
2. An app whose Quadlet unit enters `failed` state (and was not user-stopped) comes back automatically within a bounded window, with backoff on persistent failure — no operator intervention
|
||
3. An operator can see per-app restart counts in status output, and a flapping app (>N restarts in M minutes) is flagged in logs instead of being invisible
|
||
4. Uninstalling then reinstalling any gated app — including multi-container stacks like immich/btcpay — leaves no ghost My-Apps entries or orphan containers, preserves data per policy, and returns the app healthy, verified by the cascade gate tier
|
||
5. Install and uninstall progress bars move monotonically from real backend progress events and always land on a terminal success/failure state — asserted in the gate, and the single-node gate stays green after all orchestrator changes
|
||
|
||
**Plans**: TBD
|
||
**UI hint**: yes
|
||
|
||
### Phase 5: Registry-Distributed Manifests
|
||
|
||
**Goal**: Manifests ship via the signed registry, not OTA disk files — bumping or adding an app becomes a signed catalog change
|
||
**Depends on**: Phase 4 (fleet lifecycle stable under Quadlet default before changing the distribution channel)
|
||
**Requirements**: REG-01, REG-02
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A fleet node installs and updates an image-only app from the full manifest embedded in the signed catalog, verified against the pinned release-root key, with no corresponding OTA disk file present (disk remains the fallback for build-source apps)
|
||
2. A tampered or unsigned catalog manifest is rejected and the node falls back safely — it never installs from an unverified manifest
|
||
3. Bumping an app version fleet-wide requires only regenerating, re-signing, and publishing the catalog — no binary OTA, no disk rsync — proven live on the fleet
|
||
|
||
**Plans**: TBD
|
||
|
||
### Phase 6: Manifest Security Enforcement
|
||
|
||
**Goal**: A third-party manifest cannot weaken node security — declared security policy is fully validated and actually enforced at runtime
|
||
**Depends on**: Phase 5 (enforcement guards the registry channel third-party manifests will arrive through)
|
||
**Requirements**: SEC-01, SEC-02
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A manifest violating ADR-009 mandates (root user, unpinned `latest` tag, capability outside the allow-list, disabled seccomp) is rejected at validation with a clear error naming the violation
|
||
2. Security overrides (`readonly_root: false`, extra capabilities) work only when explicitly listed in the manifest and leave an audit trail
|
||
3. Generated AppArmor/seccomp profiles are applied to containers at creation and verifiably effective on a running app — not just generated and ignored
|
||
4. The single-node lifecycle gate stays green with enforcement on — existing catalog apps all pass the strengthened validation (or carry documented overrides)
|
||
|
||
**Plans**: TBD
|
||
|
||
### Phase 7: Developer Tooling CLI
|
||
|
||
**Goal**: A third-party developer can build, validate, and test an Archipelago app locally without reading platform internals
|
||
**Depends on**: Phase 6 (CLI validation must mirror the final enforced rule set)
|
||
**Requirements**: DEV-01, DEV-02, DEV-03, DEV-04
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A developer runs `archy app validate` on a manifest directory and gets the same pass/fail verdict — including security rules — that a node would enforce at install
|
||
2. A developer runs `archy app render` and sees the exact Quadlet/podman configuration their manifest produces before ever touching a node
|
||
3. A developer can install their app onto a dev node and run its lifecycle test (install/UI/stop/start/restart/uninstall) from the CLI
|
||
4. A new developer following only the developer guide goes from an empty directory to a running app on a node — no tribal knowledge required
|
||
|
||
**Plans**: TBD
|
||
|
||
### Phase 8: Decentralized Marketplace
|
||
|
||
**Goal**: The north star — third-party developers publish apps via the decentralized registry and users install them on their nodes
|
||
**Depends on**: Phase 7 (publish rides the CLI; installs ride registry distribution from Phase 5 and enforcement from Phase 6)
|
||
**Requirements**: MKT-01, MKT-02, MKT-03, MKT-04
|
||
**Success Criteria** (what must be TRUE):
|
||
|
||
1. A third-party developer publishes a DID-signed app manifest to public Nostr relays (NIP-78, kind 30078) using the tooling
|
||
2. A node discovers the published app from multiple relays and the app store UI shows its trust tier (Verified / Community / Unverified) per ADR-006 scoring
|
||
3. The node verifies the manifest signature before installation; a tampered or invalid marketplace manifest cannot be installed
|
||
4. A user installs the third-party marketplace-published app on their node and it runs healthy under the standard lifecycle guarantees — the user-chosen success metric, demonstrated end-to-end
|
||
|
||
**Plans**: TBD
|
||
**UI hint**: yes
|
||
|
||
## Progress
|
||
|
||
**Execution Order:**
|
||
Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8
|
||
(Phases 1 and 2 are independent and may be worked in parallel.)
|
||
|
||
| Phase | Plans Complete | Status | Completed |
|
||
|-------|----------------|--------|-----------|
|
||
| 1. Federation & Mesh Hardening | 11/20 | In Progress| |
|
||
| 2. UI Performance | 11/12 | Complete | 2026-07-31 |
|
||
| 3. Multinode Verification Pass | 0/TBD | Not started | - |
|
||
| 4. Lifecycle Perfection & Quadlet Default | 0/TBD | Not started | - |
|
||
| 5. Registry-Distributed Manifests | 0/TBD | Not started | - |
|
||
| 6. Manifest Security Enforcement | 0/TBD | Not started | - |
|
||
| 7. Developer Tooling CLI | 0/TBD | Not started | - |
|
||
| 8. Decentralized Marketplace | 0/TBD | Not started | - |
|
||
| 9. BotFights Platform Upgrade | 7/7 | Executed — awaiting human demo verification | 2026-07-31 |
|
||
| 10. Key-Material Hardening | 0/5 | Planned — **priority override, see phase note** | - |
|
||
| 11. Wallet Experience & LND UI Parity | 0/TBD | Not started — gated on 10-05's watch-only verdict | - |
|
||
|
||
### Phase 9: BotFights Platform Upgrade
|
||
|
||
**Goal:** BotFights (app + registry) works great on every node: users sign in with the native nostr signer, a single self-contained AI prompt sets up their bot (replacing the confusing docs page), and every node's instance talks to a shared public match endpoint on VPS2 so all fighters are visible and battle across all nodes.
|
||
**Requirements**: BOT-01 native nostr signer login; BOT-02 unified AI bot-setup prompt (one copy-paste prompt, no doc-hopping); BOT-03 public shared match/fighter endpoint hosted on VPS2, node instances federate to it by default; BOT-04 registry/manifest + signed catalog updated and republished for the new version
|
||
**Depends on:** Nothing (independent app work; parallelizable with Phases 1–8)
|
||
**Plans:** 7 plans
|
||
|
||
Plans:
|
||
|
||
- [x] 09-01-PLAN.md — Arena reverse-proxy tracer: node instances become thin clients of one shared arena (BOT-03)
|
||
- [x] 09-02-PLAN.md — Finish native nostr signer login: JWT-only GET /api/auth/me, bare-pubkey path retired (BOT-01)
|
||
- [x] 09-03-PLAN.md — One self-contained AI bot-setup prompt served at /api/docs/prompt (BOT-02)
|
||
- [x] 09-04-PLAN.md — Canonical public arena on VPS2 + DNS/TLS via nginx-proxy-manager (BOT-03)
|
||
- [x] 09-05-PLAN.md — Build+push botfights:1.2.0, roll the arena, prove cross-instance visibility (BOT-03/BOT-04)
|
||
- [x] 09-06-PLAN.md — Manifest 1.2.0 with generated JWT secret + signed catalog republished (BOT-04)
|
||
- [x] 09-07-PLAN.md — archi-dev-box deploy + demo rehearsal: real signer login, cloud bot from the prompt (BOT-01/02/03/04)
|
||
|
||
### Phase 10: Key-Material Hardening
|
||
|
||
**Goal:** Every path that creates, restores, or persists node key material proves the caller is authorized and the material is per-node — closing the three exploitable findings from `docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md`. A node that is already onboarded must refuse to have its identity replaced; a node flashed from the shared rootfs must never share another node's host keys; and the wallet spending key must not exist in cleartext outside the encrypted envelope.
|
||
**Requirements**: KEY-01 (F-01, **Critical**) `seed.generate`/`seed.restore` are unauthenticated (`api/rpc/middleware.rs:25`) and `NodeIdentity::from_seed` (`identity.rs:79`) overwrites `node_key`/`nostr_secret`/FIPS key unconditionally — one unauthenticated POST with an attacker-chosen mnemonic hijacks a live node; gate on onboarding-incomplete (the unused `identity.rs:117` `key_exists` guard) + rate-limit; KEY-02 (F-03, **High**) first-boot per-device secret regeneration is fail-open and its completion marker is set even on failure (`image-recipe/_archived/build-auto-installer-iso.sh:1647,:1659,:1663`), over a fleet-shared cached rootfs that bakes SSH host keys + the TLS key — make it fail-closed and retried; KEY-03 (F-13, **High**) the BIP-84 account **private** key is imported into Bitcoin Core's wallet (`api/rpc/bitcoin.rs:203,:229-231`), duplicating the spending key outside the encrypted envelope — move to watch-only descriptors per `docs/security/PSBT-SIGNING-ARCHITECTURE.md`; KEY-04 on-node verification of C-3/C-4/C-6 from the audit's UNVERIFIED checklist (host-key uniqueness across two real nodes, rootfs tar contents on the build host, unauthenticated LAN reachability of the RPC endpoint); KEY-05 (F-10a, **Medium**, added 2026-08-02) **a defaulted RNG cannot be inherited anywhere in the crate**. The audit's F-10 recorded this as 2 call sites; it is **41 raw matches across 15 files** (`session.rs` 16 → 4 prod + 12 test, `pine_ha.rs` 6, `wallet/bdhke.rs` 4 → 2 prod — *Cashu proof secret + blinding factor, genuine key material*, `storage_crypto.rs` 1 — *AEAD nonce*, `mesh/x3dh.rs` 2 — *prekey identifiers, **not** key material, corrected 2026-08-02*, +10 more; full table in the audit's §F-10a. Per-site prod/test classification is KEY-05's Task 1, not an assumption). Nothing is broken today — `rand::random()`/`thread_rng()` are ChaCha12 seeded from `getrandom(2)` — but this is the exact T1 structural shape that produced the 2026-07-30 COLDCARD defect, now with key material in its blast radius. Five layers, all required: (a) **sealed allowlist trait** at key-generation seams (private supertrait, so no other module *or crate* can implement it; exactly one production impl, `OsRng`) — this also retires the `impl rand::CryptoRng for CountingRng` false promise at `seed.rs:656`; (b) **`clippy.toml` `disallowed-methods`** banning `rand::thread_rng`/`rand::random` crate-wide, so enforcement is a compile failure in CI rather than a review convention (no `clippy.toml` exists today; CI already runs clippy); (c) **`cargo-deny`** failing on duplicate `rand` majors — two coexist today, which is the mechanism by which a bump could silently rebind (absorbs R-05); (d) **degenerate-entropy runtime check** before key generation (rejects all-zero / counter-like draws — the one layer that would catch the Coldcard failure *on the device* rather than in review); (e) **persist the CSPRNG-readiness verdict** that `seed.rs:59` already computes and discards, so a node can answer after the fact "was the pool seeded when this key was born?" (absorbs R-09). Supersedes R-13
|
||
**Depends on:** Nothing (independent security work; parallelizable with Phases 1–8). **Priority override: F-01 is Critical and live on every fleet node — this phase should be planned and executed ahead of its numeric position, which reflects append order in a shared roadmap, not sequencing.**
|
||
**Plans:** 6 plans
|
||
|
||
> **EXECUTION GATE (user instruction, 2026-08-02):** do **not** begin executing this phase until
|
||
> (a) the concurrent agent working Phase 1 has finished, and (b) their changes are synced and
|
||
> accounted for. Rationale: Phase 10 edits `middleware.rs`, `identity.rs`, `seed_rpc.rs`,
|
||
> `bitcoin.rs` and — under KEY-05 — ~15 further files across the same crate that agent is
|
||
> actively committing to. Verify a clean tree and a fetched `gitea-ai/main` before starting.
|
||
>
|
||
> **KEY-05 is planned** as `10-06` (added 2026-08-02). The other 5 plans predate KEY-05 and
|
||
> are unchanged by it. `10-06` is wave 2 because it shares `seed.rs` with `10-05` and
|
||
> `api/rpc/auth.rs` with `10-01`; see its `<file_collision_analysis>`.
|
||
|
||
Plans:
|
||
|
||
**Wave 1** *(parallel — no shared files)*
|
||
|
||
- [ ] 10-01-PLAN.md — Identity-mutating unauthenticated RPCs hard-refuse on a provisioned node, with the byte-identity regression suite (KEY-01)
|
||
- [ ] 10-03-PLAN.md — First-boot secret regeneration retries then fails closed, and the rootfs tar ships identity-free (KEY-02/KEY-04 C-4)
|
||
- [ ] 10-05-PLAN.md — Delete the Bitcoin Core xprv-import path; make LND's PSBT round trip first-class, tested and honestly documented (KEY-03)
|
||
|
||
**Wave 2** *(each blocked on its wave-1 sibling)*
|
||
|
||
- [ ] 10-02-PLAN.md — On-node C-6 exposure measurement, live refusal proof, and the fresh-node onboarding non-regression (KEY-01/KEY-04) — depends on 10-01
|
||
- [ ] 10-04-PLAN.md — Fleet detection of image-baked host secrets, guarded one-time rotation, and C-3 two-node verification (KEY-02/KEY-04) — depends on 10-03
|
||
- [ ] 10-06-PLAN.md — A defaulted RNG cannot be inherited anywhere in the crate: sealed allowlist, clippy ban, cargo-deny, degenerate-entropy check, persisted CSPRNG verdict (KEY-05) — depends on 10-01 and 10-05
|
||
|
||
### Phase 11: Wallet Experience & LND UI Parity
|
||
|
||
**Goal:** The wallet is something a user chooses and understands, not something that just appears. A first-run wallet screen lets them pick a wallet type and route accordingly; seed handling reuses the SeedQR + seed-words patterns already shipped; and the day-to-day Lightning interface offers what umbrelOS's LND UI offers, so nothing is missing for someone arriving from Umbrel.
|
||
**Requirements**: WALLET-01 first-run wallet-type chooser (an intro/initial screen presenting the available wallet types with plain-language trade-offs, routing into the matching setup flow) — the available types depend on Phase 10's `10-05` watch-only verdict, so this requirement is **gated on that evidence**, not on assumption; WALLET-02 seed handling in the wallet flow reuses the existing SeedQR + seed-words components rather than reimplementing them (`neode-ui/src/utils/seedqr.ts`, `OnboardingSeedGenerate.vue`, `SeedRevealPanel.vue`, `WalletScanModal.vue`) — including the standing constraint that the LND aezeed is text-only by design and has no SeedQR; WALLET-03 evidence-based umbrelOS LND UI parity — produce a feature-by-feature comparison matrix from the actual Umbrel interface (researched, not assumed), classify each row as already-shipped / gap / deliberately-not-wanted, and close the gaps worth closing; WALLET-04 the resulting interface is house-style (Teleport-to-body modals, existing design system) and verified on the :8100 dev preview against archi-dev before any deploy; WALLET-05 **the PSBT air-gap round trip is a real, usable flow** — the standard two-scan dance (node displays the unsigned PSBT as an animated QR → offline signer scans and signs → signer displays the signed PSBT → node scans it back with the camera → finalize + broadcast). Three sub-gaps, all verified 2026-08-01: (a) **no UI exists** — `lnd.create-psbt`/`lnd.finalize-psbt` and their `rpc-client.ts:417` wrappers are called by nothing but unit tests; (b) **no animated-QR encoder** — `qrcode`/`qrloop` are dependencies and `useAnimatedQRDecoder.ts` + `WalletScanModal.vue` already handle the *inbound* scan, but nothing encodes a PSBT for display; (c) **format interop is wrong for real signers** — the animated format in use is `qrloop` (Ledger's), while Passport/SeedSigner speak **BC-UR** (`ur:crypto-psbt`) and Coldcard Q speaks **BBQr**; BC-UR is the priority given the existing Passport-Prime-compatible SeedQR work. **WALLET-05 is meaningless until 10-05's watch-only verdict lands** — `lnd.create-psbt` funds from LND's own wallet whose keys LND holds, so until LND is watch-only against the external signer the offline device would produce a signature the node does not need
|
||
**Depends on:** **Phase 10** — specifically `10-05`, which produces the evidence-backed verdict on whether LND can be provisioned watch-only against an external signer. WALLET-01's list of offerable wallet types is a direct consequence of that verdict; building the chooser first would mean guessing at what it can offer. `10-05` also deletes the dead Core wallet path, so this phase never has to represent it in the UI.
|
||
**Plans:** 0 plans
|
||
|
||
**Already shipped — do not rebuild (verified 2026-08-01):** `LightningChannelsPanel.vue`, `SendBitcoinModal.vue`, `ReceiveBitcoinModal.vue`, `WalletScanModal.vue`, `WalletSettingsModal.vue`, `SeedRevealPanel.vue`, `LndSeedBackupPrompt.vue`, `utils/seedqr.ts`, and the channels All/Active/Pending/Closed tabs. The parity matrix (WALLET-03) must start from this inventory so the phase closes real gaps instead of re-implementing existing surfaces.
|
||
|
||
Plans:
|
||
|
||
- [ ] TBD (run /gsd-plan-phase 11 to break down)
|