feat: wallet owner_id on the wire (B1.4 + B1.5)#41
Merged
Conversation
Carry an opaque wallet-binding record on the offer browser->agent. The relay copies it verbatim and never interprets it (still blind: sees only ciphertext + routing metadata). Backward-compatible; no behavior change when absent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The agent recovers the Noise-KK X25519 pin from the offer's wallet-signed binding instead of hex-decoding owner_id. Checks binding.wallet==owner_id and a valid signature, then pins binding.x25519. No legacy hex path. device is the owner's device id (not machine_id), so it is not checked here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
owner.json gains a stable device_id and a cached wallet->x25519 binding, signed at SetFromSecret/Rekey. mir attach now dials owner_id=<wallet address> and carries the signed binding on the offer; the KK initiator still uses the X25519 transport key. Errors if the identity has no wallet. The two client e2e tests pin the wallet address (not the hex transport key) to match the agent's binding.wallet==owner_id check (B1.4.1). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pairing now pins the base58 wallet, proven by an Ed25519 auth signature
over the Noise channel binding (domain miranda/auth/v1). msg1 carries
PairClaim{wallet}; a new msg3 carries the signature. The agent pins the
wallet only if auth verifies. Adds identity.SignAuth/VerifyAuth.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign-in derives the Ed25519 wallet alongside the X25519 transport key
(one prf root, mirroring Go's owner.json). Attach dials owner_id=<wallet
address> and carries a signed binding on the offer; the Noise-KK initiator
still uses the X25519 transport key. Pairing sends PairClaim{wallet} + a
msg3 auth signature over the channel binding, byte-identical to Go. Adds
identity/auth.js and a stable per-browser device id; precache auth.js.
The pairing interop vector now carries wallet_prf so the JS side derives
the same wallet and reproduces msg1/msg3 exactly. wallet-derivation and
wallet-binding vectors are untouched (Noise data plane unchanged).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
docs/superpowers/specs/2026-06-12-b1.4-wallet-owner-id-wiring.md(decisions locked 2026-06-13) and the plan in
docs/superpowers/plans/2026-06-13-b1.4-b1.5-wallet-wire-plan.md.Summary
owner_idis now the base58 Solana wallet address end-to-end. The agentrecovers the X25519 Noise-KK pin from a wallet-signed binding carried on the
offer, and pairing pins the wallet — proven by an
authsignature over the Noisechannel binding. No legacy path (decision 2): wallet + binding are always
required; an identity without a wallet re-pairs via
mir keygen --wallet.The Noise data plane is byte-identical — the X25519 transport and the
wallet-derivation/wallet-binding/ Noise-KK vectors are untouched. Only thesource of the pinned key changed, plus a new pairing
authscheme.What changed (per slice)
signal— opaqueBindingfield onSignalMsg; the relay forwards itverbatim and never reads it (still blind). Backward-compatible, independently
deployable.
agent— at attach, parse+verify the offer's binding(
wallet == owner_id+ signature), pinbinding.x25519. Replaceshex.Decode(owner_id).client—owner.jsongains a stabledevice_idand a cached signedbinding (signed at
SetFromSecret/Rekey);mir attachdialsowner_id=<wallet>and carries the binding on the offer. KK initiator still uses the X25519 key.
pairing— msg1 carriesPairClaim{wallet}; a new msg3 carriesEd25519.sign(wallet, "miranda/auth/v1" || channelBinding). The agent pins thebase58 wallet only if it verifies. Adds
identity.SignAuth/VerifyAuth.web— sign-in derives the wallet alongside the X25519 key (one prf root);attach sends
owner_id=<wallet>+ a signed binding; pairing mirrorsPairClaim+msg3 auth byte-for-byte with Go.
devicesemantics (clarified vs the draft spec)binding.deviceis the owner's stable device id, not the agent'smachine_id, soone cached binding works across all the owner's machines (decision 3). The agent checks
wallet == owner_id+ signature, notdevice == machine_id— KK alreadyauthenticates the X25519 holder, so the device check would only break caching.
Tests
go test ./...green (incl. agent/client/pairing e2e updated to wallet+binding);go build ./...,gofmt,go vetclean.npm test93/93 (newauth.test.js; rewritten pairing-interop assertsmsg1/msg2/msg3 byte-match Go via the new
wallet_prfin the vector).testdata/pair-interop.jsonchanged;wallet-derivation/wallet-binding/ Noise-KK vectors untouched.Deploy sequencing (your hand)
The new
mir-signal(one-lineBindingpassthrough) is backward-compatible, so itcan be redeployed first. Because there is no legacy path, agents + clients are a
coordinated cut with the wallet wire; existing X25519-only identities re-pair via
mir keygen --wallet.🤖 Generated with Claude Code