feat(remote-control): add encrypted outbound relay alongside direct access - #668
feat(remote-control): add encrypted outbound relay alongside direct access#668pascalandr wants to merge 9 commits into
Conversation
OpenCode V2 compatibility audit 2026-09-04Reviewed the current official V2 documentation and the published beta contract used by this branch.
Validation against Conclusion: keep the shared outbound E2EE relay design and the loopback-only CodeNomad/OpenCode boundary. No OpenCode V2 architectural change is required for this PR. Official sources: JavaScript client, embedded SDK, Cloudflare SDK profile, plugin RPC, session warming, session sharing, and HTTP API. |
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/33828113576 Artifacts expire in 7 days.
|
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/33832319543 Artifacts expire in 7 days.
|
Architecture follow-up / merge decisionI reviewed the overlap between this OSS Remote Control work and the WebRTC-based Cloud direction. They are complementary rather than competing implementations:
One product-scope mismatch needs an explicit decision before merge: the current PR removes the existing direct URL/LAN path, including Recommended path: amend #668 so Remote Control is added without removing the explicit direct/LAN option; merge after review; then extract the transport-neutral core and add WebRTC/fallback adapters in a focused follow-up. Alternatives are (1) merge as-is and immediately restore direct/LAN in a follow-up, accepting a temporary regression, or (2) hold this PR for a much larger Cloud unification. I do not recommend either. @shantur, does the recommended path match your intent for #668? |
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/33832319543 Artifacts expire in 7 days.
|
Replace the legacy URL/LAN remote access model with an outbound-only Remote Control flow. Users can now pair another browser with a ten-minute link or QR code and revoke its 30-day device credential from the host settings. Add a shared multiplexed HTTP/WebSocket protocol and a Cloudflare Worker backed by one Durable Object per random host identity. The connector authenticates with a bearer secret, validates an explicit protocol handshake, strips remote credentials, injects a dedicated internal CodeNomad session, bounds pre-handshake queues, and cancels abandoned requests. CodeNomad and OpenCode remain loopback-only behind existing workspace, Git, Yolo, and proxy authorization boundaries. Remove the superseded remote proxy, saved server profiles, LAN discovery, remote native windows, certificate bypasses, and related capabilities. Electron and Tauri now keep the backend alive after the final window closes only while Remote Control is enabled. Cover identity persistence, local-only management, relay security, binary payloads, packaging closure, and desktop lifecycle behavior. Validate the full UI, server, Electron, Tauri, relay, and protocol suites plus a local Wrangler end-to-end pairing and proxy flow.
Upgrade Remote Control to an authenticated protocol v2 with persistent host P-256 identity, ephemeral browser keys, fresh host challenges, directional AES-GCM channels, and replay-resistant counters. Keep Cloudflare opaque to application payloads while preserving one-time pairing and revocable device credentials. Move host and client sockets onto Durable Object WebSocket Hibernation, add bounded pairings, devices, clients, HTTP work, WebSockets, frames, handshakes, and queues, and constrain decrypted loopback traffic to CodeNomad API and workspace namespaces. Filter remote credentials and forwarding metadata before injecting the host-local session. Install the encrypted browser transport before application startup, including multiplexed streaming fetch, reconnecting SSE with Last-Event-ID, and same-origin WebSocket bridging. Keep hashed UI assets public so asset delivery does not wake an idle Durable Object while authenticating HTML and bootstrap discovery. Add protocol tamper/replay coverage, Worker authorization tests, Wrangler end-to-end pairing/HTTP/WebSocket/reconnect/revocation/limit coverage, browser transport tests, CI integration, deployment documentation, and trust-boundary guidance.
Use the platform-specific automation registry environment variable in Developer Mode tests instead of assuming LOCALAPPDATA always controls the registry path. Derive the stale-registry fixture directory through the production resolver so Linux, macOS, and Windows exercise the same location policy. This prevents Linux CI from writing fixtures into an uncreated Windows-style path and keeps test registrations out of the runner's real user config. Validated with the focused automation suite, the complete server test suite, server typechecking, and git diff checks.
Strip lifecycle scripts from prebuilt internal workspace packages before the desktop production install. npm 10 can otherwise run a workspace prepare hook despite the staged npm ci using --ignore-scripts, while the intentionally minimal runtime staging directory has no TypeScript sources or tsconfig. Skip nested node_modules lock entries when copying workspace packages and fail clearly when a required prebuilt dist artifact is missing. Add a packaging invariant test and validate the full staging path with npm 10.8.2 for win32-x64.
Update the generated OpenCode client, protocol, and schema lock entries to beta-19059 so CodeNomad validates against the currently published V2 contract. Keep the rotation-boundary projection test representative of production by marking its synthetic event stream connected. The new client deliberately suppresses background refreshes while disconnected, and real multiplexed SSE delivery sets this state before dispatch. Validated the published contract delta, npm 10 lock compatibility, UI/server/Electron typechecks, 145 browser-conditioned UI integration tests, and the 369-test server suite.
c8322e8 to
096279e
Compare
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/33865530959 Artifacts expire in 7 days.
|
pascalandr
left a comment
There was a problem hiding this comment.
Gatekeeper review — published head 096279e + staged amendment
Verdict: changes required before merge. Review includes the locally staged LAN restoration and relay hardening (not yet present on GitHub). Three confirmed findings remain in the amended code:
-
[P1] Security teardown throws instead of closing the tunnel.
packages/server/src/remote-control/connector.tspasses reserved codes such as 1002/1009/1012/1013 to Undici WebSocket.close();packages/ui/src/lib/remote-control/tunnel.ts:447-453does the same in browser teardown. These APIs accept only 1000 or 3000–4999. Reproduced with installed Undici: close(1013) throws InvalidAccessError. On the browser this also skips rejection/cleanup of pending operations; on the host heartbeat/buffer paths it skips reconnect cleanup. Normalize locally initiated close codes and test the actual API restrictions. -
[P2] Loopback-only HTTP exception accepts external DNS names.
packages/server/src/remote-control/connector-protocol.ts:123-125uses startsWith("127."). Reproduced: normalizedRelayUrl("http://127.attacker.example") succeeds. Such a configured relay receives the host credential over plaintext HTTP. Require a literal IPv4 loopback address (or the explicit localhost/IPv6 loopback cases), not a hostname prefix. -
[P2] Browser outbound network buffer is unbounded.
packages/ui/src/lib/remote-control/tunnel.ts:311-329releases its frame budget immediately after WebSocket.send(). This bounds pending encryption, not bytes buffered by the browser. A fast producer on a slow uplink can repeatedly refill the budget while bufferedAmount grows without bound. Check bufferedAmount plus the next encrypted frame against a fixed byte limit and fail closed without replay; test a stalled transport.
The current green CI covers the published commit only, not the staged amendment. Fixes and regression results will follow. No production deployment or private Cloud implementation is part of this review.
Publish the existing staged amendment reviewed with the user's approval: restore explicit LAN listening, saved remote servers and isolated remote windows in Electron and Tauri without turning LAN into an automatic relay fallback. Retain the staged relay hardening for one-shot crypto acceptance, ordered counters, bounded frame/body queues and control responses, authenticated asset fallback, and bounded remote proxy shutdown. Keep the staged OpenCode dependency and projection-test alignment with the dev baseline. The combined amendment and gatekeeper fixes were exercised with server, UI, Electron, protocol, Worker and Wrangler E2E suites. Tauri passed all 142 tests on rerun after a timing-sensitive process election assertion failed under parallel load. The remaining gatekeeper corrections are recorded separately for reviewability.
…buffering Address the three gatekeeper findings: restrict plaintext development relays to literal IPv4 loopback addresses or explicit localhost/IPv6 loopback; normalize reserved WebSocket close codes for browser and Undici callers; reject frames before the browser network send buffer exceeds 24 MiB. Use one shared close-code helper without changing Durable Object close semantics or the E2EE v2 wire protocol. Teardown rejects pending operations, and a failed mutative request is never automatically replayed. Add real ECDH browser-tunnel regressions for security teardown and a stalled network buffer, installed-Undici close validation, hostname spoofing cases, and shared close-code tests. Include the new tunnel suite in CI. Typechecks, targeted regressions, full server/UI/Electron suites, Worker E2E, packaging checks and the Tauri rerun pass locally.
Gatekeeper follow-up — fixes pushed in 86e34d8All three findings from the review are addressed:
The previously staged amendment is preserved separately in e334a6d, including explicit LAN/saved remote servers, desktop restoration, and relay hardening. Its existing dependency alignment uses OpenCode beta-18999; the earlier beta-19059 audit is historical, not the current lock. No proprietary Cloud code is included. Local validation of the combined tree
The new remote tunnel regressions are wired into CI. Full cross-platform packaged builds must still pass on the new head; this is not an approval of unrun builds or a substitute for the required maintainer review. No merge or production deployment performed. Refactoring signals for the gatekeeper fix: |
Bring the merged binary compatibility, startup shell, Tauri window capture, and Linux automation registry fixes into this PR without changing its feature scope. Resolve the automation test overlap by retaining per-test registry isolation and the upstream temporary-root assertion. Windows and Linux registry variables are restored, and WSL discovery is disabled only inside isolated test fixtures so tests cannot touch the real desktop registry. Validation: all eight automation plugin tests pass; server and UI TypeScript checks pass; git diff --check passes. This conflict-resolution commit is not a final native-release gatekeeper approval.
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/34060270247 Artifacts expire in 7 days.
|
Bring the current dev baseline through 81d536f into PR #668 while preserving both opt-in direct LAN access and the encrypted outbound relay. Retain the network-host helpers introduced by connectivity diagnostics in the metadata route, without restoring the upstream trailing whitespace. Keep both the server metadata retry test and all four Remote Control browser transport test files in CI. Review the automatic listener and HTTP-server merge for effective bind-host and relay coexistence. Validation: server, UI, and Electron typechecks pass; 86 server network/relay tests, 10 UI transport/metadata tests, 11 protocol tests, 14 Cloudflare tests, and the relay HTTP/WebSocket E2E test pass. Workflow YAML parsing and diff checks pass. No native release rebuild or final merge approval is claimed.
|
Resolved the new conflicts with
Validation: server/UI/Electron typechecks; 86 server network/relay tests, 10 UI transport/metadata tests, 11 protocol tests, 14 Cloudflare tests, and 1 relay HTTP/WebSocket E2E test all pass (122 total). Workflow YAML parsing and No native release rebuild or final native gatekeeper approval is claimed. PR remains open and unmerged. |
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/34412659892 Artifacts expire in 7 days.
|
Summary
End-to-end encrypted relay
The E2EE model protects against an honest-but-curious relay, storage disclosure, and captured tunnel traffic. It does not protect against an actively malicious Worker operator replacing the browser bundle before execution; reviewed releases and Cloudflare account security remain part of the trust boundary.
Relay and lifecycle
run_worker_first = truefor authenticated HTML injectionBoundaries and limits
/api/*and/workspaces/*Browser transport
fetchcalls through the encrypted tunnelLast-Event-IDEarlier validation (before the staged amendment; see gatekeeper follow-up for current results)
git diff --checkDeployment note
Production deployment is intentionally not performed from this PR workstation because access to the Cloudflare account that owns
neuralnomads.aiis not available. Until the reviewed Worker is deployed, the production relay URL will continue to report a connection failure.