Expand description
Wire protocol for the MUR mobile app ↔ Mac daemon WebSocket endpoint.
Shared by mur-mobile-sdk (the phone client) and mur-daemon (the Mac
endpoint) so both ends agree on the framing. Every message is one JSON
object sent as a WebSocket text frame. The first client frame is a
ClientFrame::Hello pairing handshake; once the server replies
ServerFrame::Paired, application traffic is carried as Ed25519-signed
SignedEnvelopes whose payload is the canonical JSON of an A2A
JsonRpcRequest — the same crypto MUR uses for agent↔agent bridge traffic.
P3 adds a voice streaming path: the phone streams raw 16 kHz mono f32 PCM chunks, the Mac runs whisper.cpp for an authoritative transcript, then replies with Kokoro TTS audio chunks.
Design: docs/superpowers/specs/2026-06-05-mur-voice-mobile-app-design.md.
Enums§
- Client
Frame - Frames the phone sends to the Mac endpoint.
- Server
Frame - Frames the Mac endpoint sends back to the phone.
Constants§
- HITL_
RESPOND_ METHOD - A2A method (carried inside a signed
ClientFrame::Envelope) by which the phone authoritatively responds to a HITL gate (v4c). It rides the signed envelope path — NOT a plain frame — so the daemon verifies the phone’s Ed25519 signature before writing the gate-releasingHitlResponse. - MOBILE_
WS_ PATH - WebSocket path the daemon’s mobile endpoint serves.
- PAIR_
ROLE_ DAEMON_ TO_ PHONE - PAIR_
ROLE_ PHONE_ TO_ DAEMON - Per-direction role tags: a daemon→phone confirm can’t be replayed as a phone→daemon proof (reflection defense).
Functions§
- ct_
verify - Constant-time byte-slice equality (length is not secret; contents compared in constant time so a partial match doesn’t leak via timing).
- mint_
nonce - A fresh 32-byte liveness nonce for the enrollment challenge (OS CSPRNG).
- pair_
proof - HMAC-SHA256(token, transcript) — full 256-bit output, never truncated.
- pair_
transcript - Build the enrollment transcript bound to every field that scopes the proof,
using FIXED-length (u32_le) length-prefixing so no field boundary is
ambiguous (mirrors
bridge::envelope::signing_bytes). The token is NOT included — it is the HMAC key inpair_proof, never in the signed bytes.