Skip to content

feat(net): checkpoint public request answering - #6411

Closed
kixelated wants to merge 3 commits into
mainfrom
quest/m1/public-model/answer
Closed

kixelated wants to merge 3 commits into
mainfrom
quest/m1/public-model/answer

Conversation

@kixelated

@kixelated kixelated commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Custom subscribers cannot reproduce built-in answering through the public model. Sources rely on session validity checks, and JavaScript's blind-answer loop reaches private request slots.

Approach

Checkpoint bound Rust answer sources, owned unused-request/group operations, empty origin handles, and session-owned source guards. Add JavaScript broadcast request/listing handling and move both subscribers onto it. Move blind answering into an origin fallback stream, preserving scope, first-answer ownership, and errors when an announced route replaces a resolved blind answer.

Consumers: both native subscribers, both JavaScript subscribers, and connection forwarding. Retarget the existing source-leak mutation to the moved guard without changing its behavior.

Per-operation FETCH and TRACK-info validity stays private and unchanged for the deferred quest. The updated answering quest records its actual request-role prerequisite and unfinished source-handout/inventory work.

Impact

  • Proposed Rust additions: origin::Producer::answer_source, Producer::empty, Consumer::empty, Dynamic::withdrawn, broadcast::Producer::{poll_unheld,close_unheld}, group::Producer::abort_unused, and track::Request::reject_unused.
  • Proposed JavaScript additions: Broadcast.Dynamic, Broadcast.Producer.dynamic, protected Broadcast.Consumer.dynamic, and Origin.Producer.fallback.
  • The fallback is not usable from published declarations yet: its generic Origin.Request has only prepare and reject after stripInternal. It needs the existing request-role split, not a public runtime-only accept check.
  • Source retirement now stops new lookups and handouts. A previously handed-out but unaccepted track request still needs model-enforced retirement; built-in sessions retain their existing per-operation guards. Already accepted tracks keep running.
  • Wire format: unchanged. No package versions changed.

Validation

  • Required Rust visibility inventory rerun with cargo check -p moq-net --features fuzz, including --tests; all temporary inventory edits restored.
  • just rs test -p moq-net: 3,034 passed, 11 ignored tests skipped (including 971 model tests).
  • JavaScript networking: 2,656 passed. JS typecheck, package build, formatting, and private-model markers pass.
  • Reproduced stale-source track admission before the fix; source retirement now refuses new lookups while accepted tracks can finish.
  • Published-package consumer confirms TS2339: Property 'accept' does not exist on type 'Request', proving the role prerequisite below.
  • just test interop: passed 8 Rust/JS wire prechecks, native media/auth controls, CLI FETCH close, and finite tails.
  • Leak sensitivity: baseline passes; the same mutation fails with the expected cancelled-broadcast assertion. All 7 mutations apply.
  • quest check --base origin/main: 613 documents pass; Required is acyclic.
  • just ci: JS/Rust/quest/drill checks pass, then the Nix runner-image check fails because systemd-repart cannot write /proc/self/uid_map (Operation not permitted). The gate cannot complete in this environment.

Alternatives

Publishing generic Request.accept would recreate the application-claim role trap. Keep that boundary and finish its already planned type split. A separate advertised blind route was previously rejected in favor of an unadvertised fallback.

Follow-ups

No new follow-up quests. Remaining source-handout validity, private-slot/test migrations, and stats/discovery inventory cleanup stay in the updated answering quest. Route ingestion and deferred FETCH/TRACK-info work keep their existing owners.

Decisions needed

  1. ✅ Decided by the maintainer on 2026-10-23: parked until the prerequisite lands. Finish the claim-request-type prerequisite before this public contract (recommended). It explicitly owns both Rust and TypeScript role separation; this checkpoint does not duplicate it.
  2. ✅ Decided by the maintainer on 2026-10-23: a source factory on the forwarded-request role, request.source(). Source ownership/name: prefer a source factory on the eventual forwarded-request role, such as request.source(), over the current producer.answer_source(&request) prototype. The latter must reject mismatched origins and application claims at runtime.
  3. ✅ Decided by the maintainer on 2026-10-23: fallback(). JavaScript fallback name: fallback() (recommended) or unrouted(). Its handle has request-stream and close operations only, since it owns no advertised route to reprice.

This checkpoint is incomplete and remains a draft.

(Written by GPT-6)

kixelated and others added 2 commits October 22, 2026 16:20
Co-Authored-By: GPT-6 <noreply@openai.com>
@kixelated kixelated changed the title Public answering feat(net): checkpoint public request answering Oct 22, 2026
Co-Authored-By: GPT-6 <noreply@openai.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Checkpoint pushed; remains a draft. The public fallback's forwarded-request role depends on the existing claim-request-type quest, and unfinished handed-out track validity plus inventory cleanup remain recorded in this quest. Source ownership/name and fallback naming alternatives are listed in Decisions needed.

3,034 Rust networking tests, 2,656 JS networking tests, native interop, and the retargeted leak-sensitivity baseline/mutation pass. Full just ci reaches the Nix runner-image build, where user-namespace mapping is denied by the environment.

(Written by GPT-6)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Plan change from moq-dev/moq#6581: the maintainer dropped the public-model goal, which was that a third party builds any relay from the public model. This quest moves to quest/m1/api-audit/answer.md and shrinks to internal work only:

  • Keep: the source minted for a dynamic request refuses new track requests with Unroutable once its route is replaced or its selection period retires. Already-accepted tracks keep running.
  • Delete: wire.ts OriginConsumer.broadcasts.
  • Drop, no longer public:
    • answer_source
    • the unused-race family (poll_unheld/close_unheld, group poll_unused/abort_unused, reject_unused)
    • Producer::empty/Consumer::empty and Dynamic::withdrawn
    • JS Origin.Producer.fallback() and Broadcast.requested

Please reshape this PR to match. Its branch name no longer matches the quest path, so delete quest/m1/api-audit/answer.md when this finishes it.

(Written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Closing: superseded by #6748, which makes a minted source enforce its own request validity.

(Written by Claude Opus 5.5)

@kixelated kixelated closed this Oct 25, 2026
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