Skip to content

refactor(runtime-rpc): extract the Node WebSocket lifecycle; opt-in pinned port - #24186

Merged
OrcaWin merged 2 commits into
mainfrom
OrcaWin/node-rt-p3-t1d
Oct 1, 2026
Merged

OrcaWin merged 2 commits into
mainfrom
OrcaWin/node-rt-p3-t1d

Conversation

@OrcaWin

@OrcaWin OrcaWin commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator
Files Added Deleted Net
Test 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​121 $\color{#cf222e}{\Huge{\mathbf{−}}}$​3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​118
Prod 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​218 $\color{#cf222e}{\Huge{\mathbf{−}}}$​143 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​75

ELI5

This splits the runtime's WebSocket server into small, testable pieces taken from #16741: the per-connection lifecycle, and the connection and message limits. It also adds an option to require an exact port, which the headless server needs for --port. All Bun WebSocket code from #16741 is dropped.

What Changed

  • node-websocket-lifecycle.ts: handles each connection, covering the pre-auth timer, heartbeat, finalize on close, over-capacity rejection (1013) and shutdown.
  • websocket-transport-limits.ts: holds the message, connection and TCP caps.
  • ws-transport.ts: now a thin class, down from 355 to 260 lines. It adds an opt-in strictPort.
  • requirePinnedWsPort (runtime RPC state and lifecycle): binds only the configured port. On a bind failure it stops the Unix socket transport and rethrows.
  • Adapted:
    • Handlers are read through getters, so a handler registered after start still reaches existing sockets.
    • Binary frames are converted with a checked branch.
    • The rationale comments are kept.
  • Wire: no changes.

Why

Design D9, T1 and D9.4: keep the Node WebSocket path and drop Bun's. The headless server (T6) will set requirePinnedWsPort for --port.

Linked Issue

Part of the Node runtime plan (Phase 3). Stacked on #24181.

Visual Proof

N/A. There's no behavior change; the new option is opt-in and unused so far.

Testing

  • Ported tests pass.

  • Added in review: a test that a failed required-port start releases the Unix socket.

  • Not yet ported: transportGeneration plumbing (read only by T3 and T7) and the remaining T1 renderer and test-server pieces.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[High risk] Refactors WebSocket lifecycle and adds port-binding constraints.

The PR does not appear safe to merge while a failed required-port start can retain stale mobile socket wiring for a retry.

Findings

  1. P1 Failed start retains old identity ▶
  2. P2 Shutdown cuts off asset responses ▶

Summary

The PR extracts the Node WebSocket connection lifecycle and transport limits, adds an opt-in required-port bind, and introduces relay work-drain and producer-publication primitives.

  • Required-port bind failure now stops the Unix socket transport.
  • Relay request contexts gain transport generations, and producer notifications gain write-settlement callbacks.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Runtime[Runtime RPC startup] --> Unix[Unix socket]
  Runtime --> WS[Node WebSocket transport]
  WS --> Lifecycle[Connection lifecycle]
  WS --> Limits[Transport limits]
  Runtime -->|required port bind fails| Cleanup[Stop Unix socket and fail startup]
  Relay[Relay dispatcher] --> Admission[Work admission and drain]
  Relay --> Publication[Producer publication]
  Publication --> Writer[Writer settlement]
Loading

Reviews (3) · Last reviewed commit: "test(runtime-rpc): require a failed pinn..."

Comment thread src/main/runtime/runtime-rpc/runtime-rpc-lifecycle.ts
Comment on lines +103 to +105
if (this.requirePinnedWsPort) {
await socketTransport.stop().catch(() => {})
throw error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Failed start retains old identity

If a caller retries start() on the same server after a required-port bind failure, this branch has left mobileSocketWiring in place. The retry creates a fresh pairing identity but reuses wiring that holds the previous registry and keypair. If device or key state changed between attempts, the new listener can authenticate against stale identity state. Clear the wiring when this start fails.

Knowledge Base Used: Remote runtime transport

resolve()
})
// Why: idle keep-alive static-web connections would otherwise hold close() open.
httpServer.closeAllConnections()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Shutdown cuts off asset responses

closeAllConnections() closes active HTTP connections, not just idle keep-alive connections. This server also streams web-client assets, so stopping or rebinding it during an asset response now cuts off that download and forces the browser to retry. Consider letting active responses finish within a bounded drain while closing idle connections promptly.

Knowledge Base Used: Remote runtime transport

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@pullfrog pullfrog Bot 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.

ℹ️ No critical issues — the extraction is faithful. One behavior note inline and a small coverage gap below.

Reviewed changes

  • Extract node-websocket-lifecycle.ts: per-connection lifecycle (attachNodeWebSocketLifecycle), shutdown (stopNodeWebSocketTransport), pre-auth timer clearing, and over-capacity rejection move here. Handlers are read through lazy getters, so a handler registered after start still reaches earlier sockets. The logic is equivalent to the removed handleConnection/stop bodies.
  • Extract websocket-transport-limits.ts: message/connection/TCP caps become named exports (1 MiB, 128, 256); values unchanged.
  • Opt-in strictPort on WebSocketTransport: binds only the configured port and rethrows on failure instead of falling through to the persisted fallback or port 0.
  • requirePinnedWsPort server option: threads strictPort, skips the fallback port, and stops the already-started Unix socket transport before rethrowing so a failed start leaves no orphaned endpoint. Unused in production so far; orcad/serve are the intended consumers.
  • Tests: a failed required-port start releases its Unix socket; stop() while an HTTP request is unanswered; strictPort fails closed.

ℹ️ Nitpicks

  • The new strictPort/requirePinnedWsPort success path has no positive test — only the failure case and the non-required fallback case are covered. A test that starts a transport on a free configured port and asserts resolvedPort === port would pin the main behavior of the new option.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

resolve()
})
// Why: idle keep-alive static-web connections would otherwise hold close() open.
httpServer.closeAllConnections()

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.

stop() now hard-closes in-flight HTTP connections via closeAllConnections(); the implementation this was extracted from only called close() and waited. That is a real behavior change (a useful one — it removes a way for a keep-alive static-web request to wedge shutdown, or the widen rebind that calls current.stop()), but the PR description says there is no behavior change. Worth calling out in the description since it is bundled into the extraction.

m4air added 2 commits October 1, 2026 07:05
…-port bind mode

Port the #16741 T1 transport pieces onto the current Node-only transport.
The per-socket lifecycle (pre-auth timer, heartbeat enrolment, finalize on
close/error), over-capacity rejection and shutdown move out of
ws-transport.ts into node-websocket-lifecycle.ts, and the size/connection
caps into websocket-transport-limits.ts. WebSocketTransport gains an opt-in
strictPort that binds only the configured port and fails closed instead of
walking the fallback ladder; OrcaRuntimeRpcServer exposes it as
requirePinnedWsPort, which also skips the persisted fallback port and stops
the Unix socket transport before rethrowing, so a caller whose tunnel
forwards one fixed port never runs on a port nobody can reach.

Porting note (#16741 a68b6f3, T1):
- Taken: node-websocket-lifecycle.ts, websocket-transport-limits.ts,
  WebSocketTransport strictPort, the ws-transport strict-port test,
  runtime-rpc-state/-pairing-types/-lifecycle requirePinnedWsPort.
- Adapted: the lifecycle reads the message/close handlers through getters
  (the PR snapshotted them per connection) so a handler registered after
  start still reaches earlier sockets; binary frames convert through a
  checked RawData branch instead of `as Buffer`; the original Why comments
  move with the code; stop() keeps a single Node path (no AggregateError)
  and keeps the PR's closeAllConnections(), now covered by a test that an
  unanswered HTTP request no longer holds stop() open; the over-capacity
  test calls the extracted function instead of casting to a private.
- Dropped: every bun-websocket-* file and the Bun branches in
  ws-transport.ts (D9.4); WEBSOCKET_TRANSPORT_MAX_BACKPRESSURE_BYTES (only
  the Bun transport read it); the relaxed EACCES `port`-field check in
  isPortListenFallbackError (it covered Bun's listen errors, Node's carry
  the port).
- Not here: the orcad-entry wiring that sets requirePinnedWsPort for
  `--port` (T6, managed SSH deploy); the transportGeneration context
  plumbing (its only readers are T3/T7); holdIdleRuntimeEnvironmentCalls
  (its only caller is the T5 reconciliation coordinator).

Wire compatibility: none. The bind policy is server-local, the option
defaults off, and no RPC field, method or frame changes.
…Unix socket

The required-port startup test only checked the WebSocket side. Also assert no
runtime socket file remains, which fails if the lifecycle stops rethrowing
without first stopping the Unix socket transport.

Porting note (#16741 a68b6f3, T1): review follow-up; taken: none;
adapted: strengthens the ported required-port test; dropped: none.
@OrcaWin
OrcaWin force-pushed the OrcaWin/node-rt-p3-t1d branch from 9705c35 to d18d20d Compare October 1, 2026 14:05
@OrcaWin
OrcaWin changed the base branch from OrcaWin/node-rt-p3-t1b to main October 1, 2026 14:06
@OrcaWin OrcaWin closed this Oct 1, 2026
@OrcaWin OrcaWin reopened this Oct 1, 2026
@coderabbitai

coderabbitai Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (1)
AGENTS.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: c5f4a2fa-773d-4255-a379-47ae63371160

📥 Commits

Reviewing files that changed from the base of the PR and between 0b812bd and d18d20d.

📒 Files selected for processing (8)
  • src/main/runtime/rpc/node-websocket-lifecycle.ts
  • src/main/runtime/rpc/websocket-transport-limits.ts
  • src/main/runtime/rpc/ws-transport.test.ts
  • src/main/runtime/rpc/ws-transport.ts
  • src/main/runtime/runtime-rpc-required-ws-port.test.ts
  • src/main/runtime/runtime-rpc/runtime-rpc-lifecycle.ts
  • src/main/runtime/runtime-rpc/runtime-rpc-pairing-types.ts
  • src/main/runtime/runtime-rpc/runtime-rpc-state.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change extracts Node WebSocket lifecycle handling into shared helpers and defines shared message, WebSocket connection, and TCP connection limits. It adds strict WebSocket port binding and a runtime RPC option that requires the configured port. When required-port startup fails, the runtime stops the Unix socket transport and propagates the error. Tests cover transport shutdown, strict binding, and fallback behavior when a pinned port is optional.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to d18d2

The pinned-port option is opt-in, while existing fallback behavior remains available. Required-port failures clean up the Unix transport and preserve the bind error. The change is mergeable subject to normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to d18d2

The opt-in setting prevents fallback to an unexpected port, but startup can still succeed without the required listener when identity setup fails. The inspected changes preserve authentication controls; deployment and recovery coverage remain incomplete.

Retained concerns

  • Low · reliability · inferred: Required-port mode can report successful startup with only a Unix listener when pairing identity initialization fails. The new bind-failure cleanup branch does not cover this earlier failure, weakening the intended all-or-nothing readiness contract for managed endpoints. Authentication remains unavailable rather than bypassed; this is a failure-containment concern, not a verified security vulnerability.
Security review details

Security Blast Radius

  • inferred — The demonstrated attackable surface is the runtime listener and its connection resources on the selected interfaces. Unauthenticated clients can submit frames to the E2EE parser and consume bounded connection capacity. Broader tenant, datastore or environment authority is not established by the supplied evidence.

Security Findings and Attack Paths

  • observed — The inspected frame path still enters E2EEChannel before application dispatch. Text and binary application callbacks require an authenticated socket entry, and identity assignment clears the pre-auth timer. The extraction does not bypass these inspected controls.

Trust Boundaries and Controls

  • observed — Device identity remains resolved through registry token validation. For relay connections, the resolved device must also match the relay device identity; transport attachment does not itself grant authenticated application access.

Resilience and Maintainability Implications

  • inferred — An existing start/stop race can let shutdown snapshot empty transport state while startup awaits binding, then let startup publish listeners afterward. Base/head comparison shows the relevant ownership sequencing predates this PR; changed exposure was not established. Same-instance restart semantics remain unresolved.

Hardening Proposals

  • proposed — Define required-listener readiness across identity initialization and binding failures, and serialize startup with shutdown so every pending listener remains covered by its cleanup owner. Explicitly define whether a stopped server instance may restart.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main changes: extracting the Node WebSocket lifecycle and adding opt-in pinned-port behavior.
Description check ✅ Passed The description covers the main template sections, including the plain-language summary, changes, rationale, issue context, visual proof, and testing. It also explains deferred work and includes the a…
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.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@OrcaWin
OrcaWin merged commit a789233 into main Oct 1, 2026
52 of 74 checks passed
OrcaWin added a commit that referenced this pull request Oct 2, 2026
* Revert "feat(orcad): source-side dormant export of a relay-hosted SSH target (#16741 T6-8) (#24519)"

This reverts commit 783101b.

* Revert "feat(ssh): update, roll back, recover and stop a managed orcad server (#16741 T6-5 follow-up) (#24463)"

This reverts commit 38c2d1d.

* Revert "feat(ssh): deploy and pair an empty managed orcad server over SSH (#16741 T6-5) (#24453)"

This reverts commit 8b76683.

* Revert "fix(ssh): orcad GC honors the activation journal; readiness requires proven daemon coverage (#16741 T6 follow-up) (#24451)"

This reverts commit d3f8c50.

* Revert "feat(ssh): remote orcad stop by request file and journaled decommission (#16741 T6-4) (#24449)"

This reverts commit 43d9b43.

* Revert "feat(orcad): supervisable server: stop requests, managed stop receipts and a lifetime that keeps its lock on failed teardown (#16741 T6-3) (#24433)"

This reverts commit b093d3a.

* Revert "feat(ssh): crash-safe orcad activation, rollback and recovery (#16741 T6-2) (#24423)"

This reverts commit 1a9ac0e.

* Revert "feat(runtime): SSH access links for paired servers in a downgrade-safe sidecar (#16741 T5-1+T5-2) (#24420)"

This reverts commit 99db2bf.

* Revert "feat(relay): capability-gated owner reset with a durable preparation journal (#16741 T3 R1) (#24418)"

This reverts commit 34a582b.

* Revert "feat(ssh): track connection-manager drains, test probes and provider continuations (#16741 T2 P3+P8a) (#24407)"

This reverts commit d53063d.

* Revert "feat(daemon): idle retirement, session census and recovery-only provider (#16741 T2 P4b) (#24409)"

This reverts commit ff212db.

* Revert "feat(ssh): add pty.resumeClient and split SSH PTY process listing (#16741 T2 P5+P6) (#24414)"

This reverts commit 92cb717.

* Revert "feat(relay): await owned watcher and agent children on shutdown (#16741 T2 P1) (#24400)"

This reverts commit 6b36e4f.

* Revert "feat(session): retry failed renderer session writes and verify local folder PTYs (#16741 T2 P9) (#24406)"

This reverts commit d23ecef.

* Revert "feat(ssh): remote orcad primitives on the pinned Node runtime (#16741 T6-1) (#24419)"

This reverts commit dd87ae5.

* Revert "fix(runtime): fence runtime-environment subscriptions and status probes by identity (#16741 T5-3) (#24421)"

This reverts commit ece9e4d.

* Revert "feat(orcad): migration manifest and dormant-state contracts (#16741 T6-7) (#24422)"

This reverts commit 3fbdaba.

* Revert "feat(ssh): wire SshConnection through the work and transport close ledgers (#16741 T2 P2) (#24401)"

This reverts commit 4e8edc8.

* Revert "feat(profiles): carry markdown frontmatter visibility in project transfers (#16741 T2 P7) (#24405)"

This reverts commit 60c9326.

* Revert "fix(runtime): project the PTY incarnation onto mobile session tabs (#24413)"

This reverts commit 99e0303.

* Revert "feat(daemon): tag daemon stream data with the PTY incarnation id (#16741 T2 P4a) (#24402)"

This reverts commit 817af76.

* Revert "feat(ssh): port the SSH connection work ledger and transport close ledger (#16741 T2) (#24210)"

This reverts commit c991893.

* Revert "feat(relay): fence and drain file and git response streams on shutdown (#24185)"

This reverts commit dc08ffe.

* Revert "refactor(runtime-rpc): extract the Node WebSocket lifecycle; opt-in pinned port (#24186)"

This reverts commit a789233.

* Revert "feat(relay): route relay handlers through work admission; producer publication drain (#24181)"

This reverts commit 0b812bd.

* Revert "feat(relay): land the #16741 T1 seam (work drain, publication drain, release gate) (#24156)"

This reverts commit 3aa2d3a.

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
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