Files
punktfunk/crates
enricobuehlerandClaude Opus 5 3350d3cfbf feat(client): the punktfunk:// grammar — one parser, one emitter, one vector file
D0 of design/client-deep-links.md. Apple has shipped `punktfunk://connect/<uuid>` for a
while; this is the same grammar, generalised and given a Rust implementation the GTK and
WinUI shells, the session and the coming CLI all share:

    punktfunk://connect/<host-ref>[?fp=…][&host=addr[:port]][&launch=…][&profile=…][&name=…]

The rule the grammar exists to keep is that a URL may only ever do what a click on an
existing card could do, minus trust decisions. So it carries REFERENCES to things that
already exist on this device — never values: no resolution, no bitrate, no codec, so a
web page cannot shape a session beyond picking among the user's own configurations. And
`pair` is not a route: it parses, and refuses, so a link can never start a trust
ceremony. Everything hostile is rejected here, once, for every front-end — 2 KB total
cap, per-parameter caps, strict percent-decoding (a half-escape or invalid UTF-8 is an
error, not a shrug), control characters refused after decoding, launch ids held to the
charset the host and Decky already agree on, and `fp=` that must actually be 64 hex.

Resolution follows the same order everywhere: stable record id → unique host name →
`addr[:port]`, with `host=`+`fp=` as the reinstall recovery path, which is why
self-emitted links carry all three. An ambiguous name refuses rather than guessing; a
stale id with no recovery address refuses rather than dialling "11111111-…" as if it
were a hostname; an unknown-but-plausible address becomes the confirmation sheet, never
an auto-connect.

`pf://` parses as an input alias and is never emitted or registered — claiming a
two-letter scheme on MSIX/Apple is unconditional squatting, and a link that resolves on
one platform only is a trap.

The 44-case `clients/shared/deeplink-vectors.json` is the cross-language contract: this
module's tests run every case, and the Swift and Kotlin ports will run the same file, so
three parsers cannot drift into three different security postures. Refusal codes are
part of that contract, not just the happy path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 20:32:32 +02:00
..