Skip to content

feat(container-runner): add WebRTC Unity example - #5480

Draft
NathanFlurry wants to merge 1 commit into
mainfrom
agent/unity-webrtc-example
Draft

feat(container-runner): add WebRTC Unity example#5480
NathanFlurry wants to merge 1 commit into
mainfrom
agent/unity-webrtc-example

Conversation

@NathanFlurry

@NathanFlurry NathanFlurry commented Jul 24, 2026

Copy link
Copy Markdown
Member
  • Add a native Unity WebRTC example based on the existing FishNet Unity demo.
  • Add reliable and unreliable Unity.WebRTC data channels with Rivet gateway HTTP signaling under /request/.
  • Run native Unity peer negotiation on the Unity main loop so SetRemoteDescription, answer creation, and ICE gathering progress reliably in desktop and headless clients.
  • Default to Google and Cloudflare STUN, with runtime ICE overrides through environment variables and native client flags; no credentials are committed.
  • Resolve RTCTransportStats.selectedCandidatePairId and fail closed unless the active pair is nominated, succeeded, bidirectional, UDP, and uses only host/server-reflexive/peer-reflexive candidates.
  • Package the Linux dedicated server with Container Runner v2.3.3, the stable Cloud-compatible release identified in #rivet-triumph.
  • Document native-client, local-build, and Rivet Compute deployment workflows.

Validation:

  • Rebased on current main (6479a185).
  • Built the macOS native client and Linux dedicated server with Unity 6000.5.2f1.
  • Local native E2E passed with only stun:stun.l.google.com:19302 and stun:stun.cloudflare.com:3478; both peers logged TURN disabled.
  • The authoritative local selected pair was host/udphost/udp, nominated and succeeded, with bytesSent=1263 and bytesReceived=1770; the client logged ICE PROOF PASSED: native UDP P2P with no TURN relay.
  • Final linux/amd64 Docker build passed and contains Container Runner 2.3.3.
  • Fresh Rivet Cloud project: https://dashboard.rivet.dev/orgs/c24accdf-e245-45e7-9064-793c15bfec88/projects/compute-webrtc-t-mzlg/ns/production-ddyk?skipOnboarding=1
  • Deployed the audited STUN-only build with the ICE credential variables explicitly cleared.
  • Final PR-head image: pr5480-778c3669-stun-final (sha256:ac3faf5a4d43d94fab96fbe1033f257f928e8420081885873510cd7941abb15a).
  • Final PR-head Cloud actor p6963d1q1fhlqpiepx2jbn2d8ncl00 reproduced the result: both peers logged TURN disabled and gathered server-reflexive UDP candidates, then both transitioned from ICE Checking to Failed.
  • Fresh Cloud actor pqovsp0wbgshcifjp5p6lzxozjcl00: both peers gathered host and server-reflexive candidates with TURN disabled, but ICE transitioned from Checking to Failed; the client correctly logged ICE PROOF FAILED.
  • Diagnostic Cloud actor xcfpz8970r7fv226htk0mey4dxal00 reproduced the failure. The worker's public STUN mappings were not reachable by peer ICE checks, so a no-TURN Cloud path was not established from this native client/network.
  • A prior Cloud run succeeded only with authenticated TURN relay candidates; it is not evidence of native P2P.

Deployment note:

  • Use --drain-on-version-upgrade=false with Container Runner v2.3.3 for this project.
  • Native P2P works when the two peers' ICE candidates are directly reachable. This Rivet Cloud worker path currently requires TURN or a Cloud networking change that exposes a reachable UDP candidate.

@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review: WebRTC Unity Example (#5480)

This PR vendors a large third-party Unity project (FishNet + a forked FishyWebRTC transport) plus adds ~5 Rivet-authored C#/shell/Dockerfile files. I focused review on the non-vendored, Rivet-authored code and the docs/config changes; I did not line-by-line review the ~1,800 vendored FishNet/FishyWebRTC files themselves.

🚩 Unrelated, high-risk CLAUDE.md change

The diff adds this line to the top of CLAUDE.md, unrelated to the rest of the PR (a Unity WebRTC example):

+For all RivetKit work, use [https://rivet.dev/llms.txt](https://rivet.dev/llms.txt) as the current documentation index and reference.

CLAUDE.md instructions are treated by agents as overriding default behavior. A line that directs every future agent to fetch and treat an external, mutable URL's content "as the current documentation index and reference" is a real prompt-injection surface: whoever controls rivet.dev/llms.txt content going forward can silently steer every future agent session in this repo. Even setting aside injection risk, this is scope creep — it doesn't belong in a PR about a Unity WebRTC example, and per this file's own conventions section, CLAUDE.md additions should be "design constraints, invariants, and non-obvious rules," not a pointer to an external doc index. Recommend dropping this line from this PR (or, if genuinely wanted, landing it separately with its own review).

Correctness: blocking synchronous HTTP on the Unity main thread

Packages/com.skillcade.fishy-webrtc/Runtime/SimpleWebRTC/Client/SimpleWebRTCClientNative.cs, ConnectCoroutine calls SendHttp(...) (a synchronous HttpWebRequest.GetResponse() with up to a 30s timeout) directly inside a coroutine, twice: once for offer/ and once for answer/. Coroutines don't run on a separate thread — a coroutine only yields at explicit yield return, so this blocking call stalls Unity's entire main thread (rendering, physics, input) for however long the HTTP round trip takes, and the signaling code path explicitly waits up to 10s server-side for ICE gathering before responding, so this can be a multi-second full freeze. This directly undercuts the PR's stated goal of running "native Unity peer negotiation on the Unity main loop ... reliably." Consider UnityWebRequest (which is coroutine/async-friendly) or moving SendHttp onto a background thread with results marshaled back via the coroutine.

Docs vs. validated behavior mismatch

container-runner/examples/unity-webrtc-demo/README.md states under "ICE networking": "No TURN URL or credentials are required for the native P2P path." But this PR's own validation notes in the PR description show the opposite on Rivet Cloud: the STUN-only build failed ICE (CheckingFailed) against a fresh Cloud actor, and only succeeded with authenticated TURN relay candidates. The README should reflect this (e.g., "STUN-only P2P works locally; Rivet Cloud currently requires TURN or a networking change — see PR discussion") so a reader deploying to Cloud doesn't hit a confusing failure the author already reproduced and diagnosed.

Supply-chain: unverified runner binary download

container-runner/examples/unity-webrtc-demo/Dockerfile diverges from the sibling unity-demo/Dockerfile, which builds rivet-container-runner from source in-workspace. This Dockerfile instead does:

RUN apk add --no-cache ca-certificates curl \
    && curl -fsSL https://releases.rivet.dev/rivet/2.3.3/container-runner/rivet-container-runner-x86_64-unknown-linux-musl \
        -o /rivet-container-runner \
    && chmod +x /rivet-container-runner

This downloads and directly executes (as ENTRYPOINT) a prebuilt binary with no checksum/signature verification. Given releases.rivet.dev is first-party, risk is limited, but it's still a divergence from the from-source pattern used elsewhere in the same directory, worth at least a one-line comment explaining why this example uses the prebuilt release instead of building from source (e.g. to avoid needing the full Rust workspace for a Unity-only example), and ideally a checksum pin alongside the version pin.

Licensing: vendored fork missing LICENSE file

Packages/com.skillcade.fishy-webrtc/ is a fork of cakeslice/FishyWebRTC (MIT, per its own README prose) vendored at a pinned commit. Unlike the neighboring com.firstgeargames.fishnet/ package, which ships LICENSE.txt copies, this package includes no LICENSE file anywhere in the added file set — only a one-line "Based on ... MIT License" mention inside README.md. Worth adding the actual MIT license text as a LICENSE file for compliance, matching how FishNet is vendored.

Minor / informational

  • Runtime/SimpleWebRTC/Server/WebRTCServer.cs assigns connection IDs as a small sequential ushort counter with no secret component, and SendAnswerThread accepts an /answer/ POST for any connId with no additional binding to the original /offer/ requester. In this deployment this is protected by Rivet's gateway token in the URL path before traffic ever reaches the actor, so severity is low, but it's worth a one-line note in the README about that trust assumption for anyone reusing this signaling server outside Rivet's gateway.
  • The vendored package's own README.md (com.skillcade.fishy-webrtc/README.md) is unmodified upstream content full of Edgegap/WSL2-specific deployment instructions unrelated to Rivet. Not a blocker since it's vendored as-is, but readers browsing Packages/ could be misled into thinking Edgegap-specific env vars (ARBITRIUM_PUBLIC_IP, etc.) apply here.
  • RedactToken in ServerBootstrap.cs is a nice touch — the gateway URL's <token> is correctly scrubbed before it's logged (Debug.Log($"[bootstrap] starting WebRTC CLIENT -> ...")), avoiding credential leakage into build/Cloud logs.

Not reviewed in depth

The ~1,800 vendored FishNet and FishyWebRTC files (code-gen helpers, editor tooling, WebGL client/JS interop, etc.) were not reviewed line-by-line, consistent with them being third-party code pulled in wholesale rather than authored for this PR.

@railway-app

railway-app Bot commented Jul 24, 2026

Copy link
Copy Markdown

🚅 Deployed to the rivet-pr-5480 environment in rivet-frontend

Service Status Web Updated (UTC)
frontend-inspector 😴 Sleeping (View Logs) Web Jul 24, 2026 at 3:06 am
frontend-cloud 😴 Sleeping (View Logs) Web Jul 24, 2026 at 3:03 am
website 😴 Sleeping (View Logs) Web Jul 24, 2026 at 3:02 am
kitchen-sink 😴 Sleeping (View Logs) Web Jul 24, 2026 at 3:01 am
ladle ✅ Success (View Logs) Web Jul 24, 2026 at 2:54 am
mcp-hub ✅ Success (View Logs) Web Jul 24, 2026 at 2:54 am

@NathanFlurry
NathanFlurry force-pushed the agent/unity-webrtc-example branch 4 times, most recently from 9c69456 to 34738fc Compare July 24, 2026 08:17
@NathanFlurry
NathanFlurry force-pushed the agent/unity-webrtc-example branch from 34738fc to 778c366 Compare July 24, 2026 08:23
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