Commit Graph
2371 Commits
Author SHA1 Message Date
archipelagoandClaude Opus 5 b8979f3687 feat(01-02): demo answers every mesh/federation RPC the UI calls (FED-04)
Demo images / Build & push demo images (push) Successful in 4m14s
Ten methods the UI calls had no case at all in mock-backend.js, so the demo
answered them with "Method not found" and the frontend swallowed it in a
try/catch — peer renaming, scheduling, clear-all, the assistant panel and two
federation actions were all silently inert on the demo.

Each new handler mirrors its daemon counterpart and cites the Rust source it
mirrors, per the house convention above mesh.transport-advice:
  mesh.contacts-list/-save   typed_messages.rs (contacts merged over peers by
                             pubkey_hex; absent params leave stored fields)
  mesh.clear-all             status.rs ({ status: "cleared" })
  mesh.schedule/list/cancel  assistant.rs + scheduler.rs ScheduledMessage
  mesh.assistant-status/-configure  assistant.rs (key-presence semantics)
  federation.cancel-request  handlers.rs (outbound+sent only, notify defaults
                             true) — plus an outbound seed request, since
                             without one the demo's cancel path was
                             unexercisable
  federation.notify-did-change      handlers.rs ({ notified, failed, results })

mesh.peers and mesh.contacts-list now share one DEMO_MESH_PEERS list so they
cannot disagree about who is on the mesh, and the peer with no pubkey_hex is
omitted from contacts exactly as the daemon omits it.

scripts/mock-rpc-parity.mjs cross-references UI call sites against mock cases
and then drives a live scripted RPC sequence against an ephemeral-port
instance (MOCK_BACKEND_PORT). It matches only `method: '<x>'`, NOT bare string
literals: Mesh.vue and Federation.vue use the same dotted names as
resource-cache keys, and matching those would report permanent phantom gaps.

Fail-first proof: disabling the mesh.clear-all case makes the harness exit 1
naming the gap; restored, it exits 0 twice in a row with no stray listener.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 06:25:59 -04:00
archipelagoandClaude Opus 5 579398981f feat(01-03): paid tick renders the branded screensaver ring (FED-06)
Demo images / Build & push demo images (push) Failing after 1m14s
Both paid-tick surfaces now show the EQ-segment ring instead of a CSS ripple
burst (send modal) and a plain circle (scan modal), via a new `badge` size
variant at 160px/192px with matching --viz-radius. A transform scale of the
compact variant was ruled out in 01-UI-SPEC.md because it would scale segment
stroke width and blur along with the geometry.

Both surfaces use the identical composition — a badge-sized relative container
with the checkmark core absolutely centred over the ring — so the two ticks
cannot drift apart visually.

ScreensaverRing also gains the prefers-reduced-motion guard it never had, on
the component rather than per call site, so the screensaver and
SystemDangerZone variants are covered too.

Also records live-browser verification for 01-13's scroll cue (three
viewports, plus a geometry probe of the hide condition).

Verified: 5 new tests; full suite 102 files / 822 tests green; npm run build
clean with viz-ring-badge present in the built bundle. The live visual
no-clipping observation Task 2 asks for is explicitly NOT done — recorded as
deferred to plan 01-07's consolidated sign-off.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 06:07:10 -04:00
archipelagoandClaude Opus 5 8b51b7e2dc fix(quick-260731-upz): make the master-seed RNG explicit (ARCHY-1 / F-02)
bip39::Mnemonic::generate(24) resolves through Mnemonic::generate_in to
&mut rand::thread_rng() INSIDE the bip39 crate (bip39-2.1.0/src/lib.rs:
311-313 -> :296-298 -> :267-283), so the entropy source behind Archipelago's
entire key hierarchy -- node Ed25519 did:key, node Nostr key, FIPS mesh key,
per-identity keys, the BIP-84 wallet, the LND aezeed entropy, and the fleet
release-root SIGNING key -- was chosen by a dependency default rather than
stated at the call site.

Not a vulnerability today: rand 0.8.5's thread_rng is a fork-protected
ChaCha12 CSPRNG seeded from getrandom(2). But it is precisely the structural
shape of the 2026-07-30 COLDCARD entropy defect (T1), where a refactor
silently rebound seed generation to a non-cryptographic PRNG with no compile
error and no test failure.

- New private helper generate_mnemonic_with<R: CryptoRng + RngCore> calls
  bip39's injectable generate_in_with; MasterSeed::generate passes OsRng
  explicitly, with the rationale pinned in a doc comment
- mnemonic_generation_uses_injected_rng: drives generation from a
  deterministic test RNG and asserts the result equals from_entropy(exactly
  the bytes that RNG emitted) -- direct proof the INJECTED rng is consumed --
  plus a known-answer pin and a determinism check. This test cannot be
  written against the previous code: there was no seam to inject through
- mnemonic_generation_is_256_bit: the OsRng path yields 24 words and two
  successive productions differ

No change to derivation paths, word count, the empty-BIP-39-passphrase
decision, or the at-rest encryption envelope.

Verified: CARGO_INCREMENTAL=0 cargo test -p archipelago seed:: -> 25 passed,
0 failed.

Full analysis: docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md (F-02, §4, §7).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 06:03:17 -04:00
archipelagoandClaude Opus 5 5ba80e49b7 docs(quick-260731-upz): entropy-audit remediation backlog + tracker items
- Adds F-13 (High) to the audit: the BIP-84 account PRIVATE key is imported
  into Bitcoin Core (bitcoin.rs:203 disable_private_keys=false, :229-231
  wpkh(xprv/...)), so the spending key is persisted outside the Argon2
  envelope in a wallet with an empty passphrase; the descriptors also carry
  no [fingerprint/derivation] key origin, so no hardware signer could ever
  use them. Found by tracing secret class (1) end-to-end
- Fills the Remediation Backlog: R-00..R-15, prioritised severity x effort,
  each with the finding it closes, files, effort, and hardware gating; plus
  an explicit "not implemented here, and why" section
- Records ARCHY-1 as APPLIED with the exact test evidence and an honest note
  that making the source explicit removes a future failure mode rather than
  repairing a past one
- Wires the resulting open items into docs/UNIFIED-TASK-TRACKER.md in its
  existing tier/checkbox format: Tier 0 (cargo audit/deny CI, ceremony
  mnemonic input, a five-item hygiene batch, secrets.rs OsRng), Tier 1 (ISO
  fail-open first-boot secrets, Argon2 vs ADR-005, the on-node checklist),
  Tier 2 (the Critical unauthenticated seed RPCs, PSBT Phase 1, PSBT phases
  2-7, seed-RPC transport confinement)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 06:02:31 -04:00
archipelagoandClaude Opus 5 5f366f7589 fix(01-13): on-brand scroll cue makes the onboarding tickbox findable (UIFIX-03)
Demo images / Build & push demo images (push) Successful in 4m25s
On a short viewport the seed-confirmation tickbox sits below the fold inside
the step's scrolling area while Continue stays pinned and disabled in the
fixed footer — onboarding reads as broken rather than incomplete.

The cue is a sticky-bottom scrim and glass pill inside the scroll region, and
its visibility comes from real geometry: scrollHeight vs clientHeight for
overflow, then a getBoundingClientRect comparison of the tickbox's bottom
against the container's. On a tall screen the element does not render at all,
so those screens are unchanged. Rects rather than offsetTop because offsetTop
is relative to the nearest positioned ancestor — here the outer card, not the
scroll container.

It is wayfinding only: activating it scrolls the tickbox into view and never
sets confirmed, focuses Continue, or auto-ticks, which a test pins.

Listener setup was moved onto both onMounted paths — the sessionStorage
restore path returned early, so a user navigating back would have had no cue.

Verified: 6 new tests plus the full frontend suite green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 05:54:40 -04:00
archipelagoandClaude Opus 5 46bf5a7870 fix(01-15): hand the video off to a custodial PiP session (UIFIX-05)
Entering picture-in-picture read as the lightbox being dismissed, and the
session died with it: both Teleport and KeepAlive move their subtree on
deactivation, which the PiP spec treats as removal.

MediaLightbox now listens for the video's own enterpictureinpicture event —
so PiP entered by the browser's native control behaves identically to the
toolbar button — and follows a fixed order: adopt, animate, then emit close.
Adopting first is what makes the element survive the unmount the emit
triggers; the invariant is documented in place so a refactor cannot reorder
it innocently.

The backdrop animates a handoff on the PiP path only, closing on transitionend
with a bounded 350ms fallback for browsers that skip the transition and for
the reduced-motion path where the duration is zero and the event never fires.

Verified: 5 new tests plus the full frontend suite green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 05:53:38 -04:00
archipelagoandClaude Opus 5 4265254700 fix(01-11): remove every shipped Fedimint gateway credential (FED-07)
Six code paths configured the Lightning gateway with a bcrypt hash committed
to this repository — and one deploy path with a plaintext password literal —
whenever the per-install secret was missing. Anyone holding a copy of the repo
held the admin credential for every gateway that ever took a fallback.

container::secrets now owns the credential end to end: ensure_gateway_credential
(idempotent, delegates to ensure_one's bcrypt arm) and gateway_bcrypt_hash,
which returns Err when the secret is missing/empty and when the stored value is
on the KNOWN_DEFAULT_GATEWAY_HASHES denylist — so this codebase cannot hand
back the compromised value even to a node already carrying it.

get_app_config was widened to Result so a credential-less install cannot reach
podman run at all; configure_fedimint_lnd takes the resolved hash instead of
re-reading with its own fallback. The four shell paths stop generating
credentials entirely (dropping the htpasswd host dependency) and skip container
creation with a printed reason rather than substituting anything.

Naming converges on the manifest's fedimint-gateway-hash/.pw, with legacy
fedimint-gateway-password values copied forward rather than regenerated so no
node loses a working unique credential. Plan 01-16 owns rotation of installs
already carrying the default.

Verified: cargo build clean; cargo test -p archipelago 999 passed (2
boot_reconciler timing tests failed under concurrent load, green in isolation,
untouched by this diff); bash -n clean on all five scripts; the compromised
literal now appears exactly once in the tree, as the denylist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 05:46:02 -04:00
archipelagoandClaude Opus 5 5faf1a3c5f docs(quick-260731-upz): PSBT-first signing architecture spec
Watch-only descriptor wallets, external signers, multisig, air-gap transport
and honest LND limits — a spec a future /gsd-plan-phase can consume.

- Names the current gap: bitcoin.rs:203 passes disable_private_keys=false and
  bitcoin.rs:229-231 imports wpkh(xprv/...), so Core holds the BIP-84 account
  PRIVATE key today. Closing that is Phase 1 and unblocks everything else
- Full Core RPC loop with wallet- vs node-scoped RPCs; analyzepsbt drives UI
- Tier 1 single-sig with mandatory [fingerprint/derivation] key origin; Tier 2
  wsh(sortedmulti) on BIP-48; taproot/MuSig2 deferred as UNVERIFIED
- BC-UR v2 primary (fountain-coded, degrades gracefully), BBQr for Coldcard,
  file fallback always; animated multi-frame is mandatory, not optional
- LND: channel/revocation/HTLC keys CANNOT be air-gapped; funding tx must
  NEVER be self-broadcast (type-level refusal, not a boolean)
- On-chain (PSBT-protectable) vs lightning (necessarily hot) split, with the
  exact user-facing sentence the UI must use
- Hot wallet kept as explicitly-secondary with server-enforced limits; safe
  path is the DEFAULT, per T1's survivors
- Migration section refuses to over-alarm: the audit found no entropy defect,
  so no Archipelago user needs to rotate a seed
- 7 phases with dependencies, candidate requirements, and hardware gating
- Answers two open items in docs/hardware-signer-design.md
- Flags bitcoin-knots:latest as an unpinned tag vs ADR-009

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 23:09:17 -04:00
archipelagoandClaude Opus 5 f11db4ea1d docs(quick-260731-upz): entropy & seed-generation security audit
Evidence-backed audit of every secret class against the real tree, prompted
by the 2026-07-30 Coinkite COLDCARD entropy incident.

- No Coldcard-class entropy defect exists: no non-cryptographic PRNG, no
  clock-seeded key, no Math.random() in any browser key path
- F-01 (Critical, NOT entropy): seed.generate/seed.restore are unauthenticated,
  unrated, and unconditionally overwrite a live node's Ed25519/Nostr/FIPS keys
- F-02 [ARCHY-1] CONFIRMED: mnemonic entropy source is a bip39 transitive
  default, not a call-site argument — the exact structural shape of T1
- F-03 (High) [ARCHY-3]: first-boot TLS/SSH regeneration is fail-open and its
  completion marker is set even on failure, over a fleet-shared cached rootfs
- ARCHY-2 confirmed good; ARCHY-5 refuted as a present defect (32 | 256)
- Argon2::default() is 19MiB/t=2, not ADR-005's stated 64MB/3
- Corrects the scoping assumption that image-recipe/_archived/ is dead: it is
  the live ISO builder, exec'd by build-debian-iso.sh
- Adds a "What we do right" section and an UNVERIFIED on-node checklist

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 23:03:26 -04:00
archipelago cc52719e81 docs(01-01): complete federation node-store lock plan 2026-07-31 22:29:31 -04:00
archipelagoandClaude Opus 5 4a8925f09e fix(01-14): satisfy vue-tsc strict-null checks in usePaidItemViewer.test.ts
Demo images / Build & push demo images (push) Successful in 3m41s
npm run build's vue-tsc -b pass caught TS2532 (possibly-undefined array
access) on two array-index reads the vitest run alone doesn't type-check —
optional-chain them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:29:28 -04:00
archipelagoandClaude Opus 5 0c63a8518c docs(quick-260731-upz): plan entropy/seed audit + PSBT signing architecture
Following the confirmed 2026-07-30 Coinkite COLDCARD low-entropy incident,
plan three deliverables as a single 3-task quick plan:

1. docs/security/ENTROPY-SEED-AUDIT-2026-07-31.md — evidence-backed audit of
   every key-material path (Rust/TS/shell), adjudicating research findings
   [ARCHY-1]..[ARCHY-4] with file:line evidence, incl. the one-ISO-many-nodes
   correlation risk and an explicit UNVERIFIED on-node checklist.
2. docs/security/PSBT-SIGNING-ARCHITECTURE.md — descriptor watch-only,
   wsh(sortedmulti) multisig, air-gap transport, honest LND limits
   (channel/revocation/HTLC keys cannot be air-gapped), hot wallet as
   explicitly secondary, migration path, phased rollout.
3. Remediation backlog into docs/UNIFIED-TASK-TRACKER.md + one gated
   hardening fix (explicit-OsRng injection at the mnemonic call site) proven
   by a known-answer test that cannot exist before the change.

Audit-and-spec only — no wallet/signing implementation. Verify gates enforce
file:line evidence density and a secret-shaped-string check on both docs, and
assert no commit authored by this plan touches the concurrent agent's files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:29:23 -04:00
archipelagoandClaude Opus 5 b5628d968e fix(web5): raise connected-nodes row-breakpoint height floor to 40rem (UIFIX-02)
Demo images / Build & push demo images (push) Has been cancelled
Dorian verified the sibling-height match, internal scroll, and unchanged
stacked layout on his running dev session — all correct. The only issue was
the xl:min-h-[20rem] floor (an unmeasured judgement call, flagged as such in
01-12-PLAN.md): when node discovery is disabled, Web5NodeVisibility renders
short, the floor takes over, and 20rem left the Connected Nodes card looking
stunted. Doubled to xl:min-h-[40rem] per his direct instruction ("twice as
tall"). Test updated to pin the new value.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:27:44 -04:00
archipelagoandClaude Opus 5 4b5367ebc4 fix(01-01): route remaining federation mutators through the store lock (FED-01)
Task 2 of 01-01-PLAN.md, closing the gap left after Task 1's initial commit
(2f99db5e):

- record_peer_transport and update_node now hold FEDERATION_STORE_LOCK for
  their whole load-mutate-save cycle via the *_inner variants, instead of
  calling the public (separately-locked) load_nodes/save_nodes — closing
  the same class of race the lock was introduced to fix, just for the two
  mutators Task 1 didn't reach.
- Add test_remove_errors_when_tombstone_write_fails: pre-creates the
  removed-nodes path as a directory so the tombstone write fails, then
  asserts remove_node returns Err AND load_nodes still contains the node —
  proving a failed removal never half-applies.

cargo test -p archipelago federation::storage: 14/14 green (was 11, +3 across
Task 1/2). cargo build -p archipelago: no new warnings, no dead-code warnings
on any *_inner fn. Public signatures unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:26:28 -04:00
archipelagoandClaude Opus 5 bc9a210c75 fix(01-14): route Paid Files pictures/videos into the app lightbox with a visible wait (UIFIX-04/UIFIX-06)
Demo images / Build & push demo images (push) Successful in 3m47s
Cloud.vue's viewPaidItem() called window.open() instead of the in-app
MediaLightbox, and its content.owned-get fetch had a 60s timeout with no
loading indicator and a swallowed catch. Moves the fetch/decode/route logic
into a new usePaidItemViewer composable: image/video route to a second
MediaLightbox instance fed a synthetic FileBrowserItem, audio still goes to
the global bottom-bar player, and anything with no in-app viewer keeps
today's browser-tab fallback. The Paid Files row now shows an "Opening…"
spinner (matching PeerFiles' existing treatment) for the fetch's duration,
becomes non-interactive to prevent double-fetch, and a real error surfaces
through the view's existing alert-error block instead of an empty catch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:17:57 -04:00
archipelagoandClaude Opus 5 3288a02df8 feat(01-15): singleton PiP session with body-level custodial host
Demo images / Build & push demo images (push) Successful in 3m44s
usePipSession() owns an off-screen div under document.body; adopt(video)
moves the element there so it survives the unmount of whatever view
rendered it (a Teleport and a KeepAlive'd view both move their subtree on
deactivation, which the picture-in-picture spec treats as removal).
release() tears down playback and detaches the element; a
leavepictureinpicture listener on the adopted element is the primary
release path so an orphaned owner can never leak it.

Also adds isPipSupported() to pip.ts — a call-time version of the existing
import-time pipSupported const, needed because a test can't restub
document.pictureInPictureEnabled after import. togglePip and pipSupported
are unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:11:56 -04:00
archipelagoandClaude Opus 5 ceafbcb596 fix(web5): bound connected-nodes tab panes to sibling-matched height (UIFIX-02)
Demo images / Build & push demo images (push) Successful in 3m58s
The three tab panes carried max-h-72 xl:max-h-none, so at the xl breakpoint
(where the Web5 row becomes two grid columns) the cap lifted with nothing to
replace it: the visible pane grew to fit every row, stretched the grid row,
and the scrollbar the user expects never appeared.

Give each pane xl:flex-1 xl:basis-0 xl:max-h-none instead — zero flex-basis
means the pane contributes no intrinsic height, so the grid row is sized by
the Web5NodeVisibility sibling alone, grid's default align-items: stretch
gives the card that height, and flex-1 hands the leftover height back to the
pane, which scrolls inside it via the existing overflow-y-auto. The card root
gets min-h-0 (so the flex column can shrink below content height) plus an
xl:min-h-[20rem] floor so a short sibling still leaves a usable list area
instead of collapsing to the header+tabs strip.

Below the row breakpoint nothing changes: the stacked cap (max-h-72) and
scroll are untouched, and dropping flex-auto (replaced by nothing, i.e. the
default 0 1 auto) has no visible effect since single-column stacked cards
have no extra flex space to distribute anyway.

Adds Web5ConnectedNodesScroll.test.ts to pin the contract across all three
tab panes and the card root so a future cleanup cannot reintroduce the
grow-to-fit regression a third time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 21:38:34 -04:00
archipelagoandClaude Opus 5 bf9cfc446a docs: capture ISO build handoff for v1.7.119-alpha
Release and OTA assets are live; the installer ISO was blocked three times by
a dirty shared tree. Records the exact command, TMPDIR requirement, the
background-execution lesson, the build-from-HEAD decision and its reasoning,
and which gate stages were already observed passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 21:13:51 -04:00
archipelagoandClaude Opus 5 d02ba1aa7b chore(catalog): sync catalogs to BotFights 1.2.11
Demo images / Build & push demo images (push) Successful in 3m39s
apps/botfights/manifest.yml went to 1.2.11 in aea17248, but the two unsigned
catalogs (app-catalog/catalog.json and its neode-ui/public copy) still
advertised 1.2.9, failing the release gate's catalog-drift check and blocking
the ISO build. releases/app-catalog.json was already correct and signed.

Regenerated via scripts/generate-app-catalog.py (syncs from manifests, no key
needed). app_ports.rs was rewritten by the same generator; verified the port
set is byte-identical in content (35 ports, none added or removed) and
re-normalised with cargo fmt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 20:07:44 -04:00
archipelagoandClaude b0a08345c5 chore(catalog): sign catalog with botfights 1.2.11
Signs releases/app-catalog.json after aea17248 (BotFights 1.2.11 —
6 IDOR/auth fixes + winnings-claim UI + existing-bot AI-config UI).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 19:56:01 -04:00
archipelagoandClaude aea17248f6 feat(botfights): bump to 1.2.11 — closes 6 IDOR/auth bugs + winnings-claim UI
Ships botfight repo commits f5f57e6, c162d5e, 41f1b93, 10d4209:

- Fixed 6 instances of the same trust-a-client-supplied-pubkey pattern
  across auth/payments/queue routes, two of them critical: GET
  /winnings/:botId had no auth at all and leaked live spendable Cashu
  bearer tokens; POST /connect-wallet let anyone redirect a victim
  bot's future payouts to an attacker's wallet by pubkey (public by
  design in nostr).
- Added owner-reachable AI-answer settings (existing bots, not just at
  creation) and a "claim your winnings" UI (Cashu payouts were minted
  server-side but had no frontend consumer at all until now).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 19:48:06 -04:00
archipelagoandClaude Opus 5 6c2b6668dc fix(iso): honor TMPDIR for the QEMU boot-test disk/serial log
Both the 20G sparse qcow2 test disk and the serial console log were
hardcoded to /tmp regardless of $TMPDIR, so pointing the ISO release
build at a disk-backed scratch dir (to avoid tmpfs space pressure)
would not have covered this stage. Falls back to /tmp when TMPDIR is
unset — no behavior change for existing callers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:46:12 -04:00
archipelagoandClaude Opus 5 c4e1375c81 chore(release): sign v1.7.119-alpha manifest
Demo images / Build & push demo images (push) Successful in 3m46s
Signed by Dorian via scripts/sign-manifest.sh; signature verified
against the pinned release-root did:key by the signing script itself
and independently re-checked here (check-release-manifest.sh: version,
changelog line count, and both components' sha256/size all match the
on-disk artifacts).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
v1.7.119-alpha
2026-07-31 18:56:30 -04:00
archipelagoandClaude Opus 5 258a91781c fix(release): correct v1.7.119-alpha changelog/manifest lifecycle-gate note
create-release-manifest.sh's changelog extraction pulls every non-blank
line between the version header and the next "## ", not just "- "
bullets — so the previous commit's "### Known gap" markdown heading and
its paragraph leaked into releases/manifest.json (and, via
sync-whats-new.py, the Settings "What's New" modal) as a malformed,
truncated entry (the closing clarification sentence was cut by the
extractor's 10-line cap).

Rewritten as a single "- " bullet, matching every other CHANGELOG entry,
so it renders cleanly and completely in both the OTA manifest and the
in-app modal instead of showing raw "### " syntax to node operators.
Also folds in core/Cargo.lock's version bump, which create-release.sh's
own commit step omits from its `git add` list.

Same binary/frontend artifacts as the prior commit (identical sha256/
size in the regenerated manifest) — only the changelog text changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 13:50:34 -04:00
archipelago baaa4e8ea1 chore: release v1.7.119-alpha 2026-07-31 13:13:53 -04:00
archipelagoandClaude Opus 5 af1af8266c docs: sync What's New modal for v1.7.119-alpha; drop markdown emphasis from CHANGELOG
scripts/sync-whats-new.py --check (part of the release gate) requires
every CHANGELOG version to have a matching block in the Settings
"What's New" modal. Also strips CHANGELOG markdown bold/italic markup
from the v1.7.119-alpha bullets first — the modal renderer only
strips backticks, not **/* emphasis, so it would have leaked literal
asterisks into the user-facing modal text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 12:17:29 -04:00
archipelagoandClaude Opus 5 37d293be59 style: cargo fmt — fix formatting drift blocking the release gate
Whitespace-only reflow in storage.rs/seed.rs/update.rs (rustfmt line-
wrapping rules) and app_ports.rs (array literal reflow after the port
list grew). No logic change. tests/release/run.sh's cargo-fmt --check
stage was failing on this before v1.7.119-alpha could be cut.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 12:15:19 -04:00
archipelagoandClaude Opus 5 09ab5f6b11 docs: curate CHANGELOG entry for v1.7.119-alpha
Written ahead of create-release.sh, which validates a curated
CHANGELOG.md section already exists rather than generating one.
Leads with FED-08 (private-channel invoice route hints) and FED-09
(doctor/Tor setgid restart-loop fix), covers phase-02 sessionStorage
security hardening and the PWA auto-update decision, and records the
5x lifecycle gate omission (node .228 unreachable) as a known gap
rather than a footnote.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 12:13:51 -04:00
archipelagoandClaude Opus 5 98d6534e13 chore: gitignore neode-ui/.vite build cache
Untracked Vite cache dir was tripping build-iso-release.sh's
git-status-porcelain clean-tree preflight check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 12:10:10 -04:00
archipelagoandClaude Opus 5 0400d07c26 chore(deps): commit package-lock peer-flag churn to clear release preflight
Demo images / Build & push demo images (push) Successful in 3m49s
Pure npm metadata: 'peer': true flags dropped by a differing npm version.
No dependency added, removed, or version-changed. Committed rather than
reverted so nothing another session did is discarded; the ISO release
preflight requires a clean tree on main.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 12:07:59 -04:00
archipelagoandClaude Opus 5 215e13bff6 docs(phase-02): complete phase execution
Phase 02 (ui-performance) verified passed. 12 plans (8 planned + 4 gap
closure). PERF-01/02/03 all met. Two residuals formally overridden by the
user: Discover's entrance-animation replay (animations are his domain) and
OpenWrtGateway (no device connected, unmeasurable).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:58:35 -04:00
archipelagoandClaude Opus 5 e42f73a3b6 docs(02): final verification status
Reassessed after the OpenWrtGateway override (commit b4350e24) closed the
single open item from the prior human_needed pass. Judged the override on
its merits: well-formed, corroborated by a direct first-person quote from
Dorian this session, with one noted imprecision (the rationale slightly
overstates that no measurement at all is obtainable, when the
disconnected-state UI could technically still be re-measured) that doesn't
change the substance of a legitimate stakeholder scope call. With both
residual, non-poller-fixable costs (Discover's animation replay,
OpenWrtGateway's untestable hardware dependency) now individually accepted
by rationale-backed override, and every other named regression fixed or
substantially recovered, status is passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:57:27 -04:00
archipelagoandClaude Opus 5 b4350e244f docs(02): record user override for unmeasurable OpenWrtGateway surface
No OpenWrt device is connected to this node, so the surface cannot be
exercised and no post-fix measurement is obtainable. The user decided to pass
it for this milestone. Also notes that its earlier baseline/regressed figures
measured a disconnected-device UI rather than the real screen, and records the
residual risk for the next milestone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:42:35 -04:00
archipelagoandClaude Opus 5 8569f5376a docs(02): final verification
Independently re-derived all 02-11/02-12 claims: recomputed every claimed
median directly from 02-PERF-FINAL.json's raw samples (all match), confirmed
the frozen perf harness is still byte-for-byte untouched, re-ran the
keepAliveLifecycle regression suite (19/19) and the full workspace suite
(788/788), and confirmed the three leaked-poller fixes are genuinely wired
in source. Discover's override is well-formed and corroborated across three
independent artifacts. Status: human_needed — OpenWrtGateway is the one
named regression with zero post-fix measurement (crashed for an unrelated
reason) and no override scoped to it; everything else is either verified
fixed, substantially recovered with an honest residual, or formally
overridden by Dorian.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:32:54 -04:00
archipelagoandClaude Opus 5 813d079b9b docs(02-12): mark plan CANCELLED per Dorian's decision — no animation changes
Dorian decided entrance-animation behavior is his domain and is not to be
changed, cancelling this gap-closure plan before any source file shipped.
The composable drafted during investigation was deleted (never imported).
Preserves the investigation findings (blast radius, the ~241ms-1716ms
measured cascade, the first-paint-901ms-of-1095ms evidence, the Apps-revisit
184-267ms calibration, and the pre-phase-2-baseline note) so the analysis
isn't lost or re-done. Discover's revisit cost stands as a formally accepted
deviation at its 02-11 FINAL number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:16:43 -04:00
archipelagoandClaude ed34e41f98 chore(catalog): sign catalog with botfights data_uid=999 fix
Signs releases/app-catalog.json after the corrected manifest fix in
3c7a1fbb (data_uid: 999:999, matching the image's actual internal UID).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 10:54:41 -04:00
archipelagoandClaude Opus 5 991e9b5e4c docs(02): record Dorian's formal override for Discover's residual revisit cost
The re-verification required either a fix or an accountable stakeholder
decision. Plan 02-11 fixed four of five measurable surfaces. 02-12 isolated
Discover's remainder to the card-stagger entrance animation replaying on every
revisit; the only fix changes animation behavior, which Dorian rules off-limits
as his domain. Decision recorded with rationale, evidence and attribution.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 10:53:39 -04:00
archipelagoandClaude 3c7a1fbbb5 fix(botfights): correct data_uid to 999 (actual image UID, not 1001)
The prior fix (5745db51) copied the fedimint-clientd/barkd data_uid
pattern (1001) without verifying it against this image. Live on
x250-beta the container still crash-looped with the same SqliteError:
unable to open database file — `podman exec botfights id` showed
uid=999(botfights) gid=999(botfights), not 1001. The image's
Dockerfile does `useradd --system` with no explicit UID, which lands
at 999, and security.user in the manifest is descriptive only — it is
not read by this app's (non-Quadlet) install path, so it can't be used
as the source of truth either.

Fixed data_uid to 999:999 and corrected security.user to 999 to match
reality. Verified live: manually re-chowned the existing bind mount to
999:999 on x250-beta, restarted the container, confirmed
`database migrated` + `listening on http://localhost:9100` +
`/api/health` returns ok, and confirmed /api/bots on x250-beta returns
identical data to the canonical arena (arena-proxy forwarding
correctly). Regenerated catalog; cargo test -p archipelago-container
manifest passes (38/38).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 10:49:58 -04:00
archipelagoandClaude 96caa6e5e9 chore(catalog): sign catalog with botfights bind-mount/data_uid fix
Signs releases/app-catalog.json after the manifest fix in 5745db51
(absolute bind-mount path + data_uid: "1001:1001" for botfights).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 10:37:07 -04:00
archipelagoandClaude 5745db51ae fix(botfights): absolute bind-mount path + data_uid for fresh installs
Two root-caused bugs found live during a fresh install on a second node
(x250-beta) that never surfaced on archi-dev-box by accident of that
node's prior state:

- volumes.source was a bare relative "botfights-data" instead of an
  absolute host path, inconsistent with every other app's manifest.
  Resolved to /var/lib/archipelago/botfights on archi-dev-box but to
  /home/archipelago/botfights-data on x250-beta, which doesn't exist
  there. Fixed to the absolute path, matching netbird-server and every
  other app's convention.

- data_uid was missing entirely. The container runs as internal UID
  1001 (security.user), but without data_uid the orchestrator's bind-dir
  ownership fixup only fires via a same-owner-as-anchor fallback that
  assumes no-data_uid apps run as container-internal root. Root cause of
  a real SqliteError: unable to open database file crash-loop on
  startup. Fixed by adding data_uid: "1001:1001", same pattern as
  fedimint-clientd and barkd.

Bumped catalog via generate-app-catalog.sh; cargo test -p
archipelago-container manifest passes (38/38).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-31 10:35:53 -04:00
archipelagoandClaude Fable 5 1b8eaefbd0 chore(catalog): sign app-catalog — BotFights 1.2.9, Cashu primary UX + anonymous-bot staking
releases/app-catalog.json regenerated and signed by the release-root key
(did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur). botfights is
the only content change vs the previously-published catalog (verified
structurally — all other 65 apps unchanged): 1.2.8 -> 1.2.9.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 10:19:23 -04:00
archipelagoandClaude Fable 5 71f2f22f04 feat(botfights): 1.2.9 — Cashu primary entry-fee UX, anonymous-bot ranked staking
Demo images / Build & push demo images (push) Successful in 4m21s
Bumps to botfights:1.2.9, which carries: Cashu token payment wired as the
primary entry-fee UX in WalletConnect.vue (was built server-side already
but never called from any UI), Lightning/NWC demoted to secondary, and a
fix so anonymous poll-mode bots (not just nostr-authenticated humans) can
use ranked/staked fights — join-ranked previously required a pubkey
unconditionally, silently locking out the entire AI-agent audience.

No archy-side manifest changes beyond the version/image bump.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 10:16:37 -04:00
archipelagoandClaude Opus 5 59798ebff5 docs(02-12): plan the KeepAlive entrance-animation-replay gap closure
02-11 named a second, distinct cause of Discover's revisit slowness with
full profiling evidence (card-stagger/home-card-animate classes never
removed from the DOM, so Chromium restarts the CSS entrance animation on
every KeepAlive reattach) but deliberately left it unfixed — the blast
radius (Discover/Apps/Marketplace/Home + Web5 sub-cards) exceeded that
plan's scope. This gap plan fixes every affected site with one shared
composable rather than per-file patches, decouples Home.vue's dual-purpose
animateCards ref so its overlay/EasyHome visibility logic stays untouched,
and ends at a blocking human-verify checkpoint before any SUMMARY is written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:58:40 -04:00
archipelagoandClaude Opus 5 4435f95ef3 fix(01-20): stop doctor from fighting Tor over the setgid bit
fix_tor_permissions() exact-matched stat's output against the literal
string "700", but stat -c '%a' omits leading zeros so Tor's own
setgid HiddenServiceDir mode (2700) never matched. Every ~5-minute
doctor run "fixed" it back to 700, restarted Tor, and Tor immediately
set 2700 again — so Tor never survived long enough to build a usable
consensus/HSDir cache, breaking the mesh's Tor fallback entirely.

- Compare only the last 3 mode digits (owner/group/other), which is
  the property that actually matters, so 700 and 2700 both pass while
  750/707/2755 etc. are still corrected.
- Add a 30-minute restart backoff (timestamp file under
  /var/lib/archipelago/) so no future condition can reproduce a
  restart storm even if the fix fires repeatedly.
- Log clearly in both directions: a debug-level no-op line when a
  directory is already correct, and an explicit "was NOT fully
  denied" line when a real fix is applied, plus a line when a restart
  is skipped by the backoff.

Verified statically against temp directories (2700 accepted with 0
restarts; 750/707/2755 corrected with exactly 1 restart; a second
real fix inside the backoff window logs a skip instead of
restarting). No live Tor/doctor/systemd unit was touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:45:55 -04:00
archipelagoandClaude Opus 5 56cc94237b docs(02-review): clarify PWA auto-update provenance in the record
The code-fixer correctly declined this change twice on relayed consent. It was
then made by the orchestrator holding first-hand authorization from Dorian, who
chose forced auto-update with the mid-payment reload risk explicitly stated and
rejected warning UI. Records the quote, and that the implementation reused the
existing kiosk auto-apply path (preserving the cinematic and first-install
guards) rather than flipping build-time skipWaiting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:41:59 -04:00
archipelagoandClaude Opus 5 f514ab515b docs(02-11): complete plan — leaked-poller fix, four-way verdicts, SUMMARY
Phase 02's last open item closed: web5/server/fleet fixed and proven
on archi-dev-box; app-details restored to at/near baseline;
discover's second cause (CSS entrance-animation replay on KeepAlive
reactivation) named and evidenced but not fixed, scoped as a
follow-up; openwrt-gateway not measurable this pass, prior numbers
flagged not retracted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:37:54 -04:00
archipelagoandClaude Opus 5 68d63b1140 docs(01): add FED-09 — doctor's Tor restart loop breaks mesh Tor fallback
container-doctor.sh's fix_tor_permissions() exact-matches mode '700', but Tor
sets its hidden-service dirs to 2700 (setgid). Every 5-minute doctor run
'fixes' 2700->700 and restarts tor@default; Tor resets it and the cycle
repeats, so Tor never builds a usable consensus/HSDir cache. Result on a live
node: 'No more HSDir available to query', onion peers unresolvable, and with
the FIPS direct path also timing out, mesh sends failed entirely.

Planned as 01-20 in wave 1 so it ships in the same release. FIPS direct
connect_fail is a separate concern, handed to FED-03's transport review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:37:41 -04:00
archipelagoandClaude Fable 5 9ad31a7495 docs(09): record demo-day deviations + Cashu fixed-stake feature scoping
Records everything found/fixed live during 09-07's blocking human-verify
checkpoints today (botfights 1.2.2-1.2.8): iframe embed, native signer
bridge, mode-picker discoverability, proxy-URL leaks, round-jump backfill,
broken profile images (CSP), AI-answer discoverability, and the webhook_test
signature exception. Also records the full threat register worked through
for the user-directed Cashu fixed-stake entry-fee request (21 sats, winner
takes all) — mint configured and verified live (Minibits), actual
token-accepting implementation deliberately NOT done yet, scoped as a
follow-up.

Note: this section was written once already earlier in the session and
appears to have been lost to a shared-tree overwrite before it was
committed (CLAUDE.md "Concurrent agent in shared tree" hazard) — recreated
and committed immediately this time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 09:37:40 -04:00
archipelagoandClaude Opus 5 48a2ff7c16 docs(02-11): four-way re-measure, final per-surface verdicts, second Discover cause
Deployed the poller fix to archi-dev-box and re-ran the frozen harness
(02-PERF-FINAL.json, 5 runs/surface). Web5 fixed (275ms, below both
its 566ms pre-phase-2 baseline and the 300ms pass bar). Server's
regression closed (574ms, below 738ms baseline) though not yet under
300ms. Fleet substantially improved (790ms, down from a 2631ms
regression). AppDetails restored to at/near its own baseline.

Investigated the two open items the coordinator raised:
- OpenWrtGateway: this run's 5/5 samples failed with a Chromium
  "Target crashed" error cascading from an unrelated surface earlier
  in the same harness run — recorded as not-measurable, not written
  in as data. Separately confirmed via source (OpenWrtGateway.vue's
  h1 renders unconditionally, and a "No router configured" RPC error
  deterministically shows a real Connect-to-Router form) that the
  prior baseline/after/remeasure numbers were measuring a genuine,
  substantive disconnected-state UI render, not an empty/error page —
  so the "six confirmed regressions" count is not retracted, but the
  numbers are flagged as reflecting one specific code branch.
- Discover (1389ms, worst remaining, least improved): profiled
  directly and found a SECOND, distinct, phase-2-class cause —
  showStagger/card-stagger entrance-animation classes are baked into
  the DOM at first mount and never programmatically removed (the flag
  is a correctly-scoped once-per-session const, but nothing ever
  re-renders to strip the class), so every KeepAlive detach/reattach
  cycle restarts the CSS animation on reactivation, replaying the full
  entrance cascade on every revisit. Confirmed via a diagnostic
  showing DOM card count doubling transiently on every revisit and an
  extended animationstart/animationend event log. Not fixed this pass
  — the safe fix's blast radius spans 5+ files outside this plan's
  scope (Apps.vue, Marketplace.vue, Home.vue, several Web5 sub-cards)
  and needs its own real-device verification budget, matching the
  precedent 02-02's original KeepAlive rollout needed for this exact
  class of change. Named and evidenced, recommended as a dedicated
  follow-up rather than expanded into this plan under time pressure.

REQUIREMENTS.md's PERF-02/PERF-03 rows updated to the final state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:32:19 -04:00
archipelagoandClaude Fable 5 1216198992 chore(catalog): sign app-catalog — BotFights 1.2.8, CSP img-src + AI-answer discoverability
releases/app-catalog.json regenerated and signed by the release-root key
(did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur). botfights is
the only content change vs the previously-published catalog (verified
structurally — all other 65 apps unchanged): 1.2.7 -> 1.2.8, fixes broken
profile pictures (CSP img-src) and makes the AI-answer feature discoverable
by default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 09:06:47 -04:00