docs(10-05): record the Core-wallet fleet census — 4 nodes clear, 6 unchecked (D-07b)
Task 3 of plan 10-05, run by the operator over Tailscale on 2026-08-02 using the read-only procedure in KEY-03-SIGNING-POSTURE.md. No escalation: nothing found. Examined and CLEAR (4): archi-dev-box, shorty-s/.228, archy-x250-beta, archy-x250-pa. On every one there is no wallet named `archipelago` — the deleted handler's default wallet_name — `listwallets` returns only the unnamed default, and that default reports blank=true, keypoolsize=0, txcount=0, balance=0. The only named wallets are Fedimint gatewayd-*. The result holds across two container vintages (bitcoin-knots and bitcoin-core), so it is not four copies of one image behaving identically. Not examined (6), recorded with reasons rather than omitted: framework-pt, archipelago-1, archipelago and archy-dev-pa (SSH permission denied — password rotated/not held), archipelago-5 (timed out during banner exchange), and archy-x250-dev (offline). Password auth was deliberately not attempted: several fleet nodes lock PAM quickly on a wrong password, and locking out an in-use production node is a worse outcome than an incomplete census. The conclusion is stated at the strength the evidence supports — no *examined* node holds a wallet the deleted handler created, and no examined node holds any wallet with keys or funds. It is deliberately NOT generalised to "the fleet is clear" while six nodes are unknown. F-13 is closed by deletion regardless: the code that could create such a wallet is gone from every future build. No key material appeared in any output and `listdescriptors true` was never run. Also corrects the now-stale R-04/F-13 entry in UNIFIED-TASK-TRACKER.md, which still described `handle_bitcoin_init_wallet_from_seed` and a watch-only migration as pending work — that code no longer exists. Marks it done-by- deletion and adds the six unchecked nodes as a standing item, flagged as a natural fold-in for KEY-04's on-node work but tracked independently so it does not vanish if KEY-04 is re-scoped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
454388226c
commit
0d513a0ef7
@@ -203,19 +203,33 @@ those are marked ✅ below with the commit that did it, so we stop re-litigating
|
||||
authentication boundary on a live fleet — **needs its own `/gsd-plan-phase` with a
|
||||
federation re-verify**, not an opportunistic patch.
|
||||
|
||||
- [ ] **PSBT-first signing: Phase 1 — move the Bitcoin private key out of Core**
|
||||
(entropy audit R-04, finding **F-13** + `docs/security/PSBT-SIGNING-ARCHITECTURE.md`
|
||||
§8 Phase 1). `handle_bitcoin_init_wallet_from_seed` passes
|
||||
`disable_private_keys = false` (`core/archipelago/src/api/rpc/bitcoin.rs:203`) and
|
||||
imports `wpkh(xprv/0/*)` / `wpkh(xprv/1/*)` (`:229-231`), so the BIP-84 account
|
||||
**private** key is persisted in Bitcoin Core's `wallet.dat` — with an empty wallet
|
||||
passphrase (`:205`) — in addition to the daemon's Argon2 envelope. The descriptors
|
||||
also carry **no `[fingerprint/derivation]` key origin**, so no hardware signer
|
||||
could ever locate its key even if the private key were removed. Fix: watch-only
|
||||
(`disable_private_keys=true`) + xpub-with-origin descriptors + a migration that
|
||||
verifies balance and UTXO parity **before** removing the old wallet. This is the
|
||||
single highest-value change in the PSBT spec and unblocks every later phase.
|
||||
Needs a node with real UTXO history to verify.
|
||||
- [x] **PSBT-first signing: Phase 1 — move the Bitcoin private key out of Core** — **DONE
|
||||
2026-08-02 by deletion, not conversion** (entropy audit R-04, finding **F-13**;
|
||||
Phase 10 plan 10-05, decision **D-07b**). The handler that imported the BIP-84
|
||||
account **private** key into Core's `wallet.dat` had no caller anywhere, LND is the
|
||||
wallet the UI drives, and the endpoint was authenticated *and* password-gated — so
|
||||
it was deleted outright rather than rewritten watch-only. `bitcoin.rs`'s wallet-init
|
||||
handler and its `dispatcher.rs` arm are gone; **no daemon code path writes the
|
||||
BIP-84 private key into Bitcoin Core.** No migration was performed or is needed —
|
||||
a 4-node fleet census found no wallet the handler created. D-09's key-origin
|
||||
requirement moved to the PSBT itself: `lnd.create-psbt` now reports
|
||||
`key_origin` (`psbt_key_origin_report`, `api/rpc/lnd/wallet.rs`).
|
||||
**Read `docs/security/KEY-03-SIGNING-POSTURE.md` for the current state** — it also
|
||||
records the verdict that **no fleet node is provisioned watch-only**, so what ships
|
||||
today is PSBT *transport*, not air-gapped custody.
|
||||
|
||||
- [ ] **Finish the Core-wallet fleet census — 6 nodes unchecked** (Phase 10 plan 10-05,
|
||||
Task 3; standing item). The 2026-08-02 census examined 4 nodes (archi-dev-box,
|
||||
shorty-s/.228, archy-x250-beta, archy-x250-pa) and found **no** wallet created by
|
||||
the deleted handler and no wallet holding keys or funds. Six were not examined:
|
||||
framework-pt, archipelago-1, archipelago, archy-dev-pa and archipelago-5
|
||||
(SSH auth/connectivity) and archy-x250-dev (offline). Re-run the **read-only**
|
||||
procedure in `docs/security/KEY-03-SIGNING-POSTURE.md` § *Fleet census* when
|
||||
credentials or connectivity allow — a natural fold-in for KEY-04's on-node work.
|
||||
**Never run `listdescriptors true`** (it returns private keys). If any node reports
|
||||
a wallet named `archipelago`, or any descriptor wallet with
|
||||
`private_keys_enabled: true` that is not blank/empty, **stop and escalate — do not
|
||||
migrate or modify it** (D-07b).
|
||||
|
||||
- [ ] **PSBT-first signing: Phases 2-7 rollout**
|
||||
(`docs/security/PSBT-SIGNING-ARCHITECTURE.md` §8) — the spec is written to be
|
||||
|
||||
@@ -92,8 +92,8 @@ key was ever imported into it and no transaction ever touched it**. The two `gat
|
||||
are Fedimint gateway wallets, unrelated to the BIP-84 path. The `wallet.dat` at the datadir root
|
||||
is Core's own legacy default-wallet location, not this handler's output.
|
||||
|
||||
**This is one node.** archi-dev-box is verified; the rest of the fleet is **UNVERIFIED** pending
|
||||
the census below.
|
||||
**This is one node.** The same check was subsequently run across the reachable fleet — see the
|
||||
census below: **4 nodes examined and clear, 6 unreachable and therefore unknown.**
|
||||
|
||||
**Supporting history evidence:** `git log -S "init-wallet-from-seed"` scoped to
|
||||
`core/archipelago/src/api/rpc/dispatcher.rs` and `neode-ui/src` returns exactly one commit —
|
||||
@@ -314,11 +314,16 @@ loss.
|
||||
|
||||
## Fleet census — Core descriptor wallets
|
||||
|
||||
**Status: INCOMPLETE — one node verified, fleet pending.** This section answers one question per
|
||||
node: *does this node hold a Bitcoin Core descriptor wallet that
|
||||
`handle_bitcoin_init_wallet_from_seed` created, and does it hold private keys?* It is recorded
|
||||
per node rather than assumed, because deletion closes the door but does not tell us whether
|
||||
anyone walked through it before.
|
||||
**Status: run 2026-08-02 — 4 nodes examined and CLEAR, 6 nodes UNCHECKED. No escalation.**
|
||||
|
||||
This section answers one question per node: *does this node hold a Bitcoin Core descriptor wallet
|
||||
that the deleted wallet-init handler created, and does it hold private keys?* It is recorded per
|
||||
node rather than assumed, because deletion closes the door but does not tell us whether anyone
|
||||
walked through it before.
|
||||
|
||||
The nodes that could **not** be examined are listed with their reasons, not omitted. A census
|
||||
that quietly drops its failures is worthless — an auditor must be able to see exactly which
|
||||
machines were looked at and which were not.
|
||||
|
||||
### Hard constraint on every command in this census
|
||||
|
||||
@@ -366,20 +371,66 @@ bitcoin-cli -rpcuser=archipelago -stdinrpcpass -rpcwallet=<name> listdescriptors
|
||||
bitcoin-cli -rpcuser=archipelago -stdinrpcpass getnetworkinfo | head
|
||||
```
|
||||
|
||||
### Results
|
||||
### Results — examined, 2026-08-02 (4 nodes, all CLEAR)
|
||||
|
||||
| Node | App / version | `wallets_dir_present` | `listwallets` | `archipelago` wallet? | Private-key-bearing wallet? | Verdict |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **archi-dev-box** | Bitcoin Knots 29.3 (`bitcoin-knots` container) | — (`listwalletdir` used instead) | `[ "" ]` | **No** | The unnamed default wallet reports `private_keys_enabled: true`, but also `blank: true`, `keypoolsize: 0`, `txcount: 0`, `balance: 0` — Core's own statement that **no key was ever imported and no transaction ever touched it**. It is not this handler's output. | **CLEAR** — verified 2026-08-02 |
|
||||
| `.228` (shorty-s, resilience node) | — | — | — | — | — | **UNCHECKED** |
|
||||
| `.198` (OptiPlex) | — | — | — | — | — | **UNCHECKED** |
|
||||
| `.116` (thinkpad / dev node) | — | — | — | — | — | **UNCHECKED** |
|
||||
| `x250-dev` | — | — | — | — | — | **UNCHECKED** |
|
||||
| framework-pt | — | — | — | — | — | **UNCHECKED** |
|
||||
Run by the operator over Tailscale, read-only RPCs only.
|
||||
|
||||
`listwalletdir` on archi-dev-box also returned two `gatewayd-02004b91…` / `gatewayd-03443c0c…`
|
||||
wallets. These are **Fedimint gateway** wallets, unrelated to the BIP-84 path and out of scope
|
||||
for this finding.
|
||||
| Node | Tailscale IP | Container | `listwalletdir` | `listwallets` | `archipelago` wallet? | Default wallet state | Verdict |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **archi-dev-box** | `100.69.68.39` | `bitcoin-knots` | 2× `gatewayd-*`, `""` | `[ "" ]` | **No** | `blank: true`, `keypoolsize: 0`, `txcount: 0`, `balance: 0.00000000`, `descriptors: true` | **CLEAR** |
|
||||
| **shorty-s** (`.228`) | `100.64.204.114` | `bitcoin-knots` | 1× `gatewayd-*`, `""` | `[ "" ]` | **No** | same | **CLEAR** |
|
||||
| **archy-x250-beta** | `100.72.136.5` | `bitcoin-core` | 1× `gatewayd-*`, `""` | `[ "" ]` | **No** | same | **CLEAR** |
|
||||
| **archy-x250-pa** | `100.89.209.89` | `bitcoin-core` | 1× `gatewayd-*`, `""` | `[ "" ]` | **No** | same | **CLEAR** |
|
||||
|
||||
On every examined node there is **no wallet named `archipelago`** — the deleted handler's default
|
||||
`wallet_name`. The only named wallets are Fedimint `gatewayd-*`, unrelated to the BIP-84 path.
|
||||
|
||||
The one loaded wallet on each node is Core's unnamed default. It does report
|
||||
`private_keys_enabled: true`, but also `blank: true` with `keypoolsize: 0`, `txcount: 0` and
|
||||
`balance: 0.00000000` — **Bitcoin Core's own statement that no key was ever imported into it and
|
||||
no transaction ever touched it.** It is not the deleted handler's output, and it holds nothing.
|
||||
|
||||
**The result holds across two container vintages** — `bitcoin-knots` on two nodes and
|
||||
`bitcoin-core` on two others. That matters: it is not four copies of one image behaving
|
||||
identically, so the finding is a property of the fleet rather than an artefact of a single build.
|
||||
|
||||
**No key material appeared in any output, and `listdescriptors true` was never run.**
|
||||
|
||||
### Not examined, 2026-08-02 (6 nodes, with reasons)
|
||||
|
||||
| Node | Tailscale IP | Why not checked |
|
||||
|---|---|---|
|
||||
| framework-pt | `100.65.115.109` | `Permission denied (publickey,password)` — SSH password rotated, not held |
|
||||
| archipelago-1 | `100.82.34.38` | `Permission denied (publickey,password)` |
|
||||
| archipelago | `100.70.96.88` | `Permission denied (publickey,password)` |
|
||||
| archy-dev-pa | `100.64.83.15` | `Permission denied (publickey,password)` |
|
||||
| archipelago-5 | `100.114.134.21` | Timed out during SSH banner exchange |
|
||||
| archy-x250-dev | `100.113.100.55` | Offline — Tailscale reports last seen 2 days prior |
|
||||
|
||||
**Password authentication was deliberately not attempted on any of these.** Several fleet nodes
|
||||
lock PAM quickly on a wrong password, and locking an in-use production node out is a worse
|
||||
outcome than an incomplete census. These are recorded as UNCHECKED, **not** as clear.
|
||||
|
||||
### Conclusion, at the strength the evidence supports
|
||||
|
||||
> **No examined node holds a wallet created by the deleted handler, and no examined node holds
|
||||
> any wallet with keys or funds.** Four nodes, across two container vintages, on 2026-08-02.
|
||||
|
||||
**This is deliberately not a claim that "the fleet is clear."** Six nodes were not examined, and
|
||||
an unexamined node is unknown, not safe. F-13 is closed **by deletion** — the code that could
|
||||
create such a wallet is gone from every future build, which is true regardless of the census —
|
||||
and the census adds that no such wallet was found where anyone could look.
|
||||
|
||||
### Standing item — finish the census
|
||||
|
||||
The six unchecked nodes remain open. **Homed in `docs/UNIFIED-TASK-TRACKER.md`** (the project's
|
||||
canonical "what's open" list) as *"Finish the Core-wallet fleet census — 6 nodes unchecked"*,
|
||||
rather than only here, so it is visible to someone who is not already reading a security
|
||||
document. It is flagged there as a natural fold-in for **KEY-04's on-node work**, which needs
|
||||
node access anyway — but it is tracked independently so it does not vanish if KEY-04 is
|
||||
re-scoped.
|
||||
|
||||
Re-run the read-only procedure above when credentials or connectivity allow.
|
||||
|
||||
### Standing rule if a wallet is found
|
||||
|
||||
|
||||
Reference in New Issue
Block a user