docs(roadmap): §8a done (mandatory pairing); split §8b into host+web / peer layers
ci / rust (push) Has been cancelled

§8a (require native pairing by default, serve --open) shipped + deployed. §8b
(delegated approval) refined into §8b-1 (host pending-requests + mgmt endpoints +
web Approve/Deny — achievable now) and §8b-2 (peer push to a paired Device A —
needs the native/Apple client UI).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 10:28:39 +00:00
parent 9a6058cd20
commit 57f7e32c24
+22 -17
View File
@@ -139,23 +139,28 @@ it's unbuildable on the Linux dev box; the trait boundaries are already in the r
The unified host + web-console pairing (arm a window → display the host PIN → user enters it on the The unified host + web-console pairing (arm a window → display the host PIN → user enters it on the
client) is built and live. Two changes harden it from "works" to "secure by default": client) is built and live. Two changes harden it from "works" to "secure by default":
- **Mandatory PIN pairing by default.** Today the punktfunk/1 host can run open (trust-on-first-use) - **Mandatory PIN pairing by default — done & live** (`§8a`, `serve --native` now requires
— *not* acceptable on a shared LAN, where any reachable device could connect. The unified host pairing; `serve --open` disables it). An unpaired client is rejected at the session gate; pairing
should `require_pairing` out of the box: a client must complete the SPAKE2 PIN ceremony (one online is via the SPAKE2 PIN ceremony (one online guess, no offline attack) armed from the web console.
guess, no offline attack) before any session. The operator arms a window and reads the PIN from the Validated live: unpaired → "this host requires pairing", then web-armed PIN → "client trusted".
web console (already built); an explicit `--open` escape hatch covers trusted single-user setups. Deployed to the dev box + Bazzite.
The wire is already in place (`M3Options.require_pairing` + the `serve_session` gate); this flips - **Delegated pairing approval** *(next — the ergonomic enabler for "mandatory": pair a device
the default and threads it through `serve --native` and the mgmt arm endpoint. without fetching the host PIN out of band).* Target flow:
- **Delegated pairing approval** — the ergonomic enabler for "mandatory" (pair a new device without
fetching the host PIN out of band):
1. Device A is already paired (authenticated) to Host X. 1. Device A is already paired (authenticated) to Host X.
2. The user tries to connect Device B to Host X. 2. The user tries to connect Device B to Host X.
3. Host X pushes a request to the authenticated Device A: *"Allow Device B to pair with Host X?"* 3. Host X surfaces a request: *"Allow Device B to pair with Host X?"*
4. The user approves/denies on Device A; on approve, Host X admits Device B — binding B's 4. The user approves/denies; on approve, Host X admits Device B — binding B's certificate
certificate fingerprint — with no PIN typed. fingerprint — with no PIN typed.
Needs: a host→client *pairing-approval-request* (B's fingerprint + a human label) delivered to A's Two buildable layers:
live connection (a QUIC side-plane message) or polled via the mgmt API; an approve/deny round-trip - **§8b-1 (host + web — achievable now):** an unpaired B that connects to an approval-enabled host
carrying an approval token; the host gating B's admission on it. The web console **and** the Apple is held as a **pending request** `{id, name, fingerprint, requested_at}` in `NativePairing`
client render the approval prompt. PIN pairing stays the bootstrap (the first device, or when no instead of a flat reject; mgmt gains `GET /native/pending` + `POST /native/pending/{id}/{approve,
paired device is online to approve). deny}`; the web console lists pending requests with Approve/Deny. The **operator approves from
the console** — delegated approval via the management surface.
- **§8b-2 (peer push — needs the client):** the host also pushes the pending request over a paired
**Device A**'s live QUIC connection (a new control-plane message); A's app renders the prompt and
replies approve/deny — the user's exact "Device A gets a notification" flow. The native/Apple UI
is a client-agent task.
PIN pairing (§8a) stays the bootstrap — the first device, or when no approver is online.