Skip to content

[REA-6846] Answer data-channel chunking with reactor-webrtc 0.19 - #228

Merged
douglaseel merged 4 commits into
mainfrom
douglas/rea-6846-reactor-runtime-enable-dc-chunking
Oct 5, 2026
Merged

douglaseel merged 4 commits into
mainfrom
douglas/rea-6846-reactor-runtime-enable-dc-chunking

Conversation

@douglaseel

@douglaseel douglaseel commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Why

A plain data channel takes messages of up to 256 KiB, and a send past libwebrtc's 16 MiB buffer closes the channel. The runtime has both limits in each direction: a client can't send a command or a frame larger than 256 KiB, and a result above 16 MiB can't be sent at all. reactor-webrtc 0.19 lifts both on channels where both peers declare a=x-reactor-dc-chunking in the SDP. Messages travel as frames of a few KiB, and up to 64 MiB passes in either direction. dcsctp's max_burst also goes from 4 to 256, so a 100 KB message takes about one round trip instead of five.

The runtime answers the offer, so it mirrors the attribute only for a client that declares it. A client that doesn't keeps the plain channel and its limits. That makes it safe to ship ahead of the SDKs that will ask for it.

What Changed

WebRtcConfig gains dc_chunking, on by default, following the WARP pattern:

WebRtcConfig()                    # chunking with any client that asks
WebRtcConfig(dc_chunking=False)   # plain channels only

Like SPED, it's process-wide: the media engine carries it and is built on the first connection, so the first connection's value is the one the process keeps. The field's documentation says so. A connection built with False on an engine that has chunking keeps plain channels.

The pin moves to reactor-webrtc==0.19.0. uv.lock follows once 0.19.0 is on PyPI. Until then, every job that installs from the lock fails (including the mise-lock script tests, which resolve the project).

Validated locally against a build of the 0.19 API:

  • Unit tests cover the config and the engine. The full unit suite passes apart from the mise-lock script tests, which pass with the 0.18 pin.
  • A new loopback test exchanges 1 MiB from the client and 20 MiB from the peer on a chunked channel. It fails without the engine change.
  • The integration suite passes, and ruff and ty are clean.

🤖 Generated with Claude Code

Copy link
Copy Markdown
Contributor Author

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

[codex-review] - [P1] pyproject.toml:45 — Unpublished dependency and stale uv.lock make clean installation fail.

Scope: full (5b062d7..238eb1e).

View workflow run.

Comment thread pyproject.toml Outdated

@Dere-Wah Dere-Wah left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving. I checked compatibility with older clients: the answer carries a=x-reactor-dc-chunking only when the offer declares it, so a client without chunking keeps the plain channel and its current limits. A newer client against a runtime without this change also stays plain, because libwebrtc drops the unknown line and the answer does not mirror it.

CI is red only because reactor-webrtc==0.19.0 is not on PyPI yet. It must be green, with uv.lock updated, before merge. Two non-blocking notes inline.

Comment thread src/reactor_runtime/transport/webrtc/config.py
Comment thread src/reactor_runtime/transport/webrtc/config.py
douglaseel and others added 3 commits October 5, 2026 11:46
A data-channel message is capped at 256 KiB on a plain channel, and a send
past libwebrtc's 16 MiB buffer closes the channel. reactor-webrtc 0.19 carries
larger messages as frames on channels whose peers both declare
a=x-reactor-dc-chunking. The runtime answers, so it mirrors that only for a
client that asks, and every other client keeps the plain channel.

WebRtcConfig gains dc_chunking, on by default. Like SPED it is process-wide:
the media engine carries it and is built on the first connection.

uv.lock moves to 0.19.0 once that release is on PyPI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Douglas Ferreira <douglaseel@gmail.com>
with_dc_chunking also raises SCTP's per-send burst for every data channel the
engine creates, so dc_chunking=False on one connection does not restore the
previous send timing. The field's documentation now says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Douglas Ferreira <douglaseel@gmail.com>
0.19.0 is on PyPI, so the lockfile resolves the pin and a locked install
works from public artifacts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Douglas Ferreira <douglaseel@gmail.com>
@douglaseel
douglaseel force-pushed the douglas/rea-6846-reactor-runtime-enable-dc-chunking branch from b55f0f0 to 20ccd70 Compare October 5, 2026 14:50
0.19.1 is the patch release whose source falls back to the p10 libwebrtc
prebuilt; the wheel the runtime installs already bundled it, so nothing
changes here beyond keeping in step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Douglas Ferreira <douglaseel@gmail.com>
douglaseel added a commit to reactor-team/reactor-client-sdks that referenced this pull request Oct 5, 2026
## Why

A command is capped at 256 KiB, the most a plain data channel takes, and fails with `MESSAGE_TOO_LARGE` above that. reactor-webrtc 0.19 carries messages of up to 64 MiB as frames on channels where both peers declare `a=x-reactor-dc-chunking` in the SDP. It also raises dcsctp's `max_burst` from 4 to 256, so a 100 KB message takes about one round trip instead of five. The SDK sends the offer, so it's the side that has to ask. reactor-runtime answers as of reactor-team/reactor-runtime#228, and a runtime that doesn't answer keeps the plain channel and today's limit.

## What Changed

- The workspace moves to reactor-webrtc 0.19. The FFI builds its factory with `with_dc_chunking(DcChunking::default())`, and `offer_config` asks for chunking alongside SNAP.
- `max_message_bytes()` follows the negotiation. Once the data channel is chunked it reports this side's chunked limit (64 MiB); before the channel opens, or against a runtime without chunking, it reports the plain 256 KiB. The core still checks a command against it before sending.
- Only the channel knows the runtime's advertised limit. A command above that is refused by `send()` with `DcSendError::TooLarge`, which now maps to the same `MessageTooLarge` rather than a generic transport error. A full send queue stays `TRANSPORT_ERROR`, because it clears as the queue drains, and a new code would mean a new error class in every SDK.
- The two `let mut` channel bindings lose their `mut`, since 0.19's setters take `&self`.
- CHANGELOG entries for C++, Python, Swift and Java. JS reaches WebRTC through reactor-wasm and the browser, which gets its own implementation in the next PR of this stack.

There are no C ABI changes, and no SDK's public API changes. The only visible difference: against a runtime that negotiates chunking, a command above 256 KiB is sent.

Channel close does not drain. A command's request waits for the runtime's reply, so a completed command has fully arrived; a close mid-request already aborts the request.

## Validation

All local, against a build of the reactor-webrtc 0.19 stack (patched in through an uncommitted `[patch.crates-io]`):

- New loopback tests in `reactor-ffi`. The transport offers and a reactor-webrtc peer stands in for the runtime:
  - against a chunking runtime, `max_message_bytes()` reports 64 MiB and a 1 MiB command arrives whole;
  - against a plain runtime, the limit stays 256 KiB.
  - Both passed in 3 out of 3 runs.
- Unit tests cover the offer, the error mapping, and the limit before a connection exists.
- `cargo test -p reactor-ffi -p reactor-core` passes (59 + 131), and `cargo clippy --all-targets -D warnings` is clean.

`Cargo.lock` moves to 0.19 once it's on crates.io. Until then CI can't resolve the pin.

Linear: [REA-6847](https://linear.app/reactor-team/issue/REA-6847)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

douglaseel commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Oct 5, 4:43 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Oct 5, 4:44 PM UTC: @douglaseel merged this pull request with Graphite.

@douglaseel
douglaseel merged commit cf23558 into main Oct 5, 2026
10 checks passed
@douglaseel
douglaseel deleted the douglas/rea-6846-reactor-runtime-enable-dc-chunking branch October 5, 2026 16:44
douglaseel added a commit that referenced this pull request Oct 5, 2026
…#240)

## Why

Releases what has landed since 3.6.1. It adds new public surface and moves nothing on the wire, which makes it a minor release:

- **Data-channel chunking** (#228–#230). `WebRtcConfig.dc_chunking` is on by default. The runtime answers clients that ask for chunked data channels, so a command or a model message of up to 64 MiB passes, instead of being capped at 256 KiB. Refused frames are logged once per channel, and a teardown sends what a chunked channel still has queued. A client that doesn't ask keeps the plain channel. 
- **Step sessions** (#231–#239): a session's starting input and step count on the start body, `complete_step()`, closing a session at its steps, the `runtime.step_results` manifest block, and step output saved as an MP4 or a folder of files, served over HTTP.
- **Client stats** (#201, #224): client-observed WebRTC stats decoded in the transport and journaled as metric self-loops.
- **reactor-webrtc 0.21.0** (from 0.19.1).
  - 0.21.0 keeps per-frame metadata on its own frame when libwebrtc drops a frame before render. Before, metadata was matched by queue order, so after a dropped frame every later frame got its predecessor's metadata.
  - It also keeps a `FrameTransform`'s `replace_data` through the metadata step.
  - 0.20.0 is additive.
  - Neither changes the Python API the runtime uses.
- Dependency bumps (#216–#220).

## What Changed

Two commits:
- `uv version 3.7.0`, which updates the version in `pyproject.toml` and `uv.lock`;
- `reactor-webrtc==0.21.0`, relocked. Only that package moves in `uv.lock`.

Merging tags `v3.7.0` and publishes.

Checked locally with a locked install of the published reactor-webrtc 0.21.0 wheel:
- lint, typecheck and the HTTP spec check pass;
- unit tests pass (1917);
- integration tests pass (12).

And with the release workflow's own gates:
- `mise run http-breaking-release`: "3.6.1 -> 3.7.0 (minor) satisfies the mandated patch";
- `mise run wire-check-release`: the pin matches `proto/`;
- `mise run build`: builds the 3.7.0 wheel and sdist.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

2 participants