Skip to content

feat: B2 wallet device registry (stateless, encrypted, blind relay)#48

Merged
frahlg merged 6 commits into
mainfrom
b2-device-registry
Jun 14, 2026
Merged

feat: B2 wallet device registry (stateless, encrypted, blind relay)#48
frahlg merged 6 commits into
mainfrom
b2-device-registry

Conversation

@frahlg

@frahlg frahlg commented Jun 14, 2026

Copy link
Copy Markdown
Member

Implements docs/superpowers/specs/2026-06-14-b2-device-registry-design.md. Your machines
appear everywhere by name, attachable with no add-machine and no SAS — the payoff
of the wallet identity, and it kills the re-pair-every-machine friction.

How it works

Each mir up (with your wallet) seals an encrypted record {name, host_pub, signal_url, ts}
under K_reg = HKDF(wallet_secret, "miranda/registry/v1") and attaches it to its live
/agent/signal registration. The relay holds it in-memory (no DB, no disk) and serves
GET /registry?wallet=W → [{machine_id, blob}]. Clients + browser fetch, AEAD-open (forgeries
self-drop), and auto-list. Discovery only — B1.4 acceptance + offline LAN-direct unchanged.

Stays true to the thesis (your decisions)

  • Encrypted, relay fully blind — it sees opaque blobs only.
  • Relay STAYS STATELESS — soft-state riding the live registration, rebuilt by the devices
    on reconnect (like today's live registrations). A fresh relay is empty.
  • AEAD = encryption AND authenticity for free — ChaCha20-Poly1305 with machine_id as
    AAD: a forged blob fails to open → dropped; the relay verifies/decrypts/persists nothing.
  • Zero-touch + notify — a wallet-holding mir up self-publishes and works immediately;
    a newly-seen machine prints "📣 new device joined".

Slices (all TDD)

  • B2.0 crypto: RegistryKey + ChaCha20-Poly1305 seal/open, Go+JS, testdata/registry-vector.json
    (cross-checked vs Python cryptography — 3 independent impls agree).
  • B2.1 relay: blob on the live agentConn + GET /registry (blind, stateless, -race clean).
  • B2.2 agent: mir up auto-pins its own wallet (own devices need no pairing) + publishes.
  • B2.3 client: mir list/attach fetch+decrypt+merge (local wins) + notify.
  • B2.4 browser: auto-list your machines by name + notify (verified in-browser: boots clean,
    list renders, registry fetch best-effort).
  • B2.5 e2e contract test (agent seal → relay verbatim → fetch decode, forgery dropped, fresh
    relay empty) + README/SECURITY.

Trade-off (accepted)

Online-only discovery — a powered-off machine reappears when it reconnects. Revocation = power
off, or rotate the wallet for a leaked phrase. pair/add-machine kept for cross-wallet.

Your hand

Deploy of the new mir-signal (adds /registry + the opaque Registry passthrough —
additive, backward-compatible: old clients ignore it) is live-infra, health-gated.

🤖 Generated with Claude Code

frahlg and others added 6 commits June 14, 2026 10:21
…n (Go+JS+vector)

K_reg = HKDF-SHA256(wallet_secret, salt 'miranda/registry/v1'); records are
ChaCha20-Poly1305 sealed with machine_id as AAD (encryption AND authenticity:
a forged blob fails to open). Byte-identical Go<->JS, gated by
testdata/registry-vector.json (cross-checked vs Python cryptography).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…+ GET /registry)

Agents publish an opaque encrypted device blob as the first message on their
live /agent/signal registration; the relay holds it in-memory on the agentConn
(no persistence) and serves GET /registry?wallet=W -> [{machine_id, blob}] for
the live agents under W. It never decrypts, verifies, or persists — blind and
stateless; a relay restart loses it and agents re-publish on reconnect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… registry record

A wallet-rooted mir up auto-pins its own wallet (your own devices need no
pairing) and publishes a ChaCha20-Poly1305-sealed {name,host_pub,signal_url,ts}
record on its live /agent/signal registration, re-published on each reconnect.
Legacy (wallet-less) mir up is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…chines + notify

mir list and mir attach <name> fetch the relay's encrypted registry, decrypt
with the wallet (forgeries dropped), and merge with local machines.json — no
add-machine needed for your own devices. A newly-seen machine_id prints a
one-line "new device joined" notice (seen-set in seen.json).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… notify

Sign-in keeps the prf secret in memory so the session can derive K_reg; the
machine view renders the local list immediately, then enriches it from
GET /registry?wallet= (same-origin, best-effort): decrypt each blob with the
wallet (forgeries dropped), merge (local wins), and show a one-line "new device
joined" notice. Discovery only — attach path unchanged. Verified in-browser:
sign-in → machine list renders, no errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A real-relay e2e: an agent seals a record + base64s it onto its live
registration, the relay serves it verbatim (blind), and the fetcher
base64-decodes + OpenRecords it (machine_id AAD) to recover the record — a
forgery is dropped, a fresh relay is empty (stateless). Catches cross-
component contract drift the per-slice fixtures can't. README documents
auto-discovery; SECURITY documents the encrypted/blind/stateless registry.

Deploy of the new mir-signal (/registry endpoint) is Fredrik's hand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@frahlg frahlg merged commit ebe72f5 into main Jun 14, 2026
2 checks passed
@frahlg frahlg deleted the b2-device-registry branch June 14, 2026 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant