Skip to content

Extract the session SocketRegistry port - #1513

Closed
ColeMurray wants to merge 4 commits into
mainfrom
col-50-socket-registry-port
Closed

Extract the session SocketRegistry port#1513
ColeMurray wants to merge 4 commits into
mainfrom
col-50-socket-registry-port

Conversation

@ColeMurray

@ColeMurray ColeMurray commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • define a runtime-neutral SocketRegistry<Connection> contract for session socket lifecycle and transport operations
  • move Cloudflare WebSocket acceptance, tag lookup, auto ping/pong configuration, upgrade pair creation, and hibernation recovery into DurableObjectSocketRegistry
  • update message queue and sandbox event processing to depend only on the new port while retaining D1 client identity recovery in the Durable Object implementation
  • preserve sandbox identity validation and existing wire behavior

Verification

  • npm run typecheck -w @open-inspect/control-plane
  • npm run lint -w @open-inspect/control-plane
  • npm run build -w @open-inspect/control-plane
  • npm test -w @open-inspect/control-plane (2,742 tests)
  • npm run test:integration -w @open-inspect/control-plane (861 tests)

Closes COL-50.


Created with Open-Inspect

Summary by CodeRabbit

  • Refactor

    • Improved session WebSocket handling with a dedicated socket registry.
    • Updated session connections, message queues, and event processing to support flexible socket implementations.
    • Added streamlined socket creation, auto-ping configuration, registration, messaging, and participant management.
  • Tests

    • Updated session tests to cover the new socket registry and connection behavior, including automatic ping responses.

@github-actions

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate
Tests

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@open-inspect[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 53 seconds

Limit details: You’ve used all 8 included reviews currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ae061517-3f76-416d-adb9-382102b3bb3a

📥 Commits

Reviewing files that changed from the base of the PR and between 0aae210 and 020102e.

📒 Files selected for processing (15)
  • packages/control-plane/src/cloudflare/durable-object-session-connections.test.ts
  • packages/control-plane/src/cloudflare/durable-object-session-connections.ts
  • packages/control-plane/src/cloudflare/durable-object-socket-registry.test.ts
  • packages/control-plane/src/cloudflare/durable-object-socket-registry.ts
  • packages/control-plane/src/session/connections.ts
  • packages/control-plane/src/session/durable-object-session-connections.test.ts
  • packages/control-plane/src/session/durable-object-session-connections.ts
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/session/message-queue.test.ts
  • packages/control-plane/src/session/message-queue.ts
  • packages/control-plane/src/session/messenger.test.ts
  • packages/control-plane/src/session/messenger.ts
  • packages/control-plane/src/session/ports.ts
  • packages/control-plane/src/session/sandbox-events.test.ts
  • packages/control-plane/src/session/sandbox-events.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c801b5bc-1169-4098-ad43-0a13068984c0

📥 Commits

Reviewing files that changed from the base of the PR and between 33b85b5 and 0aae210.

📒 Files selected for processing (9)
  • packages/control-plane/src/session/durable-object-session-connections.test.ts
  • packages/control-plane/src/session/durable-object-session-connections.ts
  • packages/control-plane/src/session/durable-object-socket-registry.ts
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/session/message-queue.test.ts
  • packages/control-plane/src/session/message-queue.ts
  • packages/control-plane/src/session/sandbox-events.test.ts
  • packages/control-plane/src/session/sandbox-events.ts
  • packages/control-plane/src/session/server.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/control-plane/src/session/sandbox-events.test.ts
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/session/durable-object-session-connections.test.ts
  • packages/control-plane/src/session/message-queue.test.ts

Included review availability: Your plan includes up to 8 reviews per rolling hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The session WebSocket manager is replaced by DurableObjectSocketRegistry. Session connections, message queues, and sandbox event processing now use explicit socket interfaces and generic connection types. Sandbox socket scans use the "sandbox" tag.

Changes

Socket registry migration

Layer / File(s) Summary
Durable Object socket registry
packages/control-plane/src/session/durable-object-socket-registry.ts, packages/control-plane/src/session/durable-object-socket-registry.test.ts
Renames the registry types, adds upgrade-pair creation and auto-ping configuration, and filters sandbox socket scans by tag.
Session connection wiring
packages/control-plane/src/session/durable-object-session-connections.ts, packages/control-plane/src/session/durable-object-session-connections.test.ts, packages/control-plane/src/session/durable-object.ts
Routes socket creation, lookup, registration, messaging, cleanup, and participant access through the registry.
Generic socket consumers
packages/control-plane/src/session/message-queue.ts, packages/control-plane/src/session/message-queue.test.ts, packages/control-plane/src/session/sandbox-events.ts, packages/control-plane/src/session/sandbox-events.test.ts, packages/control-plane/src/session/server.test.ts
Makes queue and sandbox event processing generic over connection types and removes obsolete manager casts and logging data.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 0aae2

This PR extracts the session socket lifecycle behind a runtime-neutral interface while preserving existing identity validation and wire behavior; no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: extracting a runtime-neutral session SocketRegistry port.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch col-50-socket-registry-port

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.

@open-inspect open-inspect 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.

Summary

PR #1513, "Extract the session SocketRegistry port" by @ColeMurray, changes 14 files with 126 additions and 158 deletions. The change cleanly moves Cloudflare WebSocket mechanics into DurableObjectSocketRegistry, updates session collaborators to consume narrower socket abstractions where practical, and preserves the existing sandbox/client lifecycle behavior.

Critical Issues

None found.

Suggestions

  • Architecture packages/control-plane/src/session/durable-object-session-connections.ts:11 - DurableObjectSessionConnections still depends on the concrete DurableObjectSocketRegistry because it needs persistClientMapping, configureAutoPing, and upgrade socket creation. This is not blocking, but if the goal is a fully runtime-neutral socket port, consider moving those methods into a dedicated interface or splitting the lifecycle/Cloudflare-only responsibilities so this adapter can depend on a contract rather than the implementation.

Nitpicks

None.

Positive Feedback

  • The tagged ctx.getWebSockets("sandbox") usage is a nice simplification and avoids scanning/classifying unrelated client sockets during sandbox recovery and detach.
  • Existing hibernation and stale sandbox identity checks were preserved, including terminal sandbox cleanup before re-adopting sockets.
  • The control-plane typecheck passes locally.

Questions

None.

Verdict

Approve.

@open-inspect open-inspect 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.

[deep review] The extraction is moving in the right direction, but the new boundary is not actually runtime-neutral or substitutable yet. The inline findings are architectural blockers because they leave consumers coupled to both WebSocket-specific state and the concrete Durable Object implementation.

Comment thread packages/control-plane/src/session/socket-registry.ts Outdated
Comment thread packages/control-plane/src/session/socket-registry.ts Outdated
Comment thread packages/control-plane/src/session/durable-object-session-connections.ts Outdated
@github-actions

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate
Tests

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @open-inspect[bot], Action: pull_request

@github-actions

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate
Tests

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @open-inspect[bot], Action: pull_request

@open-inspect

open-inspect Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Addressed the fresh architecture findings in 020102e:

  • Added typed SandboxCommandSender and ClientResponder boundaries; queue and sandbox event services no longer access raw sandbox sockets.
  • Reduced DurableObjectSessionConnections and SessionConnections to production-used messaging capabilities.
  • Made DurableObjectSocketRegistry implement SocketRegistry<WebSocket, ClientInfo> directly and removed the adapter literal from SessionDO while preserving hibernation identity reconstruction.
  • Added coverage for unavailable versus failed sandbox delivery, push/ACK failures, legacy identity-less push specs, participant lookup, and hibernated client reconstruction.

Deferred the explicitly scoped follow-ups: removing WebSocket from ClientInfo, relocating SessionDO, and narrowing persistence repository ports.

Local validation passed: control-plane lint, typecheck, build, 2,746 unit tests, and 861 integration tests.

@github-actions

Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate
Tests

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @open-inspect[bot], Action: pull_request

@ColeMurray

Copy link
Copy Markdown
Owner Author

Closing: this PR targeted the pre-decomposition tree (the SessionConnections layer it extracted was deleted as dead by #1604), and #1715 (COL-83) has since landed the SocketHost port the manager is built over. The remaining scope is re-cut as COL-84 (upgrade decision object: the authenticator decides, the Cloudflare adapter completes the handshake) and COL-50 (P-3), with the decision PR following shortly.

@ColeMurray ColeMurray closed this Sep 3, 2026
ColeMurray added a commit that referenced this pull request Sep 3, 2026
…COL-84) (#1745)

## Summary

Roadmap **P-2 / COL-84** (Control plane on AWS, epic E1). Follows #1715
(P-1).

`SessionConnectionAuthenticator.handleWebSocketUpgrade` performed the
whole upgrade for both legs: it created a `WebSocketPair`, accepted the
server half, and returned the 101 with `webSocket: client`. Both the
pair and `Response.webSocket` exist only on Workers; on Node the HTTP
server completes the handshake (`ws.handleUpgrade`) after the same
authentication and guards. So the session now **decides** and the host
**accepts**.

### What changed

- **`UpgradeDecision`** (`session/connection-authenticator.ts`):
`authorize(request)` runs every guard and returns either `{ kind:
"reject", response }` or `{ kind: "accept", role, attach(ws) }`. Guard
order is unchanged and still entirely after token validation: 403 wrong
sandbox id → 401 invalid token → 410 session terminal → 410 sandbox
stopped → 403 credentials changed. `attach` is a **one-shot capability**
closed over the admitted identity and the request-scoped logger, so
authorize → host handshake → attach is the only successful path by
construction. Sandbox attachment is **prepare-then-commit**: arming the
inactivity alarm is the one fallible await and runs first; adopting the
socket, the ready status, and the broadcasts follow synchronously, so a
failed handshake leaves the previous bridge in place and publishes
nothing. The authenticator implements the narrow
`SessionUpgradeAdmission` interface hosts program against.
- **Cloudflare adapter** `src/cloudflare/websocket-upgrade.ts`:
authorize → `WebSocketPair` → attach the server half → 101 with the
client half; on attach failure it closes the server half and returns
500. `SessionDO.fetch` routes `Upgrade: websocket` requests to it;
everything else still goes through `server.onRequest`.
- The HTTP dispatcher no longer has an upgrade branch or dep, and
`createUpgradeSockets()` is gone from the manager. `SessionRuntime`
gains `upgrades`; the request-correlation child logger moved to
`session/request-logger.ts`, shared by the dispatcher and the
authenticator.
- **Prod → public reconciliation** (first commit): the two manager hunks
production has carried since #1586 land on the `SocketHost` port.
Accepting a bridge now closes every other live sandbox socket (not only
the cached pointer, which hibernation drops), and the cached socket is
validated against the persisted sandbox id before the fast-path return.
Two tests ported with them.

### Deviations from the issue text

- The accepted decision carries the attachment rather than the identity
fields (`sandboxId` / `wsId`); the identity is closed over. `ClientInfo`
is built at `subscribe`, not at upgrade, so there was nothing else to
carry.
- `sockets.accept(server, tags)` stays inside the manager
(`acceptClientSocket` / `acceptAndSetSandboxSocket`) rather than moving
to the adapter: the tags are manager-owned identity and the manager
already reaches the host through the `SocketHost` port, so a Node host
gets the same tagging for free.

### Done when

- [x] No `WebSocketPair` or `webSocket:` outside `src/cloudflare/`
(`index.ts:141` is the Worker-level forward, untouched per the issue).
- [x] `websocket-sandbox.test.ts`, `websocket-client.test.ts`, the #1577
410 race tests and the credentials-changed 403 test pass unchanged.
- [x] `connection-authenticator.test.ts` drives `authorize` with fake
collaborators and asserts the decision for each guard, including the
mid-hash mutations, and `attach` for both roles, the prepare-then-commit
ordering, the nothing-committed failure path, and one-shot attachment.
- [x] `index.ts handleWebSocket` untouched.

### Verification

- `npm run typecheck -w @open-inspect/control-plane`: clean
- Unit: 245 files, 3,619 tests pass
- Workerd integration: 96 files, 1,129 tests pass (the two "force
eviction" uncaught-exception lines are the eviction test's deliberate
abort)

### Follow-up filed, not in this PR

The deep review noted that closing replaced sandbox sockets is cleanup,
not a dispatch fence: the message router processes frames from any
`sandbox`-tagged socket without checking it against the active one. That
predates this PR (and the prod hunk it reconciles) and needs its own
design for stale-bridge trailing events and hibernation recovery, so it
is filed as COL-128 (P-9), blocking N-8.

Closes COL-84. PR #1513 closed as superseded.

https://claude.ai/code/session_01E3k9fw7GE4HMYHh86vKxXp


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added host-managed WebSocket upgrades for session connections.
  * Added request correlation using trace and request IDs in logs.

* **Bug Fixes**
* Improved validation for sandbox WebSocket connections, including stale
credentials and changed sandbox identities.
* Replaced all active sandbox sockets when a sandbox reconnects,
including sockets retained during hibernation.
* Improved handling of upgrade authorization failures and attachment
errors.

* **Tests**
* Added coverage for connection authorization, lifecycle events, socket
replacement, and stale connection recovery.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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