Skip to content

fix(olcrtc): validate profiles and recover sidecars - #157

Merged
hawkff merged 11 commits into
mainfrom
test/olcrtc-combined-ad5cc1e
Jul 14, 2026
Merged

fix(olcrtc): validate profiles and recover sidecars#157
hawkff merged 11 commits into
mainfrom
test/olcrtc-combined-ad5cc1e

Conversation

@hawkff

@hawkff hawkff commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Pin every olcRTC build path to hawkff/olcrtc@ad5cc1e3d60b657b15ccd26f8db91395bf9630d0.
  • Validate imported, edited, exported, and runtime profile fields against the supported carrier and transport matrix.
  • Supervise live olcRTC sidecars with bounded backoff, generation-owned readiness markers, listener checks, and cancellable restart handling. URL-test sidecars remain one-shot.
  • Include the wrapper regression tests in each sidecar cache key.

Verification

  • Namespace CI run 29337125648: guards, native provenance, lint, Spotless, unit tests, Room migrations, and signed OSS APK passed at 23b292d846e4803959f4621f0dbeb9fca766f06e.
  • CodeRabbit CLI reported no findings after the combined fixes.
  • Verified on Android with routed HTTPS before and after a forced sidecar exit, Stop during restart delay, a live profile reload, and a server restart. Two active clients also carried independent HTTPS requests with matching server events.
  • Post-test state: no test profiles or processes remain. The original profile configuration and selection match the baseline.

Scope

This verifies Jitsi datachannel. It does not claim VP8 or network-handoff coverage.

Greptile Summary

This PR tightens olcRTC build, validation, and sidecar recovery behavior. The main changes are:

  • Pins olcRTC workflow and local build paths to the maintained fork commit.
  • Adds shared validation for olcRTC carrier, transport, key, DNS, and VP8 fields.
  • Adds generation-owned readiness markers and supervised live sidecar restarts.
  • Keeps URL-test sidecars one-shot while adding regression tests for readiness and wrapper behavior.

Confidence Score: 4/5

This is close, but the URL-test timeout fix should be corrected before merging.

  • Strict olcRTC URL tests can still stop after 15 seconds on the default timeout.
  • Slow but valid carrier setup can still report sidecar listener not ready.
  • Live sidecar recovery and validation changes otherwise follow the intended flow in the reviewed areas.

app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt

Important Files Changed

Filename Overview
app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt Adds sidecar readiness helpers, but the olcRTC timeout helper still gives URL tests the short readiness window.
app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt Makes URL-test olcRTC sidecars one-shot by disabling recovery for test instances.
app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt Adds olcRTC readiness markers, listener polling, marker cleanup, and supervised live sidecar restart handling.

Comments Outside Diff (1)

  1. app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt, line 407-412 (link)

    P1 Strict Tests Time Out Early

    When an olcRTC URL test takes more than 15 seconds to publish its readiness marker, this branch gives up even though the sidecar was started with the longer olcRTC readiness window. A slow carrier join that would succeed within the 60-second sidecar timeout is reported as sidecar listener not ready, making URL tests fail incorrectly.

Reviews (3): Last reviewed commit: "style: format readiness timeout helper" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds guarded process restart policies and OLCRTC readiness markers, centralizes stricter OLCRTC profile validation, updates the OLCRTC wrapper lifecycle, and retargets build workflows to a new pinned repository revision.

Changes

Runtime restart and sidecar readiness

Layer / File(s) Summary
Restart policy and backoff
app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicy.kt, app/src/test/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicyTest.kt
Adds validated restart timing, capped exponential backoff, stable-readiness reset behavior, and restart decision tests.
Guarded process supervision
app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt
Refactors process watching, termination, restart preparation, readiness observation, delay calculation, and startup parameters.
OLCRTC sidecar readiness integration
app/src/main/java/io/nekohasekai/sagernet/bg/proto/*, app/src/test/java/io/nekohasekai/sagernet/bg/proto/*
Adds per-generation readiness markers, recovery-aware sidecar launching, readiness polling, shutdown cleanup, strict failure handling, and policy tests.

OLCRTC profile validation

Layer / File(s) Summary
Profile validation and parsing
app/src/main/java/io/nekohasekai/sagernet/fmt/olcrtc/OlcrtcFmt.kt
Centralizes carrier, transport, VP8, room, key, client ID, payload, DNS, IPv4, and IPv6 validation across parsing and argument generation.
Settings save validation
app/src/main/java/io/nekohasekai/sagernet/ui/profile/OlcrtcSettingsActivity.kt
Validates profiles during serialization and reports invalid input with a Toast during save.
Profile behavior coverage
app/src/test/java/io/nekohasekai/sagernet/fmt/OlcrtcFmtTest.kt
Expands tests for round trips, payloads, transports, VP8 values, DNS endpoints, URI constraints, and carrier-host extraction.

OLCRTC build integration

Layer / File(s) Summary
Wrapper readiness lifecycle
buildScript/lib/olcrtc-src/main.go, buildScript/lib/olcrtc-src/main_test.go
Adds readiness-marker flags, atomic publication and cleanup, readiness-aware shutdown, and unit tests.
Pinned source and wrapper verification
buildScript/lib/olcrtc.sh
Switches the default OLCRTC source pin and runs wrapper tests during staging.
Workflow dependency and cache inputs
.github/workflows/*, .depot/workflows/build-apk.yml
Updates OLCRTC repository and commit values and includes main_test.go in sidecar cache status hashing.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BoxInstance
  participant GuardedProcessPool
  participant olcrtc-socks
  participant ReadyMarker
  BoxInstance->>GuardedProcessPool: start OLCRTC with restart policy
  GuardedProcessPool->>olcrtc-socks: launch with ready-marker path
  olcrtc-socks->>ReadyMarker: publish ready marker after WaitReady
  BoxInstance->>ReadyMarker: poll marker and external port
  GuardedProcessPool->>olcrtc-socks: clear marker before restart
  olcrtc-socks->>ReadyMarker: republish marker after recovery
Loading

Possibly related PRs

Poem

A bunny watched the sidecar glow,
A marker bloomed: “It’s ready—go!”
Backoff hopped from slow to fast,
Fresh tests guard each launch at last.
New pins guide builds through the night—
OLCRTC shines bright. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main changes: OLCRTC profile validation and sidecar recovery.
Description check ✅ Passed The description is clearly related to the PR and matches the pinning, validation, supervision, and caching changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/src/main/java/io/nekohasekai/sagernet/fmt/olcrtc/OlcrtcFmt.kt (1)

140-158: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Unrecognized payload keys are silently dropped instead of rejected.

The when (key) block has no else branch, so a typo like vp8-fp=30 (missing hyphen) or any future/unsupported key silently falls through, leaving vp8Fps at its default with no error. Every other malformed-payload path in this file (missing =, empty key, non-integer vp8 values, delimiter-bearing client id) fails fast — this is the one case that fails silently.

♻️ Proposed fix
             payloadValues.forEach { (key, value) ->
                 when (key) {
                     "vp8-fps" -> vp8Fps = value.toIntOrNull()
                         ?: throw IllegalArgumentException("olcRTC: VP8 FPS must be an integer")
 
                     "vp8-batch" -> vp8BatchSize = value.toIntOrNull()
                         ?: throw IllegalArgumentException("olcRTC: VP8 batch size must be an integer")
 
                     // Our non-standard pairing-token carrier.
                     "cid", "client-id", "clientid" -> {
                         require(value.none { it in DELIMITERS }) {
                             "olcRTC: client id contains a reserved delimiter"
                         }
                         clientId = value
                     }
+
+                    else -> throw IllegalArgumentException("olcRTC: unrecognized transport payload key")
                 }
             }

If unknown keys are meant to be ignored on purpose (e.g. forward-compatibility with a newer wrapper), please confirm — otherwise this should fail fast like the rest of the parser.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/io/nekohasekai/sagernet/fmt/olcrtc/OlcrtcFmt.kt` around
lines 140 - 158, Add an `else` branch to the payload-key `when` inside the
parser so any key other than `vp8-fps`, `vp8-batch`, and the supported client-id
aliases throws an `IllegalArgumentException` with a clear unsupported-key
message before `validateOlcrtcProfile()` runs.
app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt (1)

312-329: 🩺 Stability & Availability | 🔵 Trivial

No permanent circuit breaker for olcRTC restarts.

With GuardedProcessRestartPolicy() and shouldFailAfterProcessExit bypassing the uptime fast-fail whenever a policy is present, a persistently-broken olcRTC carrier config will restart forever (capped at a 30s delay) for the life of the VPN connection, with no total-attempt/circuit-breaker limit. Worth considering a max-attempt or max-duration cutoff to bound background retries on a permanently misconfigured profile, though this isn't a regression versus the other sidecars' pre-existing no-backoff restart-forever behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt` around
lines 312 - 329, Bound olcRTC recovery retries by adding a circuit-breaker limit
to the GuardedProcessRestartPolicy path in the enableOlcrtcRecovery branch.
Track either total restart attempts or elapsed recovery duration and stop
restarting once the configured limit is reached, while preserving the existing
readiness wait and normal no-restart behavior when recovery is disabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt`:
- Around line 344-367: Update pendingExternalPorts to check the pool’s
active/liveness state inside its polling loop, before probing ports and while
delaying between iterations, and return promptly when the pool becomes inactive.
Preserve the existing timeout and pending-port behavior for active pools, and
ensure the caller’s pool-inactive handling remains effective during superseded
starts.

---

Nitpick comments:
In `@app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt`:
- Around line 312-329: Bound olcRTC recovery retries by adding a circuit-breaker
limit to the GuardedProcessRestartPolicy path in the enableOlcrtcRecovery
branch. Track either total restart attempts or elapsed recovery duration and
stop restarting once the configured limit is reached, while preserving the
existing readiness wait and normal no-restart behavior when recovery is
disabled.

In `@app/src/main/java/io/nekohasekai/sagernet/fmt/olcrtc/OlcrtcFmt.kt`:
- Around line 140-158: Add an `else` branch to the payload-key `when` inside the
parser so any key other than `vp8-fps`, `vp8-batch`, and the supported client-id
aliases throws an `IllegalArgumentException` with a clear unsupported-key
message before `validateOlcrtcProfile()` runs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 815710b8-879e-423c-b3c1-96fada2ff3cc

📥 Commits

Reviewing files that changed from the base of the PR and between bdda7ca and ab4a021.

📒 Files selected for processing (18)
  • .depot/workflows/build-apk.yml
  • .github/workflows/build.yml
  • .github/workflows/ci.yml
  • .github/workflows/preview.yml
  • .github/workflows/release.yml
  • app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicy.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicy.kt
  • app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt
  • app/src/main/java/io/nekohasekai/sagernet/fmt/olcrtc/OlcrtcFmt.kt
  • app/src/main/java/io/nekohasekai/sagernet/ui/profile/OlcrtcSettingsActivity.kt
  • app/src/test/java/io/nekohasekai/sagernet/bg/GuardedProcessRestartPolicyTest.kt
  • app/src/test/java/io/nekohasekai/sagernet/bg/proto/SidecarReadinessPolicyTest.kt
  • app/src/test/java/io/nekohasekai/sagernet/fmt/OlcrtcFmtTest.kt
  • buildScript/lib/olcrtc-src/main.go
  • buildScript/lib/olcrtc-src/main_test.go
  • buildScript/lib/olcrtc.sh

Comment thread app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
@hawkff

hawkff commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

Review follow-up

Commit 23b292d846e4803959f4621f0dbeb9fca766f06e includes the approved fixes:

  • The readiness poll now exits when its process pool becomes inactive, so superseded starts reach the existing inactive-pool handling without waiting for the full deadline.
  • One-shot URL tests and their sidecars now share max(15 seconds, configured timeout). Live recovery keeps max(60 seconds, configured timeout).
  • Focused tests cover both timeout modes and configured-timeout dominance.

Review decisions:

  • The 15-second one-shot floor remains intentional. A default 3-second connection test should not block for the live service's 60-second recovery window. The wrapper and outer poll now use the same deadline.
  • The parser continues to ignore syntactically valid unknown payload keys for forward compatibility. It still rejects malformed payload syntax and invalid recognized values.
  • The retry policy keeps lifetime recovery while the service is active. Its 30-second delay cap bounds retry load and allows recovery after an extended outage.

Namespace CI run 29337125648 passed at the final SHA. CodeRabbit CLI reported no findings on the semantic changes.

@hawkff
hawkff merged commit 2294fe6 into main Jul 14, 2026
9 checks passed
@hawkff
hawkff deleted the test/olcrtc-combined-ad5cc1e branch July 14, 2026 16:12
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