Add the MoQ Namespace Tail extension draft - #3035
Conversation
moq-transport lets a subscriber ask for the namespaces under a prefix and nothing else, so a subscriber wanting a subset receives the whole set and discards the rest. The cost is a message per namespace it did not want, and it grows with the publisher's inventory rather than with the subscriber's interest. Prefixes are not the natural shape of every interest. A namespace's leading fields tend to say who owns the content and its trailing fields tend to say what it is, so "every recording belonging to this tenant" is a prefix query while "every namespace of this kind, whoever owns it" is not expressible at all. This adds the other end: a NAMESPACE_TAIL parameter on SUBSCRIBE_NAMESPACE, with the publisher answering the namespaces that match both halves. Split out of the dynamic advertisement work, where it started as a field on the pattern request. It does not belong there: filtering by tail is useful with no patterns anywhere, and folding it into that extension would make a subscriber negotiate pattern support it may never use to get it. The relay case gets its own section, because a relay is usually the only endpoint on a path that has this: it subscribes upstream with the prefix alone and filters locally, which is the only option against an upstream without the extension and worth doing even with one, since several downstream requests differing only in tail share an upstream subscription. That comes with a bound, since distinct tails under one prefix all draw on the same upstream set and an unbounded relay lets a subscriber multiply its upstream state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8af8ca955d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| An endpoint MUST NOT send the NAMESPACE_TAIL parameter to a peer that did not declare support. | ||
| A peer without this extension would ignore the parameter and answer the prefix alone, which is a strictly larger set: correct, but silently unfiltered, and a subscriber cannot tell that from a prefix whose contents genuinely all match. |
There was a problem hiding this comment.
Correct the unsupported-parameter behavior
When NAMESPACE_TAIL is sent without negotiation, the peer cannot ignore it and answer the prefix alone as claimed here. MoQ Transport message parameters have no generic skip rule, and both repository implementations reject unknown message parameters (decode_params! in rs/moq-net/src/ietf/parameters.rs and getMessageParamKind in js/net/src/ietf/parameters.ts). The request therefore fails instead of producing a silently unfiltered response, so this compatibility rationale should describe the protocol error while retaining the negotiation requirement.
AGENTS.md reference: drafts/AGENTS.md:L7-L13
Useful? React with 👍 / 👎.
Split out of #3005, where the request tail started life as a field on the pattern request.
Why it's separate
Filtering by tail is useful with no patterns anywhere. Folding it into
moq-dynamicwould make a subscriber negotiate pattern support it may never use in order to get it, so it's its own small extension thatmoq-dynamiccan reference.The gap
moq-transport lets a subscriber ask for the namespaces under a prefix, and nothing else. A subscriber that wants a subset receives the whole thing and discards the rest, at a cost of one message per namespace it did not want — a cost that grows with the publisher's inventory rather than with the subscriber's interest.
Prefixes are not the natural shape of every interest. A namespace's leading fields tend to say who owns the content and its trailing fields tend to say what the content is. So "every recording belonging to this tenant" is a prefix query, while "every namespace of this kind, whoever owns it" is not expressible at all. This adds the other end.
The extension
0x40B63) on SUBSCRIBE_NAMESPACE, carrying a sequence of Track Namespace tuple fields. The message's prefix is the head of the request, the parameter is its tail, and a publisher that negotiated the extension MUST NOT send an advertisement that doesn't match both.0x40B61) declares support. Sending the parameter to a peer without it is a MUST NOT: an unaware peer ignores the parameter and answers the prefix alone, which is correct but silently unfiltered, and the subscriber can't distinguish that from a prefix whose contents genuinely all match.Relays get their own section
A relay is usually the only endpoint on a path that has this, since its downstream subscriber and upstream publisher are separate deployments upgraded at separate times. It subscribes upstream with the prefix alone and filters locally — the only option against an upstream without the extension, and worth doing even with one, since several downstream requests differing only in tail share a single upstream subscription.
That comes with a bound. Distinct prefixes correspond to distinct namespaces a subscriber must be authorized for, but distinct tails under one prefix all draw on the same upstream set, so a relay opening one upstream subscription per requested tail lets a subscriber multiply its upstream state at will.
Security considerations also note that filtering is not access control: a tail narrows what a subscriber is sent, never what it is entitled to.
just drafts checkpasses on all ten drafts and the doc-site test passes.🤖 Generated with Claude Code
(written by Claude Opus 5)