D-19 supersedes D-15's two-repo premise and voids D-18. Flags 13-06/13-09/13-11
as needing a re-plan against aiui/ before wave 2 runs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings AIUI's full 230-commit history under aiui/ via git subtree, plus main's
current head. Operator decision 2026-08-03: AIUI moves into this repo rather
than staying at git.tx1138.com. This also lands e30ac1d (13-01 Task 3), which
was stranded local-only while that remote was unreachable.
Plans 13-06, 13-09 and 13-11 still target /home/archipelago/Projects/AIUI paths
and must be re-planned against aiui/ before wave 2 runs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tasks 1 (session-gated model forwarder, 97921d99) and 2 (retire the Python
sidecar/OpenRouter relay, b28cc3ee) are committed, cargo build --package
archipelago succeeds, and all 5 model_proxy:: unit tests are confirmed
passing (via direct execution of the compiled test binary, since a fresh
`cargo test` invocation was too slow to complete under severe host resource
contention — see the SUMMARY's Issues Encountered for the full account).
Task 3 (checkpoint:human-verify, gate="blocking" — real-node curl/systemd
proof, S-15) is intentionally NOT executed. Per the plan and this
executor's instructions, it halts here and returns a structured checkpoint
rather than self-approving.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Records the continuation ground-truth review of WIP checkpoint 6ba52b22,
the atomic per-task re-commit (fe6ccff7 Rust spine, 0ab9bdc7 neode-ui
broker), and the external-repo Task 3 commit (AIUI e30ac1d, not yet
pushed). Logs two open WINDOWS.md items: the cargo test run that never
completed under machine resource contention (id 16), and the AIUI push
blocked by an unreachable git.tx1138.com (id 17).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4 cores, load 35, 15G of 23G swap in use, rustc at 8.3G RSS while a live
node (bitcoind/electrumx/lnd) shares the machine. Two concurrent cargo
builds in separate worktrees (no shared target dir) made wave 1 crawl for
over an hour with zero commits. Wave 2 has four plans, so this would have
gotten worse before it got better.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Promotion to Trusted is a privilege escalation — a Trusted peer can read
node state, be deployed to, and is exempt from the `!= Untrusted` gates
federation/DWN/messaging use. It must therefore cost a fresh proof that
the person at the keyboard is the operator, not merely that a session
cookie exists. Same reasoning as node.rotate-identity and TOTP setup,
both of which already re-verify.
Both entry points are covered:
- `federation.invite` gates on the RESOLVED level, not on an explicit
request for Trusted: "Link Your Nodes" sends no `trust_level` at all
and falls through to the Trusted default. The invite is a bearer grant
of Trusted to whoever redeems it, so minting it IS the escalation.
Observer invites are untouched.
- `federation.set-trust` gates only when the peer is not already
Trusted, so the dropdown re-emitting its own value doesn't demand a
password for a no-op.
Demotion is deliberately NOT gated: making something less privileged
must never be harder than leaving it alone, or the safe action becomes
the inconvenient one.
The backend is the sole authority on what counts as an escalation — it
returns a `PASSWORD_REQUIRED:`-prefixed error and the UI prompts and
retries only on that, so the rule lives in exactly one place and the
frontend never pre-judges. TrustPasswordModal.vue (modelled on
RotateDidModal.vue) serves both flows. NodeDetailModal's select snaps
back to the node's real level on change, since a cancelled or failed
promotion would otherwise leave the dropdown displaying a level the node
never accepted.
The operator path stamps TrustSource::Manual; set_trust_level grew an
`Option<TrustSource>` so automatic adjustments (the discovery-handshake
demotion safety net) pass None and leave the recorded provenance alone
rather than laundering an uninvited-join peer into looking approved.
Follow-up, deliberately out of scope: `federation.join` also reaches
Trusted when redeeming someone else's Trusted invite, with no re-auth.
Tests: 44/44 federation, 79/79 rpc-client, vue-tsc clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wave 1 recovery: 13-03 was complete and is merged into the lane; 13-01 and
13-02 had uncommitted executor work rescued into WIP checkpoints and are
being continued in their existing worktrees.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
13-ROUTSTR-FINDINGS.md records the routstr_probe run against all three
docs.routstr.com default relays: zero kind-38421 events and zero
#d=routstr-provider fallback events in a 30s window each, with all
three relay connections succeeding (ruling out a connectivity
failure as the explanation). Every claim is labelled OBSERVED or
DOCS-ONLY per the plan's scope note; RESEARCH assumption A2 is
recorded as neither confirmed nor refuted, risk unchanged.
COVERAGE.md's three former "INTEGRATE — UNCONFIRMED" rows (tool
calling, Cashu payment header, Nostr provider discovery) are
downgraded to explicit opt-outs with dated, evidenced reasons — zero
rows retain unconfirmed-integration status. The Gate section states
13-13 may not proceed directly and must open with a checkpoint:
decision, which 13-13-PLAN.md's Task 1 already is (proceed-observed /
proceed-docs-with-probe-first / defer-with-residual) — no edit to
13-13-PLAN.md was needed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Plan-checker revision iteration 1 — 1 blocker + 2 warnings.
BLOCKER (context_compliance, D-13): the music track blocked phase
completion despite being locked as non-blocking. 13-15 depended on
13-11, which chains back through 13-07 to 13-04, so the phase could
not close without the entire music chain. Took the checker's option
(b): 13-15 depends_on is now ["13-06","13-09","13-14"] — 13-06 added
so the content-grid check stays a real gate, 13-11 dropped so no path
reaches 13-04/13-07/13-11. UAT step 7 is now content-only and blocking;
new step 7b is the music view as record-and-defer, the same shape step
10 already used for Routstr. Verified: 13-15's transitive closure
contains no music plan.
WARNING (scope_reduction): T-13-32 claimed the filebrowser-client.ts
JWT-in-query-string leak was "fixed" while only guaranteeing it was not
propagated. Now actually fixed — streamUrl returns a query-free
same-origin URL and relies on the path=/ cookie login() already sets;
filebrowser-client.ts and a new regression test are in 13-06's
files_modified. T-13-32 is scoped to new code; new T-13-39 owns the
pre-existing leak and names the residual (the JWT is still 24h, now
confined to the cookie jar).
WARNING (verification_derivation): the edge-probe reconciliation did
not match the files. Corrected in 13-VALIDATION.md — 10 probe findings
vs 9 edge entries kept apart, 13-07's 3 truths retagged as authored
rather than probe-surfaced. No truths deleted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AIUI conversational node control & content surfaces, decomposed tracer-first:
13-01 leads with one end-to-end read-only tool proving the whole spine
(AIUI chat -> postMessage -> authenticated RPC -> Rust agent loop -> real
node data), then expands.
Waves 1-8 across three tracks that stay independent per D-13:
- control/assistant: 13-01, 13-05, 13-08, 13-10, 13-12, 13-13, 13-14
- content: 13-06
- music library: 13-04, 13-07, 13-11 (no control/content plan depends on it)
- security & delivery: 13-02, 13-03, 13-09
- on-device sign-off: 13-15
Notable decisions recorded in the plans:
- Open Q1: delete-and-replace the live unauthenticated port-3142 Claude proxy
with a session-gated Rust forwarder; the OpenRouter open relay is removed.
- Open Q2: /aiui/-scoped CSP connect-src plus a per-session rate limit;
the iframe sandbox attribute is explicitly rejected with reasons.
- Open Q3: a live Routstr spike (13-03) gates the Routstr backend (13-13).
- Open Q4: one "assistant." dispatcher prefix arm, so the existing
session/CSRF/RBAC gate applies unchanged before dispatch.
- Promote (not add-alongside) CallerScope as the primary caller/permission
noun; the mesh-specific controls become one variant's resolution inputs.
- schemars rejected as an unaudited crate; JSON Schema is hand-written.
- AI-SPEC's `cargo test --test assistant_evals` corrected to an in-crate
module: core/archipelago is a binary-only crate with no lib target.
Also adds COVERAGE.md (Routstr capability matrix, every opt-out reasoned).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
v1.7.120-alpha is shipped and verified; do not re-cut it. Two fixes landed
after it: federation trust escalation (c0cfc72a) and the lnd-ui OTA pin +
host networking (5088aef5).
The task file now carries a RESUME HERE block with the groundwork already
located for the next item (the password gate on granting Trusted) — the
exact helper, both entry points with line numbers, and the rule that
demotion stays ungated — so the next session does not repeat the search.
Paused here deliberately rather than starting the app-port auth work at
low context: it is the largest item, the operator asked for umbrelOS and
StartOS research first, and it is the same bug class as the leaks fixed
in v1.7.120 but across every app port and transport.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reported: Framework PT took the OTA and got the new bitcoin-ui but not
lnd-ui. Two causes, both in the update path rather than the app.
1. LND_UI_IMAGE was "lnd-ui:latest" while BITCOIN_UI_IMAGE was pinned to
1.7.119-alpha. Podman does not re-pull a tag it already holds locally,
so a node that ever pulled lnd-ui:latest keeps that copy forever and
every subsequent release silently no-ops. Pinned to 1.7.119-alpha, so
a version change is what triggers the pull — the same mechanism that
made bitcoin-ui update correctly.
2. first-boot-containers.sh declared lnd-ui as bridge with -p 18083:80.
docker/lnd-ui/nginx.conf listens on 18083 DIRECTLY (it must, to proxy
the backend on 127.0.0.1:5678 same-origin), so that maps a host port
onto a container port nothing serves — reproduced on-node as HTTP 000.
This is the THIRD copy of the same declaration: container-specs.sh and
apps/lnd-ui/manifest.yml were both already corrected, this one was
missed, and it is the copy fresh installs use. Now host-networked with
no published ports, matching its siblings and the other two copies.
The underlying hazard is that one container spec lives in three files
that can disagree; recorded as a follow-up rather than refactored here.
Also opens .planning/RELEASE-1.7.121-TASKS.md — every outstanding item
for the next release with its evidence, so nothing in a fast-moving queue
gets lost between sessions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verifies the AIUI-01 gating question against source (no tool-calling
anywhere in this codebase today; Pine's HA intents are read-only Q&A,
not an action-executing loop), surfaces a live unauthenticated
Claude-proxy exposure (port 3142) and a same-origin iframe sandbox
gap not previously named, and maps existing Cashu/Nostr primitives
onto the Routstr integration.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Records the two release-process traps for the next cut: the manifest is
committed before signing (so the fleet would refuse the OTA), and
gitea-vps2 is the same server as gitea-ai with a dead token.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AIUI is embedded and styled but not functional: the chat cannot act on the
node and its content views are not wired to real data. Phase 13 scopes making
it work — Pine's human-language intent->action capability reachable from typed
chat, conversational settings, and the peer-files/music/movies/node-content
surfaces rendered live.
The gating requirement is AIUI-04: a user-granted capability sandbox. An LLM in
the browser is now adjacent to wallet keys, macaroons and node identity, so
secrets stay server-side behind scoped tokens, capability grants default closed
and stay revocable, destructive operations need a human confirmation, and
peer-supplied text is treated as untrusted input to the model context. This must
not widen the Phase 10 hard-refuse gates.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bookkeeping left uncommitted by an earlier session. It records work that
is already shipped — bc9a210c routed Paid Files pictures/videos into the
app lightbox with a visible wait — so the checklist and the mapping table
were simply lagging the code.
Landed as its own commit rather than swept into the release: it is not my
edit, and the release script refuses to run with a dirty tree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Includes what was NOT verified — the torrc block is deployed but dormant,
since regenerate_torrc only fires on a Tor services change and the change
is inert until bitcoind gets an -onion flag in Phase 12.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Per the operator: every option umbrelOS surfaces must be reachable in the
UI, Knots-only options surfaced separately from the ones Core shares, and
network mode a setting whose DEFAULT is Tor rather than clearnet.
Scoped as a phase rather than done inline because bitcoind's arguments are
currently hardcoded in three places (first-boot-containers.sh,
container-specs.sh, apps/bitcoin-knots/manifest.yml) — the same
triplication that produced the lnd-ui HTTP 000 defect. There is nowhere
for a UI to write, so BTCSET-01 is a settings model those three render
FROM, not another restatement.
Two constraints recorded up front so they are not discovered late:
- Knots-only flags gated to Knots is a CORRECTNESS requirement — offering
one on Core yields a node that refuses to start.
- Several options are not freely reversible: txindex forces a reindex,
prune is destructive and needs a full resync to undo. On a node that is
somebody's wallet backend those must be labelled and gated, not
silently applied. Any change at all restarts bitcoind, interrupting
LND, electrs and the fedimint gateways.
Inbound onion is explicitly out of scope: it needs Tor's ControlPort,
which is deliberately disabled for security, so the node reaches .onion
peers but stays unlisted. The UI must say so rather than imply otherwise.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deployed 6b3693dc to archi-dev-box off a clean tree and exercised the
real RPC surface. lnd.getinfo returns a real identity_pubkey through the
field that did not exist before this plan. mesh.lightning-peers returns
an empty array with success — the FED-05 empty edge proven on hardware,
not just in a unit test. Both refusal paths of mesh.send-lightning-info
observed live, including T-01-13's "no broadcast form".
The finding worth carrying to 01-06: this node's LND advertises no URI
(uris: []), so send-lightning-info correctly refuses rather than
shipping an empty advertisement a peer would store as an undialable
target. The receive and list halves work; the SEND half is inert on any
node whose LND has no externally reachable address. The picker must not
assume the local node always has something to share.
Corrects this summary's own earlier claim that nothing was exercised on
hardware — that was true when written and is not now. The mesh leg
proper (an advertisement crossing real RF into a peer's lightning_uri)
remains unproven and is still called out as such.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 3, completing 01-04.
mesh.lightning-peers returns the peers that have advertised a Lightning
URI: filtered, deduplicated, deterministically ordered, and an empty
array rather than an error when nobody has — "nobody yet" is a normal
state on a fresh node, not a fault.
mesh.send-lightning-info advertises this node's own URI to ONE chosen
peer. There is deliberately no broadcast form: this discloses the node's
payment endpoint, and who learns it is the operator's choice rather than
a side effect of being in radio range (T-01-13). It refuses to send when
LND advertises no URI, instead of sending an empty one a peer would
store as an undialable target.
The list-building and target-parsing logic is extracted into pure
functions because this file has no handler test harness and the
handlers need a live mesh service. That keeps the three contracts that
actually matter provable rather than merely readable:
- dedup is keyed on identity_pubkey_hex() — the AUTHENTICATING key,
lowercased — never the firmware routing key, so a radio contact and
its federation twin collapse to one entry (T-01-11)
- "newest advertisement wins" compares PARSED RFC3339 timestamps, not
strings: 09:30-01:00 is later than 10:00Z while sorting earlier as
text, and there is a test that fails if that is ever string-compared
- ordering is name-then-contact_id and asserted byte-identical across
eight rotations of the input, because a HashMap's iteration order is
not stable and a picker that reshuffles between reads means an
operator can click a different node than the one they aimed at
The peer allow-list is untouched: server.rs has an empty diff and
is_peer_allowed_path still occurs 13 times (T-01-15).
Verified: cargo test -p archipelago 1087 passed / 0 failed; clippy
--all-targets clean in every touched module (two useless_format lints in
the new test code fixed, not waived).
The SUMMARY records one deviation honestly: Task 1's tests were written
alongside its implementation rather than before, so no pre-implementation
failing output exists. A mutation test was run in its place — disabling
the pubkey validation fails 3 of the 5 tests — which proves the
assertions bind, and the mutation was reverted and verified gone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Controlled test on archi-dev-box with operator approval. The daemon was
stopped first so the reconciler could not repair the state before the
re-exposure was confirmed — without a confirmed 200, the later 401 would
be consistent with the state never having been broken at all.
1. stale conf installed + container restarted -> POST /bitcoin-rpc/
returned 200 with a real block height and Allow-Origin: *
2. daemon started 20:00:36, nothing else touched
3. 20:02:19 reconcile rendered the conf and logged the expected warn
line naming bitcoin-ui/archy-bitcoin-ui, then restarted it
4. POST -> 401, Allow-Origin origin-scoped
5. conf byte-identical to the pre-test known-good, container healthy
Both halves are now proven on real hardware: a05956c4's template (the
gate works) and f6b5245b's delivery path (the gate reaches a container
the reconciler had been skipping).
Also records the operator's decision AGAINST credential rotation — no
macaroon, no Bitcoin RPC password — with the trade it accepts stated
plainly, so it is not silently re-litigated later.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Names the four open items explicitly, including the one that is easy to
lose: the reconcile fix is deployed but unexercised, so the node's 401
proves the template and not the delivery path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The node is closed and verified 401 with origin-scoped CORS. But an
unrelated bitcoin-ui rebuild at 18:36 cleared the stale conf before the
reconcile fix was deployed at 19:06, so the 401 proves a05956c4's
template and NOT the delivery path f6b5245b adds.
Window 14 closed (exposure gone, verified). Window 15 opened for the
delivery path, which is deployed but never exercised — bitcoin-ui is
still in the uninstall marker, so this node depends on that untested
path the next time its config has to change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
system.stats on archi-dev-box returns host_secrets with verdict 'per-node'
and three evidence lines (machine-id anchor 2026-04-09; every SSH host key
and the TLS key newer than the anchor). Previously proven against the file
contract in unit tests only.
Honest limitation: this is one node, not the dev pair — archy-x250-dev has
been offline for two days, so the second node is unreachable, not skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verified live on archi-dev-box, code fix committed in f6b5245b but not
deployed. Deliberately logged as open rather than fixed: the defect that
matters to an operator is the running node, not the source tree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All five KEY-05 layers landed. Records the two things a reader would
otherwise get wrong:
- the 2 boot_reconciler test failures in the full-suite run are
parallel-load flakes (4/4 pass in isolation), not regressions;
- layer (b)'s clippy gate is LIVE but not yet EFFECTIVE, because 42
pre-existing warnings already fail the same -D warnings step.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SUMMARY for 10-04, plus three WINDOWS.md entries (11-13) so the unverified
items stay visible at ship time: the rotation never exercised on real
hardware, host_secrets never observed in a live system.stats, and the C-3
finding itself — three live nodes still on shared SSH host keys, two of them
also sharing a TLS private key, none of them rotated.
STATE.md and ROADMAP.md deliberately not touched: both carry other agents'
uncommitted work in this shared tree and the orchestrator owns them for this
wave.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deployed the frontend to archi-dev-box only (--frontend-only, no fleet,
no alpha-tester, no Tailscale, no OTA, no release) and proved all six
UIFIX fixes are in the bundle the node actually serves.
- Resolved the live chunk set from sw.js first: /opt/archipelago/web-ui/
assets keeps every prior deploy's hashed chunks, so a naive disk grep
returns hits from dead chunks and would have produced a false pass
(threat T-01-83, and it was a real trap here).
- Fetched each live chunk over HTTP from http://archi-dev-box and grepped
it: all eight probe strings for UIFIX-01..06 PRESENT.
- Real Chromium boot check on the node at 1440x900 and 390x740: app
mounts, 0 console errors, 0 page errors, 0 failed requests.
- archy-x250-dev recorded as an explicit gap: offline, last seen 2d ago,
no MagicDNS record; still has neither this plan set's nor phase 2's
frontend.
Task 2's six numbered checks are all recorded NOT VERIFIED. They need an
authenticated session on the node (UI returns 401 / redirects to /login,
and no credential was guessed against a node holding real funds), and two
of them are not testable as the node stands: it owns exactly one
purchased item (image/jpeg) and has zero video and zero audio content
anywhere, so the purchased-video, purchased-music and picture-in-picture
checks have nothing to open.
No source file modified, no fix applied inline, and STATE/ROADMAP/
REQUIREMENTS deliberately left untouched - UIFIX-01..06 are NOT closed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 1 done. Tasks 2 and 3 are blocked on unmet preconditions and were
NOT auto-approved: no fleet node runs 10-01's gate (installed binary
predates 879de59e and lacks the refusal string), and no second machine
was available to probe from. Nothing moved UNVERIFIED -> VERIFIED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Written by the previous session's executors for 01-17, 10-01, 10-03 and
10-05, all of which are complete and whose code is already committed. The
session was cut off by a dropped SSH connection before these were staged,
so they were sitting untracked. Recording them so the phase history is not
lost.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The on-disk format was detected by sniffing the first byte for `[` or `{`.
Encrypted blobs begin with a random 12-byte nonce, so roughly 1 in 128
saves produced a valid encrypted file whose first byte was 0x5B or 0x7B;
those were misread as plaintext JSON, failed `String::from_utf8`, and the
store became permanently unreadable. This was surfacing as a flaky
`test_list_credentials_no_filter`, but it is a real data-loss bug: a node
whose ciphertext happened to start with one of those bytes could not load
its credentials.
Writes now carry a fixed `ARCHYCRED1` marker, which cannot collide with a
random nonce, so detection of the current format is exact.
Legacy unmarked files are detected by SUCCESSFUL AEAD DECRYPTION rather
than by another byte sniff. A verifying Poly1305 tag under the node key is
a cryptographic discriminator (~2^-128 false-positive rate), strictly
stronger than any structural guess — which is why the deferred item's
suggested "keep the first-byte sniff as the legacy fallback" was not the
shape adopted. Plaintext JSON remains the last resort, and is still
reachable on a node that has no node key at all.
An undecodable file now errors instead of returning an empty store, so a
transiently unreadable file is never silently replaced by an empty one
that the next save would commit to disk (CLAUDE.md: migrations never
destroy data). Legacy files upgrade on write, never on read.
Tests drive the collision deterministically via an explicit nonce rather
than waiting on the 1-in-128 draw, and cover all three on-disk
populations, the read-path-does-not-rewrite guarantee, and tamper
rejection. 28 passed, 0 failed.
Closes the 10-01 deferred item.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Backend already exists: server.set-name runs hostnamectl set-hostname and
regenerates the TLS cert with a SAN for the new name. This is a UI step.
Records the hazard that decides the design: renaming changes both the mDNS
.local name and the TLS cert, so a rename mid-flow can drop the user's session
in the middle of onboarding — potentially between seed generation and seed
verification. Placement is therefore a design decision, with three options laid
out (last-before-Done, first, or collect-early-apply-late).
Also flags RFC-1123 slugification (users will type "Dorian's Node"), whether
the rename propagates to the Reticulum display name and mesh surfaces, the
reconnection UX, and whether the step is skippable.
Sequenced after the in-flight regenerate_tls_cert atomicity fix, since renaming
is exactly the path that fix protects.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
98 operational files still carry 146.59.87.168 — the domain was only adopted
for the git remote, not for container registry references. Bulk is app
manifests' image: lines, plus .gitmodules, both CI workflows, the signed
catalog.json, and two Android companion files with compiled constants. The 117
hits in .planning/ are historical records and stay.
Not a find-and-replace: the domain serves Gitea over HTTPS:443 while images are
pulled from :3000 over plain HTTP, and podman treats host:3000 and domain as
different registries — so every node re-pulls under the new name and any node
that can't resolve or trust the new host fails to pull. It also invalidates the
signed catalog (needs a re-sign ceremony) and the APK ships compiled constants.
Rollout order: registry serving on the domain → manifests → catalog re-sign →
APK rebuild. Steps 2-4 are actively breaking until step 1 holds.
Analysis from the concurrent agent's session before it ended; recorded so it is
not lost.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Companion build 0.5.27 (versionCode 47) shims navigator.clipboard natively, so
in-app copy/paste is fixed with zero web changes — but the contract must not be
clobbered (no unconditional re-define, no Object.freeze).
Still open web-side: main.ts's fake readText() makes SendBitcoinModal's Paste
button render and silently no-op in plain-HTTP browsers; 30 writeText call
sites across three inconsistent patterns, ~10 of which toast 'Copied!'
regardless of success; scanner prewarm/torch/constraints/no-reinit.
Also records three factual corrections to docs/qr-scanner-snappiness-handover.md
(ZXing not ML Kit; FORMAT_QR_CODE + KEEP_ONLY_LATEST already in place; do NOT
drop to 720p — 1080p is a deliberate 0.5.22 fix for dense bolt11 QRs).
Routed at Phase 11: the signed-PSBT paste affordance and the scanner items are
the same surface as WALLET-05.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>