diff --git a/.planning/quick/260731-upz-research-coinkite-conkite-low-entropy-ha/260731-upz-PLAN.md b/.planning/quick/260731-upz-research-coinkite-conkite-low-entropy-ha/260731-upz-PLAN.md
new file mode 100644
index 00000000..16ce4002
--- /dev/null
+++ b/.planning/quick/260731-upz-research-coinkite-conkite-low-entropy-ha/260731-upz-PLAN.md
@@ -0,0 +1,440 @@
+---
+phase: quick-260731-upz
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md
+ - docs/security/PSBT-SIGNING-ARCHITECTURE.md
+ - docs/UNIFIED-TASK-TRACKER.md
+ - core/archipelago/src/seed.rs
+autonomous: false
+requirements: [QUICK-UPZ-01, QUICK-UPZ-02, QUICK-UPZ-03]
+
+must_haves:
+ truths:
+ - "docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md exists and every single finding carries file:line evidence from the real tree — no finding is asserted from the research file alone (QUICK-UPZ-01)"
+ - "Each of [ARCHY-1] [ARCHY-2] [ARCHY-3] [ARCHY-4] is explicitly CONFIRMED, REFUTED, or UNVERIFIED against real code, and a refuted finding says so plainly rather than being quietly dropped (QUICK-UPZ-01)"
+ - "The audit covers all six mandated secret classes: master BIP-39 seed, LND aezeed, fleet release-root + catalog/manifest signing keys, node identity keys (nostr/FIPS/Reticulum), container::secrets generated_secrets, and session tokens/CSRF (QUICK-UPZ-01)"
+ - "[ARCHY-3] (one-ISO-many-nodes correlation) is answered from image-recipe/ evidence where the tree can answer it, and everything the tree cannot answer is listed in a separate, explicitly-labelled on-node verification checklist marked UNVERIFIED — never asserted as verified (QUICK-UPZ-01)"
+ - "Every finding has Severity (Critical/High/Medium/Low/Informational) + evidence + exploitability + blast radius + concrete remediation, and there is a 'What we do right' section (QUICK-UPZ-01)"
+ - "docs/security/PSBT-SIGNING-ARCHITECTURE.md exists and covers: descriptor-only watch-only wallet, the create->export->sign->import->finalize->broadcast loop with named Core RPCs, single-sig-HW and wsh(sortedmulti) multisig tiers, air-gap transport choice, LND's honest limits, hot wallet as explicitly-secondary, migration for existing hot-seed users, and a phased rollout that a future /gsd-plan-phase can consume (QUICK-UPZ-02)"
+ - "The PSBT spec states plainly that a routing node's channel/revocation/HTLC keys cannot be air-gapped, and splits the design into on-chain (PSBT-protectable) vs lightning (necessarily hot) (QUICK-UPZ-02)"
+ - "No produced document contains any real secret value — no mnemonic words, no private keys, no tokens, no passwords; secrets are referenced by path/variable name only (QUICK-UPZ-01, QUICK-UPZ-02, QUICK-UPZ-03)"
+ - "A prioritised remediation backlog exists in the audit doc and the resulting open items appear in docs/UNIFIED-TASK-TRACKER.md in that file's existing tier/checkbox format (QUICK-UPZ-03)"
+ - "If [ARCHY-1] is confirmed, the mnemonic-generation call site takes its RNG as an injected parameter (OsRng in production) and a test proves the injected RNG is the one actually used — a test that cannot exist before the change; if it is not applied, the audit records why in a greppable line (QUICK-UPZ-03)"
+ - "The other agent's work is untouched: container/secrets.rs, federation/storage.rs, pip.ts, Cloud.vue, OnboardingSeedGenerate.vue and the new composables/ files are read-only in this plan, and no commit authored by this plan contains any of those paths"
+ artifacts:
+ - "docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md (evidence-backed audit + prioritised remediation backlog + on-node verification checklist)"
+ - "docs/security/PSBT-SIGNING-ARCHITECTURE.md (watch-only/multisig/air-gap/LND spec + phased rollout)"
+ - "docs/UNIFIED-TASK-TRACKER.md (updated with the resulting open items)"
+ - "core/archipelago/src/seed.rs + its test module (ONLY if [ARCHY-1] is confirmed and the fix is small/obviously-correct/testable)"
+ key_links:
+ - "core/archipelago/src/seed.rs is the fan-out root — every other key class (node Ed25519 did:key, nostr, FIPS, release-root signing, per-identity keys, BIP-84 bitcoin, LND aezeed via HKDF) descends from the one mnemonic, so a defect there has strictly larger blast radius than a hardware wallet's"
+ - "bip39 2.1.0's Mnemonic::generate -> generate_in -> generate_in_with(&mut rand::thread_rng(), ...) is the transitive-default hop that makes the entropy source implicit at Archipelago's call site — this is the exact structural shape of the COLDCARD defect (T1)"
+ - "docs/hardware-signer-design.md already exists (TROPIC01 air-gapped signer, exploratory stub) — the PSBT spec must cross-link it as the future first-party signer, not duplicate or contradict it"
+ - "image-recipe/build-debian-iso.sh + image-recipe/archipelago-scripts/install-to-disk.sh + image-recipe/configs/*.service are the only places that can bake a random-seed or order key generation against crng init — they are the whole [ARCHY-3] evidence surface in-tree"
+---
+
+
+Turn the confirmed 2026-07-30 Coinkite COLDCARD low-entropy incident into three concrete
+Archipelago artifacts:
+
+1. **A real entropy & seed-generation security audit** of this codebase (`docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md`) —
+ performed against actual code with `file:line` evidence, not a restatement of the research.
+2. **A PSBT-first signing architecture spec** (`docs/security/PSBT-SIGNING-ARCHITECTURE.md`) —
+ watch-only descriptors, multisig, air-gap transport, honest LND limits, hot wallet as an
+ explicitly-secondary tier, and a phased rollout a future `/gsd-plan-phase` can consume.
+3. **A prioritised remediation backlog** wired into `docs/UNIFIED-TASK-TRACKER.md`, plus — only
+ if the audit proves it necessary — one small, obviously-correct, independently-tested
+ hardening fix.
+
+Purpose: Archipelago derives its **entire key hierarchy from one 24-word BIP-39 mnemonic**,
+including the **fleet release-root signing key**. A Coldcard-class entropy defect here would not
+just drain wallets, it would let an attacker forge signed release manifests for the whole fleet.
+The research found no such defect today — but it did find the *structural shape* that produced
+T1, and it flagged the ISO/first-boot entropy story as the single most plausible real exposure.
+
+Output: 3 commits on `main` (audit doc, spec doc, backlog + optional fix), pushed via `gitea-ai`.
+
+**This is an audit-and-spec task, not a feature build.** Do NOT implement PSBT, watch-only,
+multisig, or any wallet/signing behaviour. Do NOT refactor beyond the single gated fix in Task 3.
+
+**Tracer-first decomposition deliberately does not apply here** — there are no layers to slice
+through; this is a deliverable set. Task 1 is the load-bearing evidence pass and Tasks 2 and 3
+strictly depend on its findings. Execute the three tasks **in order**.
+
+
+
+@/home/archipelago/Projects/archy/CLAUDE.md
+@/home/archipelago/Projects/archy/.planning/quick/260731-upz-research-coinkite-conkite-low-entropy-ha/260731-upz-RESEARCH.md
+
+**Read the RESEARCH.md above IN FULL before starting.** It contains the confirmed incident
+analysis, the historical low-entropy catalogue (T1–T7), the greppable audit checklist with
+exact dangerous/correct API names, findings [ARCHY-1]…[ARCHY-5], and the LND capability matrix.
+Use it to aim the audit and to justify the spec's technical choices — do **not** re-derive it,
+and do **not** copy it wholesale into the deliverables.
+
+## Facts already established — do not re-derive
+
+- **The RNG-touching surface in this tree is bounded and already enumerated.** 33 Rust files
+ under `core/*/src` and 7 TypeScript/Vue files under `neode-ui/src` match the RNG API set.
+ Task 1's grep pipeline reproduces exactly this set. There is no need to read the whole repo.
+- **Dependency versions** (`core/archipelago/Cargo.toml`): `rand = "0.8.5"` (still has fork
+ protection; 0.9.0 removed it), `bip39 = { version = "=2.1.0", features = ["rand"] }`
+ (2.2.2 is current — do not bump in this task), `argon2 = "0.5.3"`, `zeroize = "1.8.2"`.
+- **`docs/security/` does not exist yet** — create it.
+- **`docs/hardware-signer-design.md` already exists** (171 lines, 2026-06-24, exploratory
+ TROPIC01 air-gapped-signer stub). Task 2 must cross-link and stay consistent with it.
+- **`docs/UNIFIED-TASK-TRACKER.md`** is 268 lines, organised as `## Tier 0 — Quick / mechanical,
+ no blockers`, `## Tier 1 — Medium effort, unblocked`, etc., with `- [ ]` / `- [x] ~~struck~~`
+ items, ordered fastest/simplest first. Match that format exactly.
+- **`image-recipe/_archived/` is out of audit scope** (dead auto-installer path). Note it as
+ explicitly excluded in the audit doc so the next auditor does not re-derive that.
+- **`core/archipelago/src/seed.rs` is CLEAN** in git (safe to edit in Task 3). Its test module
+ starts at line 479.
+
+## CONCURRENT-AGENT HAZARD — read carefully
+
+Another agent has uncommitted work in this shared tree. These files are **dirty**:
+
+```
+core/archipelago/src/container/secrets.rs <- IN AUDIT SCOPE, read-only
+core/archipelago/src/federation/storage.rs
+neode-ui/src/utils/pip.ts
+neode-ui/src/views/Cloud.vue
+neode-ui/src/views/OnboardingSeedGenerate.vue <- IN AUDIT SCOPE, read-only
+neode-ui/src/composables/usePaidItemViewer.ts (untracked)
+neode-ui/src/composables/usePipSession.ts (untracked)
+neode-ui/src/composables/__tests__/*.test.ts (untracked)
+```
+
+`container/secrets.rs` and `OnboardingSeedGenerate.vue` are **both in audit scope AND dirty**.
+Read them **as they are on disk**. Do NOT modify them, do NOT revert them, do NOT `git stash`,
+do NOT `git checkout` them. If the audit finds something in them, write it up as a finding with
+a note that the file had uncommitted third-party changes at audit time.
+
+**That agent is committing to these paths live** (`4b5367eb` federation/storage.rs, `bc9a210c`
+Cloud.vue, `3288a02d` pip.ts all landed after this plan was written). So expect the tree and the
+log to move underneath you. That is normal and is not your problem to fix. The invariant you owe
+is narrow and absolute: **no commit you author may contain any of those paths.** Do not rebase,
+do not reset, do not revert their commits, and re-read a file rather than trusting a stale read
+if you see it change.
+
+**Staging rule (CLAUDE.md, non-negotiable):** always `git add `. Never
+`git add -A`, never `git add .`, never `git commit -a`.
+
+## Honesty requirements — apply to all three tasks
+
+- **Never claim a path is safe without `file:line` evidence.** "I grepped and found nothing" is
+ a valid finding only if you state the grep and the directories it covered.
+- **Anything that cannot be verified from this environment is `UNVERIFIED`**, listed in the
+ on-node verification checklist, never asserted as checked. Real hardware (`.228` / dev-box)
+ is not reachable from this task.
+- **Never put a real secret into a document.** Reference the path or variable name
+ (`master_seed.enc`, `STRIPE_SECRET_KEY`), never a value. This includes example/illustrative
+ mnemonics — use `<24 words>` or `word1 … word24` placeholders, never a real wordlist.
+- Mark research-derived claims that you could not confirm in-tree as `[FROM RESEARCH,
+ NOT RE-VERIFIED]` rather than laundering them into audit findings.
+
+
+
+
+
+ Task 1: Entropy & seed-generation security audit against the real codebase
+ docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md
+
+ core/archipelago/src/seed.rs (full — the fan-out root),
+ core/archipelago/src/api/rpc/seed_rpc.rs (the network boundary, [ARCHY-4]),
+ core/archipelago/src/container/secrets.rs (DIRTY — read only),
+ neode-ui/src/views/OnboardingSeedGenerate.vue (DIRTY — read only),
+ image-recipe/build-debian-iso.sh + image-recipe/archipelago-scripts/install-to-disk.sh ([ARCHY-3]),
+ docs/adr/ (for the ADR-005 Argon2 parameter cross-check)
+
+
+Perform a real audit and write it to `docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md` (create
+`docs/security/` first). Do the evidence collection with the bounded pipeline below FIRST, then
+write — do not write findings before you have their line numbers.
+
+**Step A — collect evidence (bounded; ~10 bash calls, do not wander outside these paths).**
+Run these and keep the output as your evidence corpus. `core/*/src` deliberately excludes
+`core/target/`; exclude `image-recipe/_archived/` from conclusions:
+
+```
+grep -rnE 'SmallRng|seed_from_u64|::from_seed\(|rand::rngs::mock|StdRng' core/*/src --include=*.rs
+grep -rnE 'OsRng|thread_rng|rand::random|getrandom|SystemRandom' core/*/src --include=*.rs
+grep -rn -B3 -A3 -E 'SystemTime::now|as_nanos|Instant::now' core/*/src --include=*.rs | grep -iE 'key|seed|nonce|salt|token|secret|password|mnemonic'
+grep -rn -B2 -A2 -E 'Math\.random|getRandomValues|crypto\.subtle|jsbn|SecureRandom\(' neode-ui/src --include=*.ts --include=*.vue
+grep -rnE '\$RANDOM|/dev/urandom|/dev/random|openssl rand|uuidgen|random\.random|random\.randint|shuf ' scripts/ image-recipe/ --include=*.sh --include=*.py
+grep -rniE 'random-seed|urandom|jitterentropy|haveged|rng-tools|rngd|crng' image-recipe/ --include=*.sh --include=*.service --include=*.conf
+find image-recipe -name 'random-seed' -o -name '*.seed'
+grep -rniE '(info|warn|error|debug|trace)!\(.*(mnemonic|seed|privkey|private_key|passphrase|aezeed)' core/*/src --include=*.rs
+grep -rn 'derive(Debug' core/archipelago/src/seed.rs core/archipelago/src/identity.rs core/archipelago/src/credentials/store.rs
+cd core && cargo tree -i rand | head -40 && cargo tree -i getrandom | head -40
+```
+Run `cargo audit` only if `command -v cargo-audit` succeeds; if absent, record that as a gap
+(the research explicitly recommends `cargo audit`/`cargo deny` in CI rather than a snapshot).
+
+**Step B — trace every secret class.** For each of these, trace from the syscall to the consumer
+and record `file:line` at every hop. Any hop where the entropy source is a *default* rather than
+an *argument* is a T1-shaped structural risk and must be called out as such:
+(1) user Bitcoin/LND wallet seed; (2) LND aezeed (`HKDF(seed, "archipelago/lnd/entropy/v1")`);
+(3) **fleet release-root signing key** + catalog/manifest signing keys; (4) node identity keys
+(nostr / FIPS / Reticulum); (5) `container::secrets` `generated_secrets` materialisation
+(0600/rootless per CLAUDE.md — verify the mode is actually set, don't assume);
+(6) session tokens + CSRF; (7) onboarding seed generation in the UI.
+
+**Step C — adjudicate [ARCHY-1] … [ARCHY-4] individually.** Each gets its own subsection headed
+with the tag and a verdict of exactly `CONFIRMED`, `REFUTED`, or `UNVERIFIED`:
+- **[ARCHY-1]** — check `core/archipelago/src/seed.rs` around line 92 for the `bip39::Mnemonic::generate(24)`
+ call, then confirm the transitive default by reading the vendored crate at
+ `~/.cargo/registry/src/*/bip39-2.1.0/src/lib.rs` (research cites line 297). State whether the
+ entropy source is chosen at the call site or by the dependency.
+- **[ARCHY-2]** — verify the `kernel_csprng_ready()` probe near `seed.rs:52-91` really uses the
+ nonblocking flag *as a probe only* and that no key material is drawn from that path.
+- **[ARCHY-3]** — the highest-unknown item. From `image-recipe/` evidence answer: does the build
+ bake a populated seed file into the image; is there any first-boot regeneration unit; does the
+ image install `jitterentropy-rngd`/`haveged`/`rng-tools`; can onboarding key generation run
+ before the kernel CSPRNG is initialised on freshly-flashed hardware. Answer what the tree can
+ answer with `file:line`. Everything else goes to the on-node checklist as `UNVERIFIED`.
+- **[ARCHY-4]** — confirm whether the generated mnemonic crosses the JSON-RPC boundary
+ (`seed_rpc.rs` ~line 147), the in-memory TTL and whether it is cleared at verify time
+ (~lines 205-209), and whether the daemon can be served over plaintext HTTP.
+
+If a research finding does not survive contact with the code, write `REFUTED` and say why
+plainly. Do not soften it. Also cross-check open question 9: whether `Argon2::default()` in
+`seed.rs` matches ADR-005's stated 64MB/3-iteration profile — report the actual numbers.
+
+**Step D — write the document.** Structure:
+1. Scope + method (directories covered, greps run, what was explicitly excluded and why —
+ name `image-recipe/_archived/` and `core/target/`).
+2. Executive summary — the honest one-paragraph verdict.
+3. Findings table, then one subsection per finding. Every finding carries:
+ `Severity` (Critical/High/Medium/Low/Informational) | Evidence (`file:line`) | Exploitability |
+ Blast radius | Concrete remediation.
+4. `[ARCHY-1]`…`[ARCHY-4]` adjudication (Step C).
+5. **What we do right** — a real section, giving credit where the code is correct
+ (zeroization, encrypted-at-rest envelope, the CSPRNG-readiness probe, 24-word enforcement,
+ the correct browser RNG call sites — each with `file:line`).
+6. **On-node verification checklist (UNVERIFIED)** — the discrete checks that need real hardware,
+ each written as a runnable command an operator can paste on `.228`/dev-box, including the
+ cross-node same-ISO collision test from the research.
+7. Leave a placeholder heading `## Remediation Backlog` — Task 3 fills it.
+
+Severity must reflect *this* codebase, not the Coldcard incident. Do not inflate: a benign
+`Math.random()` that only picks which word to quiz is Low or Informational, and the audit should
+say so and annotate it so the next auditor does not re-derive that it is benign.
+
+Do not put any secret value in the document (see the secret-shaped-string gate in `verify`).
+Commit with `git add docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md` only.
+
+
+ AUD=docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md; test -f "$AUD" || { echo "MISSING FILE"; exit 1; }; for t in ARCHY-1 ARCHY-2 ARCHY-3 ARCHY-4 Severity "What we do right" UNVERIFIED; do grep -qi "$t" "$AUD" || { echo "MISSING: $t"; exit 1; }; done; n=$(grep -cE '\.(rs|vue|ts|sh|py|yml|toml):[0-9]+' "$AUD"); [ "$n" -ge 20 ] || { echo "FAIL: only $n file:line evidence refs, need >=20"; exit 1; }; grep -qE '(xprv|xpub|nsec|npub)[A-Za-z0-9]{40,}|BEGIN [A-Z ]*PRIVATE KEY' "$AUD" && { echo "FAIL: secret-shaped string in audit doc"; exit 1; }; echo OK
+
+ `docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md` exists; every finding has `file:line` evidence and a severity; all four ARCHY tags carry an explicit CONFIRMED/REFUTED/UNVERIFIED verdict; all six mandated secret classes are traced; the on-node checklist exists and is labelled UNVERIFIED; a "What we do right" section exists; no secret value appears anywhere; committed with explicit paths.
+
+
+
+ Task 2: PSBT / watch-only / multisig architecture spec
+ docs/security/PSBT-SIGNING-ARCHITECTURE.md
+
+ docs/hardware-signer-design.md (existing TROPIC01 signer stub — cross-link, do not duplicate),
+ core/archipelago/src/api/rpc/bitcoin.rs (integration point),
+ core/archipelago/src/seed.rs lines ~214-233 (BIP-84 derivation + LND aezeed HKDF),
+ apps/bitcoin-core/manifest.yml + apps/bitcoin-knots/manifest.yml + apps/lnd/manifest.yml (pinned versions),
+ the RESEARCH.md Part C tables (Core RPCs, LND capability matrix, air-gap formats)
+
+
+Write `docs/security/PSBT-SIGNING-ARCHITECTURE.md` — a spec, not a tutorial, and not an
+implementation. Ground every architectural claim in either RESEARCH.md Part C or a `file:line`
+from this tree; mark anything from neither as `[UNVERIFIED]`.
+
+Required sections:
+
+1. **Target architecture.** Watch-only **descriptor** wallet on the node (Core), created with
+ private keys disabled and populated via descriptor import — descriptor-only from day one,
+ because Core 30 removed BDB legacy wallets. Name the actual Core RPCs for each step of the
+ loop (create funded PSBT -> export -> sign offline -> import -> combine -> finalize ->
+ broadcast) and say which RPCs are wallet-scoped vs node-scoped. Recommend driving UI state
+ from `analyzepsbt` (it reports which role must act next) rather than guessing. Record the
+ current versions the node actually runs from the manifests, and flag `bitcoin-knots:latest`
+ as an unpinned tag at odds with ADR-009 (in-scope to flag, out-of-scope to fix).
+
+2. **Where each step lives.** Map the loop across the three surfaces: Rust orchestrator
+ (`core/archipelago`), `neode-ui`, and the companion app. Be explicit that the BIP-84 private
+ key stays in the daemon's encrypted store and the **xpub only** goes into the Core descriptor
+ wallet — the private key is never imported into Core.
+
+3. **Tiers.** Tier 1 single-sig with an external hardware signer; Tier 2 `wsh(sortedmulti(k,...))`
+ multisig with BIP-48 paths (`m/48'/coin'/account'/2'` for P2WSH) and descriptor exchange.
+ State why `sortedmulti` over ordered `multi`. Mandate key-origin annotation
+ `[fingerprint/derivation]` — hardware signers cannot locate their key without it. Treat
+ taproot/MuSig2 multisig as future work and say why (unconfirmed 2026 support).
+
+4. **Air-gapped transport.** Pick a format and justify it against the companion app's *existing*
+ QR scanner and shipped SeedQR capability. Compare BBQr (sequential) vs BC-UR v2 (fountain-coded,
+ order-independent, degrades gracefully in poor light) vs microSD/file. Be honest about QR
+ density: a real multi-input multisig PSBT exceeds single-QR capacity, so animated multi-frame
+ is mandatory and a file fallback must always be offered. Cross-link
+ `docs/hardware-signer-design.md` as the future first-party signer and keep the format choice
+ consistent with it.
+
+5. **LND — what is and is not achievable.** Reproduce the capability matrix as a decision table:
+ watch-only + remote signer YES; signer fully offline NO (it must accept a live inbound gRPC
+ connection); air-gapping channel/revocation/HTLC keys NO; PSBT channel funding YES; opening a
+ channel with zero LND wallet balance YES; self-broadcasting the funding transaction NEVER
+ (encode that as a hard UI rule — funds can be lost). Name the required xpub accounts and the
+ taproot import gotcha. Then split the whole design into **on-chain balance: genuinely
+ PSBT-protectable** vs **lightning balance: necessarily hot**, and give the exact honest
+ user-facing sentence the UI should use. Any copy implying a routing node's channel keys are
+ cold is misleading — say so.
+
+6. **Hot wallet as the explicitly-secondary option.** Hard separation of on-chain and Lightning
+ balances in the data model and the UI (never one blended number); server-enforced per-tx and
+ rolling-daily spend limits with anything above forced onto the PSBT path; reuse of the
+ existing at-rest encryption envelope; zeroization; and the explicit cold/warm/hot tiering in
+ the UI. State the design principle from the incident: T1's survivors were the users who took
+ the *optional* extra step, so the safe path must be the **default**, not the option. Also
+ spell out how to nudge toward PSBT without making the hot path feel broken or punitive.
+
+7. **Migration for existing hot-seed users.** The honest advice implied by the incident: a
+ software fix does not repair an already-generated seed. Specify the sequence — generate a new
+ key, verify the backup and a receive address, send a test transaction, migrate funds, retain
+ the old backup until confirmed — and state clearly which Archipelago users this does and does
+ not apply to based on Task 1's findings (do not over-alarm if Task 1 found no defect; do not
+ under-state if it did).
+
+8. **Phased rollout.** Concrete, plannable phases with dependencies and what each unlocks. This
+ document is the input to a future `/gsd-plan-phase`, so each phase needs a name, a goal
+ sentence, its dependencies, and 2-5 candidate requirement lines. Suggested shape (adjust with
+ reasoning): descriptor watch-only read path -> PSBT construct/export -> external-signer import
+ and finalize -> air-gap transport -> multisig -> LND remote signing -> hot-wallet limits.
+ Note explicitly which phases need real-hardware verification.
+
+Do NOT write implementation code. Do NOT add dependencies. Do NOT modify wallet or signing
+behaviour anywhere in the tree. Commit with `git add docs/security/PSBT-SIGNING-ARCHITECTURE.md`
+only.
+
+
+ SPEC=docs/security/PSBT-SIGNING-ARCHITECTURE.md; test -f "$SPEC" || { echo "MISSING FILE"; exit 1; }; for t in walletcreatefundedpsbt analyzepsbt finalizepsbt importdescriptors disable_private_keys sortedmulti BIP-48 remotesigner "necessarily hot" migration Phase hardware-signer-design; do grep -qi "$t" "$SPEC" || { echo "MISSING: $t"; exit 1; }; done; grep -qE '(xprv|xpub|nsec|npub)[A-Za-z0-9]{40,}|BEGIN [A-Z ]*PRIVATE KEY' "$SPEC" && { echo "FAIL: secret-shaped string in spec"; exit 1; }; echo OK
+
+ `docs/security/PSBT-SIGNING-ARCHITECTURE.md` exists covering all eight required sections; the LND section states plainly that channel/revocation/HTLC keys cannot be air-gapped and that the funding transaction must never be self-broadcast; on-chain vs lightning are split into separately-protectable tiers; the hot wallet is framed as explicitly secondary; the rollout is phased with dependencies and candidate requirements; `docs/hardware-signer-design.md` is cross-linked; no code changed; committed with explicit paths.
+
+
+
+ Task 3: Remediation backlog, tracker update, and the one gated hardening fix
+ docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md, docs/UNIFIED-TASK-TRACKER.md, core/archipelago/src/seed.rs
+
+ Only if [ARCHY-1] was CONFIRMED in Task 1, the mnemonic-generation path becomes RNG-injectable
+ and the following test exists and passes (it cannot exist before the change, because there is
+ no seam to inject through):
+ - `mnemonic_generation_uses_injected_rng`: driving generation with a deterministic
+ `CryptoRng + RngCore` test RNG produces a stable, asserted 24-word mnemonic (known-answer),
+ proving the passed RNG — not an implicit transitive default — is the one actually consumed.
+ - `mnemonic_generation_is_256_bit`: generated mnemonics are 24 words and two successive
+ productions from the real entropy source differ.
+ Existing seed tests stay green.
+
+
+**Part A — remediation backlog (always).** Fill the `## Remediation Backlog` placeholder left in
+`docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md`. Prioritise by severity x effort, fastest/most
+valuable first. Each item needs: the finding it closes, the concrete change, the file(s), an
+effort estimate, and whether it needs real-hardware verification. Anything that needs a proper
+phase (PSBT work, ISO first-boot entropy regeneration, confining seed-bearing RPCs to
+loopback/TLS) is listed here as a backlog item and explicitly **not** implemented in this task.
+
+**Part B — tracker (always).** Add the resulting open items to `docs/UNIFIED-TASK-TRACKER.md`
+using that file's existing conventions: `- [ ]` checkboxes, bold lead sentence, indented
+continuation lines, placed in the correct existing Tier (Tier 0 = quick/mechanical/no blockers,
+Tier 1 = medium effort/unblocked, etc.) rather than in a new section at the top. Link back to
+`docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md` and `docs/security/PSBT-SIGNING-ARCHITECTURE.md`
+so the tracker stays the single entry point. Use `Edit` for scoped insertions — never rewrite the
+whole file.
+
+**Part C — the one gated fix (conditional).** Apply this **only if Task 1 recorded [ARCHY-1] as
+CONFIRMED**. It is the only code change authorised by this plan.
+
+Refactor `core/archipelago/src/seed.rs` so the mnemonic-generation call site takes its RNG as an
+**injected parameter** instead of inheriting a transitive dependency's default: an internal
+helper that accepts `&mut (impl CryptoRng + RngCore)` and generates a 24-word English mnemonic
+through the injectable `bip39` entry point, with the production caller passing `OsRng`. Add a
+comment at the call site pinning the rationale (a bare reference to this audit and to T1 — an
+explicit source beats an implicit one, and a future `rand`/`bip39` bump must not silently rebind
+it). Then add the two tests from `` to the existing test module (`seed.rs:479`). The
+known-answer test is what makes this a fix rather than a comment: it is impossible to write
+against the pre-refactor code because there is no seam to inject through.
+
+Do **not** bump `bip39` or `rand` versions. Do **not** change the derivation, the word count,
+the empty-passphrase decision, the at-rest encryption, or anything else in `seed.rs`.
+
+You MAY additionally fix the `core/archipelago/src/totp.rs` modulo bias (rejection sampling or
+`SliceRandom::choose` in place of `% charset.len()`) **only if** it is a <=10-line change with its
+own test and Task 1 confirmed it. Anything beyond these two goes to the backlog. If neither is
+applied, write a greppable line `ARCHY-1: NOT APPLIED` into the audit doc with the reason.
+
+Build and test from `core/` (workspace root). If the build hits a `rust-lld: undefined hidden
+symbol` error, that is incremental-cache corruption — rebuild with `CARGO_INCREMENTAL=0`.
+
+**Part D — commit and push.** Commit Part A+B together and Part C separately (if applied), always
+with explicit paths: `git add docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md docs/UNIFIED-TASK-TRACKER.md`
+then `git add core/archipelago/src/seed.rs`. Never `git add -A`. Then push all three commits with
+`git push gitea-ai main` (`main` is protected; `gitea-ai` is the push account). If the push fails,
+report the exact error — do not force-push, do not retarget another remote, do not amend history.
+
+
+ AUD=docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md; grep -qi "Remediation Backlog" "$AUD" || { echo "FAIL: no remediation backlog"; exit 1; }; grep -q "ENTROPY-SEED-AUDIT-2026-07-31" docs/UNIFIED-TASK-TRACKER.md || { echo "FAIL: tracker missing audit link"; exit 1; }; grep -q "PSBT-SIGNING-ARCHITECTURE" docs/UNIFIED-TASK-TRACKER.md || { echo "FAIL: tracker missing spec link"; exit 1; }; FORBID='container/secrets\.rs|federation/storage\.rs|OnboardingSeedGenerate\.vue|utils/pip\.ts|views/Cloud\.vue'; for c in $(git log --format=%H -10 -- "$AUD" docs/UNIFIED-TASK-TRACKER.md core/archipelago/src/seed.rs core/archipelago/src/totp.rs); do git show --name-only --pretty=format: "$c" | grep -qE "$FORBID" && { echo "FAIL: commit $c mixed in the other agent's files"; exit 1; }; done; if grep -q "ARCHY-1: NOT APPLIED" "$AUD"; then echo "OK (fix deliberately not applied, reason recorded)"; else ( cd core && CARGO_INCREMENTAL=0 cargo test -p archipelago mnemonic_generation_uses_injected_rng 2>&1 | grep -qE 'test result: ok\. [1-9]' ) || { echo "FAIL: injected-rng known-answer test missing or failing"; exit 1; }; ( cd core && CARGO_INCREMENTAL=0 cargo test -p archipelago seed:: 2>&1 | grep -q 'test result: ok' ) || { echo "FAIL: existing seed tests not green"; exit 1; }; echo OK; fi
+ Show the reviewer the full `git diff` of `core/archipelago/src/seed.rs` (and `totp.rs` if touched) BEFORE pushing, plus the output of `git log --oneline -3` and `git show --stat HEAD`. This is master-seed generation code for every new node — get explicit confirmation that the diff is limited to making the RNG explicit + adding tests, and that no derivation, word count, passphrase, or at-rest-encryption behaviour changed. If [ARCHY-1] was not applied, show the recorded reason instead and confirm that is the right call.
+
+ The audit doc has a prioritised, actionable remediation backlog; `docs/UNIFIED-TASK-TRACKER.md` carries the new open items in its existing tier/checkbox format and links both new docs; either the injectable-RNG fix is applied in `seed.rs` with a known-answer test that passes and could not exist before the change, or `ARCHY-1: NOT APPLIED` plus a reason is recorded; no commit authored by this plan contains any of the other agent's five files; all commits staged by explicit path; the `seed.rs` diff was human-reviewed before push; commits pushed via `gitea-ai`.
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| kernel CSPRNG -> userspace key generation | every secret in the system crosses here; a weak or unready source here is unrecoverable |
+| daemon -> JSON-RPC/websocket client | the master mnemonic currently crosses this boundary ([ARCHY-4]); plaintext HTTP is in use on LAN in places |
+| build host -> flashed ISO -> N nodes | a single image is written to many nodes; anything entropy-bearing baked in is shared fleet-wide ([ARCHY-3]) |
+| this task -> committed documentation | audit/spec artifacts are public-facing repo content and could leak secrets or false assurance |
+| this task -> shared working tree | a concurrent agent's uncommitted work can be clobbered by careless staging |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|-----------|----------|-----------|----------|-------------|-----------------|
+| T-UPZ-01 | Information Disclosure | produced audit/spec docs | critical | mitigate | No secret values in any doc — paths/variable names only; enforced by the secret-shaped-string negative gate in Tasks 1 and 2 `` |
+| T-UPZ-02 | Repudiation / false assurance | audit findings without evidence | high | mitigate | Every finding requires `file:line`; `>=20` evidence refs enforced by Task 1 ``; unverifiable items forced into an `UNVERIFIED` on-node checklist |
+| T-UPZ-03 | Tampering | `core/archipelago/src/seed.rs` (master-seed generation for every node) | critical | mitigate | Fix is gated on [ARCHY-1] being CONFIRMED, bounded to RNG injection + tests, proven by a known-answer test, and human-reviewed via `` before push |
+| T-UPZ-04 | Tampering | concurrent agent's uncommitted work in the shared tree | high | mitigate | Explicit-path staging only (never `git add -A`); dirty files are read-only; Task 3 `` asserts the five dirty files are still dirty |
+| T-UPZ-05 | Information Disclosure | master mnemonic over plaintext-HTTP JSON-RPC ([ARCHY-4]) | high | transfer | Audited and written up with remediation (loopback/TLS confinement, shorter TTL); implementation deferred to a proper phase, not done here |
+| T-UPZ-06 | Spoofing | fleet release-root signing key derived from the same mnemonic | critical | mitigate | Explicitly traced as a first-class secret class in Task 1 Step B — a seed defect forges release manifests fleet-wide, strictly larger blast radius than a wallet |
+| T-UPZ-07 | Elevation of Privilege | one-ISO-many-nodes entropy correlation ([ARCHY-3]) | high | mitigate | `image-recipe/` evidence grep answers what the tree can answer; the rest becomes a runnable on-node checklist including the cross-node same-ISO collision test |
+| T-UPZ-SC | Tampering | package installs | low | accept | This plan adds no dependencies and installs nothing; `cargo audit` is invoked read-only if already present |
+
+
+
+1. Both documents exist under `docs/security/` and pass their automated gates.
+2. Every audit finding carries `file:line` evidence and a severity; all four ARCHY tags are adjudicated.
+3. Nothing that requires real hardware is claimed as verified — it appears in the UNVERIFIED on-node checklist instead.
+4. No secret value appears in any produced document.
+5. `docs/UNIFIED-TASK-TRACKER.md` carries the new open items in its existing format and links both docs.
+6. If code changed: `cd core && CARGO_INCREMENTAL=0 cargo test -p archipelago seed::` is green and the new known-answer test passes.
+7. No commit authored by this plan contains any of the concurrent agent's five files (they commit to those paths themselves — that is expected and is not a failure).
+8. All commits pushed via `gitea-ai`, or the exact push failure reported.
+
+
+
+- `docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md` is an audit a security reviewer would accept: evidence-backed, severity-classified, honest about what it could not verify, and generous where the code is right.
+- `docs/security/PSBT-SIGNING-ARCHITECTURE.md` is directly plannable — a future `/gsd-plan-phase` can pick up its phase breakdown without re-deriving the architecture.
+- The remediation backlog is in `docs/UNIFIED-TASK-TRACKER.md`, so the work is not stranded in a doc nobody reads.
+- At most one small, human-reviewed, test-proven code change landed; everything larger is queued as a backlog item.
+- Zero disruption to the concurrent agent's uncommitted work.
+
+
+