PiWalletSV companion-interop protocol — v1¶
This directory is the canonical specification for the wire formats, QR transport, key derivation, and SPV requirements that connect a PiWalletSV signer to a companion application.
Anyone is free to build a companion (mobile app, desktop wallet,
browser extension, terminal tool — anything) that pairs with a
PiWalletSV signer by following the rules in this directory. The
reference implementation lives in this repository (piwallet/ on the
Pi side, companion/ on the browser PWA side) and serves as the
correctness oracle when this text is ambiguous.
The opposite direction (third-party signer paired with the PiWalletSV
companion) is also covered, although the PiWalletSV companion is
opinionated about defaults — see spv.md §5 for what it
expects from a signer.
Audience¶
You are reading this if you want to:
- Decode the QR codes the PiWalletSV signer or companion paints on screen.
- Encode QR codes that the PiWalletSV signer will accept.
- Re-implement the PiWalletSV signer in different hardware or software.
- Verify that a paired companion isn't trying to talk to the signer in an unsupported way.
You are not reading this if you only want to use PiWalletSV — that
audience belongs in ../user-manual.md (coming).
Scope of v1¶
Everything in this directory is protocol version 1. The current
envelope v field is 1 and the multipart QR magic is PW1. When a
breaking change is needed:
- The CBOR envelope's
vinteger is incremented and old versions are rejected on receive. - The QR magic becomes
PW2, etc., and assemblers refuse mixed streams.
A signer SHOULD refuse any envelope or QR stream whose version it does not implement, and surface a clear error to the user.
Files¶
envelopes.md— CBOR + gzip payload shapes for the three message kinds:xpub_export,unsigned_proposal,signed_tx. Includes byte-order rules, canonical encoding requirements, and field-by-field types.qr-transport.md— thePW1multipart QR framing: line grammar, base64url alphabet, chunk sizing, assembler rules.derivation.md— BIP39 mnemonics, BIP32 + BIP44 account derivation atm/44'/236'/0', P2PKH address encoding (BSV mainnet), receive / change branches, the 4-byte self fingerprint used to route envelopes.spv.md— what the signer verifies before producing signatures: BEEF parse, MerklePath ↔ header-anchor cross-check, prevout script ↔ derivation match, change re-derivation, conservation of value, fee-rate cap.conformance.md— canonical test vectors:addresses_canonical.json,proposal_01.cbor,proposal_01.json, and a field-by-field decoded form (proposal_01_decoded.json) that an implementation can diff against.
For where this protocol fits relative to the broader BSV BRC stack
(BRC-62 BEEF, BRC-67 SPV, BRC-74 BUMP, BRC-95 Atomic BEEF, and why
PiWalletSV intentionally is not a BRC-100 wallet), see
../brc-alignment.md.
Design tenets¶
These are the rules the reference implementation follows; they explain the "why" behind decisions that the per-file specs only state.
- The signer is the trust anchor. The companion is online but
semi-trusted. Every claim the companion makes (which UTXOs you
have, how much each is worth, which address is change) is
cryptographically re-verified on the signer using
BEEFproofs anchored to user-displayed block headers. A malicious companion cannot exfiltrate keys or trick the signer into producing a signature on a transaction that pays the wrong place. - No private material ever crosses the QR boundary. Only public xpubs, unsigned proposals, and signed (already-public) transactions travel between the two halves.
- Plain bytes, not opaque envelopes. Every wire field is a primitive (int, string, bytes, array, map). No tagging, no custom CBOR tags, no proprietary serialization. Anyone with a CBOR library and a gzip implementation can decode our payloads.
- Short keys, gzipped CBOR. PW1 multipart QR has bounded per-frame capacity, so envelope key names are short and the payload is compressed before framing.
- No mutable shared state. Each envelope is self-contained; nothing is implicit or carried over between QR sessions.
Stability promise¶
Protocol version 1 is stable in the sense that:
- The bytes a v1 signer emits today MUST still parse identically on every v1 implementation forever.
- Optional fields that may be added in v1 (such as a
metamap for future extensions) MUST be additive — a v1 decoder that ignores unknown keys SHOULD still succeed if the required v1 keys are present and well-typed.
If we ever need a breaking change, we'll cut v2 in a clearly-named
sibling directory (docs/protocol/v2/) and run the two protocols
side-by-side for a deprecation window.
Where to start¶
If you're new to the protocol, read in order:
derivation.md(shortest, sets the wallet model).envelopes.md(the actual wire payloads).qr-transport.md(how to push envelope bytes across the air gap).spv.md(what a signer must verify; informs what your companion must include).conformance.md(run your decoder against the golden fixtures before shipping).