refactor(isolation): run registered agents in an unprivileged worker - #97
Open
dmorosanu wants to merge 1 commit into
Open
refactor(isolation): run registered agents in an unprivileged worker#97dmorosanu wants to merge 1 commit into
dmorosanu wants to merge 1 commit into
Conversation
dmorosanu
force-pushed
the
codex/uid-gid-agent-isolation
branch
from
August 10, 2026 14:16
1dd19ec to
7a2c59a
Compare
dmorosanu
force-pushed
the
codex/generic-agent-worker
branch
from
August 10, 2026 14:17
1ce9fb1 to
ce92037
Compare
dmorosanu
marked this pull request as ready for review
August 10, 2026 14:17
dmorosanu
requested review from
akshaylive,
bai-uipath,
tmatup and
uipreliga
as code owners
August 10, 2026 14:17
|
Claude finished @dmorosanu's task in 1m 37s —— View job Code Review in Progress
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Stack
This is PR 2, intentionally stacked on #87. Its base is
codex/uid-gid-agent-isolation, notmain.Please review this PR as the PR 2 delta, then merge it into #87's branch. Once this PR is merged, PR 3 will be created from the updated #87 stack.
Problem
#87 enforces the UID boundary through three SDK-specific launch seams and an allowlist of Claude, Codex, and Antigravity. That cannot cover an arbitrary
AgentRegistryplugin: the harness has no generic way to find and wrap whatever subprocess a plugin may create.What changed
AgentRegistry, constructs the selected agent, and owns its fullstart/communicate/stoplifecycle.NoNewPrivsis set.This covers built-ins and third-party registry plugins installed in the image without adding per-agent isolation code.
How it works now
DockerRunnerverifies that the image declares the UID/GID isolation capability and, for now, rejects dynamic grader types that are not yet safe.agent:agent(UID/GID 2000). Hidden task data, grader inputs, and result paths remain root-only.IsolatedAgentProxy.setprivlauncher. The worker checks its kernel identity before accepting work: all UID/GID slots must be 2000, supplementary groups must be empty, all capability sets must be zero, andNoNewPrivsmust be 1.AgentRegistry, constructs whichever registered agent was requested, and keeps that same instance alive for the complete evaluation lifecycle.start,communicate,discard_pending_turn, andstopcross the process boundary through a nonce-framed JSON protocol. Stream events flow back immediately; turn records, state, pending partial turns, SDK options, and environment metadata are synchronized in responses.flowchart LR Host["Host / DockerRunner"] --> Root["Root orchestrator<br/>(trusted)"] Root -->|"stage + chown generated tree"| Workspace["/work/agent<br/>(agent-writable)"] Root -->|"spawn via setpriv"| Proxy["IsolatedAgentProxy"] subgraph AgentDomain["Unprivileged security domain — UID/GID 2000"] Worker["Stateful AgentWorker"] Registry["Plugin loading + AgentRegistry"] Agent["Selected Agent implementation"] Children["SDK / CLI / shell / candidate-code descendants"] Worker --> Registry Registry --> Agent Agent --> Children Agent <--> Workspace end Proxy <-->|"nonce-framed RPC<br/>events, results, state"| Worker Protected["/opt/coder-eval/grader<br/>root-only task + grader data"] -. "filesystem access denied" .-> AgentDomain Root -->|"trusted grading<br/>(static criteria only in this PR)"| Protected Root -->|"stop process group + scan UID 2000"| Cleanup["Fail-closed teardown"]The important change is the boundary location: isolation now wraps the generic
Agentinterface and registry construction, rather than trying to recognize and wrap a subprocess inside each built-in SDK.Out of scope
Dynamic graders (
agent_judge,run_command, anduipath_eval) remain rejected while isolation is enabled. Moving those graders out of the agent-written directory and into their own trusted execution boundary is PR 3.Validation
ruff check src tests: passedpyright: 0 errors; 1 pre-existing Antigravity warningsetprivlauncher: complete NoOp lifecycle succeeded with UID/GID 2000 in all four identity slots, no supplementary groups, all capability sets zero, andNoNewPrivs=1