Bitcoin PIR

Protocol & wire explorer

Run a query and see every WebSocket frame the SDK sends and receives. The explorer monkey-patches window.WebSocket so it observes every frame the vendored DPF/HarmonyPIR WASM clients produce, then checks the captured traffic against the four privacy invariants documented in the main repo's CLAUDE.md.

1. Pick a backend & run a query

Privacy invariants
no traffic yet
K-padding visualisation
INDEX round
(no frame yet)
CHUNK round
(no frame yet)

Each box is one PBC group query. Every group is byte-indistinguishable to the server — real queries and dummy padding are colour-equal here because they are colour-equal on the wire.

Merkle per-level pass count
No Merkle frames observed yet.
Frame timeline
0/0 frames
seqt (ms)sockdirlabelsizegroup×kpghex preview
No frames captured yet. Run a query to start the tap.

2. Found vs. not-found wire-shape diff

Found vs. not-found wire-shape diff

Run two queries — one known-found, one known-not-found — and check whether the wire shape (frame counts, byte counts, group counts) differs. The privacy guarantee is that they are identical.

Methodology

The explorer wraps window.WebSocket with a subclass that records every send() call and every received message event before forwarding them to the real socket. The vendored DPF/HarmonyPIR WASM client and the OnionPIR TS client both call the global new WebSocket(url) constructor at connect-time, so the patched subclass picks up every frame they exchange with the server. The frame body is parsed against the documented wire layout ([4B len LE][1B opcode][payload]) to extract count, keysPerGroup, and the opcode label without decrypting any payload bytes — all four privacy invariants are observable on the unencrypted wire-format envelope.

The DPF and HarmonyPIR clients normally upgrade to an encrypted channel after attestation. The explorer skips that upgrade intentionally — with ChaCha20 ciphertext on the wire the invariants would still hold but would no longer be parseable from JS. Production wallets MUST enable the channel; the plaintext mode here is observation-only.