Skip to content

Expose QUIC datagrams on the connection surface - #30

Merged
lann merged 1 commit into
mainfrom
datagram-surface
Aug 7, 2026
Merged

Expose QUIC datagrams on the connection surface#30
lann merged 1 commit into
mainfrom
datagram-surface

Conversation

@lann

@lann lann commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

The datagram half of #3's connection API, driven by the mosh-over-SSP
experiment: three functions on connection, RFC 9221 end to end.

  • max-datagram-size reports the current ceiling (path-MTU-dependent;
    the WIT doc states the ~1.1 KiB expectation under this package's
    fixed 1200-byte MTU profile) and doubles as the capability probe —
    none means the peer accepts no datagrams.
  • send-datagram is synchronous and never applies backpressure: a full
    send buffer drops the oldest queued datagrams (noq's drop=true).
    Ruling on Endpoint surface: expose QUIC datagrams as one-shot messages #28's open question: loss is this transport's semantics and
    a full buffer is loss; the driving consumer (SSP) wants stale state
    diffs dropped, not queued, and drop keeps the call sync. Too-large
    and peer-accepts-none fail invalid-argument.
  • recv-datagram is async with the accept-family concurrency contract
    (concurrent callers each get one datagram). The other Endpoint surface: expose QUIC datagrams as one-shot messages #28 question —
    a non-blocking try-recv companion — is deliberately absent: async
    recv plus concurrent calls cover pump-style consumers on the async
    ABI, and the function is additive latitude if a consumer shows the
    need.

Implementation is thin by design: noq-proto queues datagrams
internally in both directions, so the endpoint's DatagramReceived /
DatagramsUnblocked events stay discarded — resource methods poll the
queues directly under the existing bounded-polling discipline, and
recv-datagram is the accept-stream pattern verbatim. The spike
guest's leg is event-driven instead: its scripted exchange grows an
unconditional datagram echo (client sends after the stream echo and
closes only once the echo returns), which turns its former
event-discard site into the handler.

Both demo sides send fixed copies of one datagram rather than
retrying: datagrams are lossy by contract, and duplication keeps the
exchange deterministic on lossless local wires without either side
cancelling a pending receive (an in-flight import subtask must
resolve — the jco discipline).

Conformance: the endpoint matrix rows assert the datagram echo over
the relay, the UDP direct path, and the WebRTC channel (--datagram
on the demo and both hosts' drivers; the jco endpoint driver carries
the flag for when #10 unblocks); the interop rows assert it against
upstream iroh in both directions — RFC 9221 wire compatibility against
the real implementation, not just ourselves; and the spike rows
exercise it under jco today. Full gate run: fmt, clippy, validate-wit,
test, probes, matrix (17/17), bench budgets all green.

Review notes

  • The two Endpoint surface: expose QUIC datagrams as one-shot messages #28 open questions are ruled in the WIT docs: send is drop-not-backpressure (sync, lossy semantics — SSP wants stale diffs dropped), and there is no try-recv in v1 (async recv + the accept-family concurrency contract cover pumps; additive if a consumer shows need).
  • The endpoint's event-discard sites: endpoint/src/endpoint_impl.rs keeps discarding both datagram events — noq queues internally and the bounded-polling discipline reads the queues directly, so recv-datagram is accept-stream verbatim. The spike guest goes the other way (event-driven), so both plumbing styles named in Endpoint surface: expose QUIC datagrams as one-shot messages #28 are exercised.
  • Conformance: --datagram on the endpoint relay/UDP/WebRTC rows, on both interop rows (upstream iroh both directions — the RFC 9221 wire-compat claim), and unconditionally in the spike exchange (the only jco-running legs until jco: scheduler stops delivering waitable events once a detached task holds in-flight imports across export calls #10). Matrix 17/17, bench budgets hold.
  • The DATAGRAM_COPIES convention exists because the demos must be deterministic on lossy-by-contract transport without cancelling a pending receive (jco's in-flight-subtask discipline).

Fixes #28

The datagram half of #3's connection API, driven by the mosh-over-SSP
experiment: three functions on `connection`, RFC 9221 end to end.

- `max-datagram-size` reports the current ceiling (path-MTU-dependent;
  the WIT doc states the ~1.1 KiB expectation under this package's
  fixed 1200-byte MTU profile) and doubles as the capability probe —
  `none` means the peer accepts no datagrams.
- `send-datagram` is synchronous and never applies backpressure: a full
  send buffer drops the oldest queued datagrams (noq's `drop=true`).
  Ruling on #28's open question: loss is this transport's semantics and
  a full buffer is loss; the driving consumer (SSP) wants stale state
  diffs dropped, not queued, and drop keeps the call sync. Too-large
  and peer-accepts-none fail `invalid-argument`.
- `recv-datagram` is async with the accept-family concurrency contract
  (concurrent callers each get one datagram). The other #28 question —
  a non-blocking `try-recv` companion — is deliberately absent: async
  recv plus concurrent calls cover pump-style consumers on the async
  ABI, and the function is additive latitude if a consumer shows the
  need.

Implementation is thin by design: noq-proto queues datagrams
internally in both directions, so the endpoint's `DatagramReceived` /
`DatagramsUnblocked` events stay discarded — resource methods poll the
queues directly under the existing bounded-polling discipline, and
`recv-datagram` is the `accept-stream` pattern verbatim. The spike
guest's leg is event-driven instead: its scripted exchange grows an
unconditional datagram echo (client sends after the stream echo and
closes only once the echo returns), which turns its former
event-discard site into the handler.

Both demo sides send fixed copies of one datagram rather than
retrying: datagrams are lossy by contract, and duplication keeps the
exchange deterministic on lossless local wires without either side
cancelling a pending receive (an in-flight import subtask must
resolve — the jco discipline).

Conformance: the endpoint matrix rows assert the datagram echo over
the relay, the UDP direct path, and the WebRTC channel (`--datagram`
on the demo and both hosts' drivers; the jco endpoint driver carries
the flag for when #10 unblocks); the interop rows assert it against
upstream iroh in both directions — RFC 9221 wire compatibility against
the real implementation, not just ourselves; and the spike rows
exercise it under jco today. Full gate run: fmt, clippy, validate-wit,
test, probes, matrix (17/17), bench budgets all green.

Fixes #28
@lann
lann merged commit 73aef8a into main Aug 7, 2026
1 check passed
@lann
lann deleted the datagram-surface branch August 7, 2026 23:53
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.

Endpoint surface: expose QUIC datagrams as one-shot messages

1 participant