fix(coding-agent): windows daemon endpoint ownership and peer identity - #2175
fix(coding-agent): windows daemon endpoint ownership and peer identity#2175kevinjosethomas wants to merge 1 commit into
Conversation
… on Windows Name the Windows named pipe per user and agent directory, refuse to start on an occupied endpoint, and require an HMAC handshake over the owner-only agent-dir secret before the supervisor accepts or the client sends any command on Windows (opt-in elsewhere via PRIME_AGENT_DAEMON_REQUIRE_ENDPOINT_IDENTITY). Adds the capability-gated endpoint_identity surface (schema revision 28) and documents why the full launch environment is forwarded. Linear: ENG-5340
Prime Agent performance — completedPR
Python runtime
Sandbox cost: ~$0.0960 — no inference calls. Methodology and samplesMain resolved at 2026-09-09T23:47:36.757046+00:00. Harness
|
Context
Linear: ENG-5340 — https://linear.app/primeintellect/issue/ENG-5340
On Windows the daemon listened on the constant named pipe
\\.\pipe\prime-agent-daemon. Every ownership and occupancy safeguard used on Unix (0700 socket dir, 0600 socket, lockfile lease, dev/ino identity) was skipped on win32, the supervisor marked every public connection authenticated, and the client trusted the unauthenticateddaemon_hellobefore sendingcreatewithconfigandlaunchEnv(every environment variable, including provider API keys). Any process that pre-created the pipe received the client's environment; a second same-account daemon could not tell that the pipe was already occupied.Root cause: a fixed endpoint name, no occupancy check, and no peer identity before sensitive fields are sent.
Changes
daemon-socket.ts: the Windows pipe is\\.\pipe\prime-agent-daemon-<key>,key = sha256(domain\username, agent dir)[0:16];prepareDaemonSocketPathon win32 probes the endpoint and refuses to start (Daemon socket already in use) when anything answers on it.daemon-endpoint-identity.ts: owner-only shared secret<agent dir>/daemon-endpoint-secret(32 bytes, created withwx+ 0600, mode repaired on load), HMAC-SHA256 proofs bound to role + both nonces, anddaemonEndpointIdentityRequired()(true on win32, orPRIME_AGENT_DAEMON_REQUIRE_ENDPOINT_IDENTITY=1elsewhere).endpoint_identity):daemon_hellogainsendpointChallengeandendpointHandshakeRequired; new control-plane commandendpoint_handshake { nonce, proof } -> { proof }. Compatibility map, plane map, read-only set, and schema id updated.Endpoint handshake requiredand the socket is closed; a wrong proof fails and closes.DaemonClient: when identity is required (or the hello says so), one handshake per connection runs before the first command and before parked commands are replayed after reconnect. A wrong daemon proof rejects withDaemonPeerIdentityErrorand drops the connection. A daemon without the capability is refused for everything exceptlistandshutdown(what the launcher needs to retire a stale daemon).collectDaemonLaunchEnvkeeps the full environment; its doc comment now states why (the worker must reproduce the client's shell: PATH, HOME, provider keys and base URLs, proxies, locale) and that it must only be sent after peer identity is established.docs/daemon.md(new "Endpoint Ownership and Peer Identity" section),docs/windows.md. Changelog fragment added.Compatibility classification
endpoint_handshake: capability-gated (endpoint_identity, schema 28). Old clients ignore the new hello fields; new clients check the capability before sending the handshake.Endpoint handshake requiredand is disconnected. New client / old daemon:DaemonCapabilityUnavailableError("endpoint_identity")for anything butlist/shutdown;ensureInteractiveDaemonRunningalready treats such a daemon as stale (schema id differs) and retires it when idle, so interactive startup self-heals. Both directions are covered by tests.No model-facing surface (tool names, system prompt, kernel API) changes.
Validation
Local (worktree, macOS):
npm run checkclean;test/daemon-endpoint-identity.test.ts(20),test/daemon-endpoint-identity-process.test.ts(1), plusdaemon-client,daemon-protocol,daemon-socket,daemon-supervisor-admission,daemon-worker-windows-timeouts,daemon-launch,daemon-client-env,daemon-mode,daemon-supervisor-*,daemon-worker-connect,daemon-routed-client,daemon-peer-transport,agent-connection-daemon,package-self-update-daemon,fire-and-forget-protocol: all passing.Prime Sandbox
dxc5m4hujv50v3a1e1y2gsjt(node:24-bookworm, Debian 12, Node 24.18.0, ordinary usertester, synthetic markers only, deleted afterwards). Unix sockets stand in for the pipe;PRIME_AGENT_DAEMON_REQUIRE_ENDPOINT_IDENTITY=1switches on the Windows policy. A squatter (fake-daemon.mjs) owns the endpoint, replays a current-looking hello, and records every command it receives.DaemonClientconnect -> hello ->create(asmain.tsdoes)createwithconfig.apiKeyand 24launchEnvvars incl.OPENAI_API_KEY=sk-synthetic-...: reproduced-p "say hi" --daemon-socket <squatter>createwith 26 env vars incl. all synthetic keys: reproducedendpoint_identity+ challengeendpoint_handshake;DaemonPeerIdentityError: ... invalid endpoint proof. No session data was sent; no marker reached the squatterDaemonCapabilityUnavailableError: does not support endpoint_identity; squatter received nothingDaemonPeerIdentityError; squatter saw onlyendpoint_handshake--mode daemon) with the policy-rw------- tester; hello carriesendpoint_identity, challenge,endpointHandshakeRequired: true; honest client: handshake thencreatesucceeds (worker spawned); wrong-secret client:Endpoint handshake failed; main-tree (legacy) client:Endpoint handshake required, disconnectedcreatesent as before (wire behaviour unchanged)On Linux the filesystem would stop a cross-account squatter from owning the socket; the sandbox demonstrates the wire-level trust problem that exists on Windows regardless of the DACL.
Native Windows verification still required
Prime Sandboxes are Linux-only, so nothing here ran on Windows. To close the ticket, on a Windows host with two local accounts:
prime-agentas user A; confirmdaemon ps/ logs show\\.\pipe\prime-agent-daemon-<16 hex>and%USERPROFILE%\.prime\agent\daemon-endpoint-secretexists and is not readable by user B.net.createServer().listen(pipe)), then startprime-agentas user A: expectDaemon socket already in usefrom the daemon and aDaemonPeerIdentityError(nocreate, no environment) from the client if B's server replays a hello.list: expectEndpoint handshake requiredand disconnect (also record whether the default pipe DACL lets B connect at all).Daemon socket already in use.list/shutdownpath).PRIME_AGENT_CODING_AGENT_DIR, get distinct pipe names.Not done here: SID-based naming (the key uses
USERDOMAIN\username, fromos.userInfo()); worker pipes (prime-agent-worker-*) still rely on the per-workerworker_authtoken rather than the new handshake.Note
Fix Windows daemon endpoint ownership and peer identity verification
listandshutdownremain available to retire legacy daemons.Macroscope summarized 3fdae5b.