Skip to content

fix(net): advertise a relayed claim only once its origin routes it - #8518

Draft
kixelated wants to merge 2 commits into
mainfrom
fix/route-advertise-confirmed
Draft

kixelated wants to merge 2 commits into
mainfrom
fix/route-advertise-confirmed

Conversation

@kixelated

@kixelated kixelated commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A relay's route node handed a claim of another node to every lite-07 announce stream as soon as the claim arrived, but the claim entered the relay's origin route table only once the session serving it drained the node's Serve output and called origin.forward. Under load that drain takes milliseconds. A downstream peer that read the advertisement in that window and sent a request pinning the new instance and node was refused at once with subscribe: unroutable, typically when a broadcast restarts at a new instance and a player resubscribes.

Approach

The node advertises a claim to other streams only once a stream serving it confirmed that instance (Stream::serving, now carrying the served claim; Slot::confirmed records the instance and epoch).

  • A stream's outbox holds each start or restart back (Outbox::withheld) while the claim has a serving stream and none confirmed its instance. The peer keeps what it was last sent, a newer change replaces a held one, and a route end drops them.
  • While anything of an identity is held, its batches close with a floor that certifies nothing, as the answer stream already does for claims it filters out. The held claim goes with the real floor once confirmed.
  • State::flush now serves the shown claims before queuing the batches that carry them, so a batch sees whether its claim is served yet.
  • A stale confirmation (an older instance) releases nothing.
  • Cost: a new confirmation scans each stream's outbox once (a read lock), the same order as the router's own fan-out of the claim. Held state is one entry per claim per stream, bounded like the pending batches.

a_claim_goes_on_only_once_served (node layer) fails without the gate and covers the start, the restart, and a stale confirmation.

Impact

  • Wire: none. A held claim's batch sends ANNOUNCE_FLOOR without a revision, which lite-07 already allows.
  • Public API: none (route::node is crate-private).
  • Behavior: a relay delays a relayed claim by the time its own origin takes to route it. A claim no serving stream ever confirms (one dropped as a loop) is no longer advertised onward.

Alternatives

  • A lite-07 request pinning an instance the origin does not route yet waits until the node stops showing that claim. Rejected: a request-side workaround every request kind must carry, and the relay still advertises what it cannot resolve.

Follow-ups

None.

Caused by

  • moq-dev/moq#8023 - lite-07 routing handed a claim to other streams before its serving stream confirmed it.

Models

Size: M
Implementer: Claude Code, claude-opus-5-5, medium effort (iterate, M)

🤖 Generated with Claude Code

(Written by Claude Opus 5.5 with medium)

Review

Rewritten by quest review after each round.

  • Reviewer: Codex, gpt-6.1-sol, medium effort
  • Session: 01a1cc90-0fb4-7a90-bda2-92b371b732b2, round 1
  • Head: d76b9f4, named in full once a round reports no findings
  • Base: origin/main at c04c583
  • Score: 6/10, Exact-path announcements are gated, but narrowed covering announcements still expose the routing race and need rework.
  • Findings, open until the next round settles them:
    • Medium, rs/moq-net/src/route/node.rs:778: Gate narrowed covering claims on their source confirmation

kixelated and others added 2 commits November 11, 2026 13:07
A relay's route node handed a claim of another node to every lite-07
announce stream as soon as it arrived, but the claim reached the relay's
origin only once the session serving it drained the node's Serve output.
A peer could name the new instance in a request during that window and be
refused as unroutable. The node now holds each start or restart back from
the other streams until a stream serving it confirms that instance, and
the floors certify nothing meanwhile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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