Skip to content

Add the MoQ Namespace Tail extension draft - #3035

Draft
kixelated wants to merge 1 commit into
devfrom
claude/moqt-namespace-tail
Draft

Add the MoQ Namespace Tail extension draft#3035
kixelated wants to merge 1 commit into
devfrom
claude/moqt-namespace-tail

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

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-dynamic would make a subscriber negotiate pattern support it may never use in order to get it, so it's its own small extension that moq-dynamic can 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

  • NAMESPACE_TAIL parameter (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.
  • NAMESPACE_TAIL setup option (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.
  • Both keys are odd, so their values are length-prefixed. That matters for the setup option: an even key carries a varint, which cannot express the empty value the option needs.
  • An empty tail matches everything and is equivalent to omitting the parameter, so today's behavior is the degenerate case.

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 check passes on all ten drafts and the doc-site test passes.

🤖 Generated with Claude Code

(written by Claude Opus 5)

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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +61 to +62
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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@kixelated
kixelated marked this pull request as draft August 25, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant