From 8ad7606ce56d4b835909f23815f1f6699ec10e7d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 30 Sep 2026 07:34:53 -0700 Subject: [PATCH] quest(m1/auth): move peer grants to the P2P line Peer grants have one consumer, P2P signaling, so they move to quest/m2/p2p/peer-grant.md and leave the auth line's Required. The auth README now scopes the no-prefix-only AUTH_OK rule to lite (IETF AUTH_OK carries prefixes and answers NOT_SUPPORTED otherwise) and adopts relay-refresh's connection-owned Connection::auth() handle. Co-Authored-By: Claude Opus 5.5 --- quest/m1/auth/README.md | 26 +++++++++++++------------ quest/m2/p2p/README.md | 2 +- quest/{m1/auth => m2/p2p}/peer-grant.md | 10 +++------- quest/m2/p2p/signal.md | 6 +++--- 4 files changed, 21 insertions(+), 23 deletions(-) rename quest/{m1/auth => m2/p2p}/peer-grant.md (91%) diff --git a/quest/m1/auth/README.md b/quest/m1/auth/README.md index 7cbb6da657..3dbe47ddb9 100644 --- a/quest/m1/auth/README.md +++ b/quest/m1/auth/README.md @@ -16,9 +16,8 @@ This questline adds an AUTH exchange to both wires: one stream per token, a grant per token, the union of every accepted token as the session's scope, and a loud failure when a publish can never be honored. It ends with the credential able to travel in band, while the URL keeps working for every peer -that predates the stream. Direct peer sessions need a second credential: a -relay-signed, hop-bound grant that a browser can verify without a signing -key, which HMAC relay keys cannot provide. +that predates the stream. Hop-bound peer grants for direct sessions belong to +[P2P](/quest/m2/p2p/peer-grant.md), their only consumer. ## Plan @@ -44,11 +43,13 @@ Decisions settled while planning, recorded so review does not relitigate them: - **A public grant contains publish patterns, subscribe patterns, and an expiry**, in the presenter's own root; the presenter never sees the relay-side root, and every token in a union shares the connection's root. Unscoped - permission is `**`; an empty union grants nothing. AUTH_OK carries those - patterns, wildcards and literals alike, from the first release. There is no prefix-only AUTH_OK and no - covering-prefix workaround. Announce stays a prefix: ANNOUNCE_REQUEST and - SUBSCRIBE_NAMESPACE do not gain patterns in that change. The public grant - type stays pattern-valued. + permission is `**`; an empty union grants nothing. Lite AUTH_OK carries + those patterns, wildcards and literals alike, from the first release, with + no covering-prefix workaround. IETF AUTH_OK carries Track Namespace + prefixes, so an acceptor whose grant is not a union of subtrees answers + AUTH_ERROR NOT_SUPPORTED rather than widening it. Announce stays a prefix: + ANNOUNCE_REQUEST and SUBSCRIBE_NAMESPACE do not gain patterns. The public + grant type stays pattern-valued. - **Fail loud by aborting the session.** A publisher whose origin announces a broadcast outside the union aborts the session with `Unauthorized`, naming the path. The check runs against the grants in hand once the tokens the @@ -72,7 +73,9 @@ Decisions settled while planning, recorded so review does not relitigate them: must explicitly grant `**` for unrestricted access; AUTH does not widen a scoped grant because the caller is another relay. - **Client API.** Tokens live on `moq_tokio::connect::Config`, the - dial-side config, and `Connection` exposes the live session's auth handle. + dial-side config. `Connection::auth()` is a handle the connection owns: it + keeps every added token, presents them on each session as it reconnects, + and reports the live session's grant. - **Spec home.** The AUTH stream is lite-06 core in `drafts/draft-lcurley-moq-lite.md`, the way routing is. moq-transport gets `drafts/draft-lcurley-moq-auth.md`, a setup-option-negotiated extension with @@ -103,11 +106,10 @@ existing lite-06 ALPN. - [Token in band](/quest/m1/auth/token-in-band.md) - the credential can leave the URL: a session starts on what the URL carried and its AUTH streams add the rest, with the URL kept for peers below lite-06 -- [Peer grants](/quest/m1/auth/peer-grant.md) - the relay issues a hop-bound, - asymmetrically signed grant a browser can verify; HS256 keys issue none ## Related - [Expiring media grants](/quest/m2/processor/grant-lease.md) - a worker's lease renewal is a new in-band token -- [P2P](/quest/m2/p2p/README.md) - the first consumer of hop-bound peer grants +- [Peer grants](/quest/m2/p2p/peer-grant.md) - P2P's hop-bound credential, + built on this line's relay tokens diff --git a/quest/m2/p2p/README.md b/quest/m2/p2p/README.md index f6e40ddb04..42eaa0e9c9 100644 --- a/quest/m2/p2p/README.md +++ b/quest/m2/p2p/README.md @@ -129,6 +129,7 @@ WASM build, so the browser side stays TypeScript. ## Required - [Data channel transport](/quest/m2/p2p/transport.md) - `@moq/p2p` speaks qmux over one ordered RTCDataChannel behind the WebTransport shape `@moq/net` consumes +- [Peer grants](/quest/m2/p2p/peer-grant.md) - the relay issues a hop-bound, asymmetrically signed grant a browser can verify; HS256 keys issue none - [Signaling and policy](/quest/m2/p2p/signal.md) - opted-in peers find each other under the prefix, the application picks who to dial, and the roster-size gate decides whether STUN is used - [Native data channel transport](/quest/m2/p2p/webrtc.md) - `moq-tokio` holds a moq-net session with a browser over str0m with a full ICE agent - [moq-cli joins](/quest/m2/p2p/cli.md) - `--p2p` publishes a roster entry with its iroh endpoint, dials iroh between native peers, and serves browsers as a transit hop @@ -140,5 +141,4 @@ WASM build, so the browser side stays TypeScript. ## Related -- [Peer grants](/quest/m1/auth/peer-grant.md) - the hop-bound credential a direct session presents; HMAC keys issue none - [E2EE](/quest/m1/e2ee/README.md) - what a peer would need if the token scope stopped being the trust boundary diff --git a/quest/m1/auth/peer-grant.md b/quest/m2/p2p/peer-grant.md similarity index 91% rename from quest/m1/auth/peer-grant.md rename to quest/m2/p2p/peer-grant.md index 12e0aa9298..4492b06a4d 100644 --- a/quest/m1/auth/peer-grant.md +++ b/quest/m2/p2p/peer-grant.md @@ -45,9 +45,9 @@ against the roster, and verifies AUTH_POP with `cnf`. The origin then serves only the granted paths. A missing, expired, HMAC-signed, or unproven grant is not a grant. -P2P is the first consumer: [Signaling and policy](/quest/m2/p2p/signal.md) presents the grant in band -on each direct session. This quest does not depend on that line. +on each direct session. P2P is its only consumer, so it lives on this line +rather than the auth line (decided in the 2026-09-30 audit). Docs: `doc/bin/relay/auth.md` states that peer grants need an asymmetric key, that HS256 operators get none, and that the public JWKS is not a @@ -61,8 +61,4 @@ Additive. ## Required -- [Relay tokens](/quest/m1/auth/relay-refresh.md) - the relay owns AUTH and knows the session's paths - -## Related - -- [Signaling and policy](/quest/m2/p2p/signal.md) - the first consumer +- [In-band auth](/quest/m1/auth/README.md) - relay tokens reach `main` with this line, so the relay owns AUTH and knows the session's paths diff --git a/quest/m2/p2p/signal.md b/quest/m2/p2p/signal.md index c088426611..e1b56885c0 100644 --- a/quest/m2/p2p/signal.md +++ b/quest/m2/p2p/signal.md @@ -25,7 +25,7 @@ Rust, so the id in the roster is the id in every chain the tab forwards. Roster: each peer publishes `` with an `info.json` snapshot track: the moq ALPNs it accepts, `webrtc: true`, whether it can run qmux unordered, the presenter public key that -[peer grants](/quest/m1/auth/peer-grant.md) bind, the application's `meta`, +[peer grants](/quest/m2/p2p/peer-grant.md) bind, the application's `meta`, and for native peers an optional `webtransport: { url, fingerprint }` and `iroh` endpoint id. The schema is shared with [moq-cli](/quest/m2/p2p/cli.md). Unordered is advertised here so the dialer @@ -74,7 +74,7 @@ and short-lived, which the peer presents in band with a proof of possession; the other side verifies the relay's signature, the hop id and key against the roster, and AUTH_POP, then serves only the granted paths. Issuance, asymmetric keys, JWKS, PoP, and refresh live in -[Peer grants](/quest/m1/auth/peer-grant.md): HMAC keys cannot be given to +[Peer grants](/quest/m2/p2p/peer-grant.md): HMAC keys cannot be given to browsers without also letting them forge grants, so an HS256-only relay issues nothing. A peer session with no verifiable grant serves nothing; there is no equal-scope shortcut. @@ -82,4 +82,4 @@ there is no equal-scope shortcut. ## Required - [Data channel transport](/quest/m2/p2p/transport.md) -- [Peer grants](/quest/m1/auth/peer-grant.md) - the hop-bound, asymmetrically signed credential a direct session presents; HS256 keys issue none +- [Peer grants](/quest/m2/p2p/peer-grant.md) - the hop-bound, asymmetrically signed credential a direct session presents; HS256 keys issue none