app-server external exec-server selected with externalSandbox, but model tool execution falls back to app-server host identity
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
With Codex CLI/app-server 0.144.5 on Windows, an explicit external
exec-server environment can be registered and contacted successfully, but a
model-requested tool process still executes under the app-server host identity
instead of the exec-server environment's restricted OS identity.
This prevents a secure embedding topology where model authentication and
coordination run in a trusted process while all engineering tools run under a
separate restricted Windows identity.
Environment
- Codex CLI/app-server: 0.144.5
- Platform: Windows
- Trusted app-server identity:
warddesk\brandon - Restricted exec-server/Workshop identity:
CodexSandboxOffline deferred_executorenabled- executor capability discovery enabled
- explicit environment selected on both
thread/startandturn/start - turn sandbox policy:
externalSandbox - exec-server transport: loopback WebSocket
- disposable fixture workspace only
No model credentials were supplied to the restricted identity.
Expected behavior
When an explicit external environment is selected for a thread and turn, every
model-requested engineering process, filesystem action, and tool operation
should be dispatched through that environment's exec-server and execute with
the exec-server environment's OS/security identity.
If the external environment cannot handle an operation, the operation or turn
should fail. It should not silently fall back to execution under the app-server
host identity.
Actual behavior
-
environment/addsucceeds. -
environment/infosuccessfully contacts the restricted exec-server. -
Deferred-executor capabilities are enabled.
-
The external environment is supplied explicitly on
thread/start. -
The same environment is supplied explicitly on
turn/start. -
The turn selects:
{ "type": "externalSandbox", "networkAccess": "restricted" } -
A real model-backed turn requests the safe command
whoami.exe. -
The resulting process reports:
warddesk\brandonrather than:
CodexSandboxOffline
The tool therefore executes with the trusted coordinator's OS authority rather
than the selected external environment's restricted authority.
Minimal reproduction
Start the exec-server under a distinct restricted Windows identity:
codex exec-server --strict-config --listen ws://127.0.0.1:0
The launcher used for the reproduction independently verifies that ordinary
commands launched through this restricted worker run as
CodexSandboxOffline.
Start app-server under the trusted coordinator identity:
codex `
-c features.deferred_executor=true `
-c features.executor_capability_discovery=true `
app-server --listen stdio://
Perform the normal initialization handshake:
{
"method": "initialize",
"id": 1,
"params": {
"clientInfo": {
"name": "external-executor-identity-repro",
"title": "External Executor Identity Reproduction",
"version": "1"
},
"capabilities": {
"experimentalApi": true
}
}
}
{"method":"initialized"}
Register the URL printed by exec-server:
{
"method": "environment/add",
"id": 2,
"params": {
"environmentId": "restricted-workshop",
"execServerUrl": "ws://127.0.0.1:<port>"
}
}
Confirm that app-server can reach it:
{
"method": "environment/info",
"id": 3,
"params": {
"environmentId": "restricted-workshop"
}
}
Start an ephemeral thread with the environment selected:
{
"method": "thread/start",
"id": 4,
"params": {
"cwd": "C:\\fixture\\workshop",
"runtimeWorkspaceRoots": ["C:\\fixture\\workshop"],
"approvalPolicy": "never",
"sandbox": "read-only",
"ephemeral": true,
"environments": [
{
"environmentId": "restricted-workshop",
"cwd": "C:\\fixture\\workshop",
"runtimeWorkspaceRoots": ["C:\\fixture\\workshop"]
}
],
"dynamicTools": [],
"selectedCapabilityRoots": []
}
}
Start the turn, selecting the environment again and requesting only an identity
probe:
{
"method": "turn/start",
"id": 5,
"params": {
"threadId": "<thread-id>",
"input": [
{
"type": "text",
"text": "Run whoami.exe and report only its stdout.",
"text_elements": []
}
],
"sandboxPolicy": {
"type": "externalSandbox",
"networkAccess": "restricted"
},
"environments": [
{
"environmentId": "restricted-workshop",
"cwd": "C:\\fixture\\workshop",
"runtimeWorkspaceRoots": ["C:\\fixture\\workshop"]
}
]
}
}
Observe the completed command's stdout or an equivalent safe process-identity
receipt. In this reproduction it is the app-server identity
warddesk\brandon, not CodexSandboxOffline.
Secondary protocol compatibility finding
Types and JSON schemas generated from the same 0.144.5 binary with:
codex app-server generate-json-schema --experimental --out <temporary-directory>
include the environment/status method.
However, sending:
{
"method": "environment/status",
"id": 6,
"params": {
"environmentId": "restricted-workshop"
}
}
is rejected by the running app-server as an unknown method.
Is this:
- a version-skew defect;
- a generated-schema mismatch;
- an unsupported or deprecated method;
- or a compatibility condition consumers are expected to detect?
Security relevance
This is not being reported as a general Codex sandbox vulnerability.
The specific problem is that a selected external execution environment does not
provide the expected execution boundary in this embedding topology. Model
authentication and coordination intentionally run in a trusted process, while
engineering tools must run under a separate restricted OS identity.
Silent host-local fallback makes that separation impossible to assert safely.
Requested behavior
Equivalent guarantees to the following would address the integration blocker:
- Every model-requested shell/filesystem/tool operation for a selected external
environment is routed through its bound exec-server. externalSandboxnever silently runs an engineering tool under the
app-server host identity.- Routing failure fails the operation or turn closed.
- Structured execution evidence must identify the selected environment and
provide independently verifiable execution provenance sufficient to establish
that the tool ran through the selected external exec-server rather than
falling back to the app-server host. Where available, include actual
process/security identity together with exec-server/session, tool
request/result, and turn/session identities. - The exec-server transport supports session-bound authentication or an
equivalently protected local channel. - Generated capability/schema advertisement agrees with runtime method
support.
Related reports
- #33820 is materially related because an
apply_patchdelete reaches
host-local filesystem instead of the selected external environment. That
report is narrower and is not established as identical to this
process-identity result. - #33391 is related to executor identity evidence but does not report this
routing fallback.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the app-server environment/add, thread/start, and turn/start entry points and trace externalSandbox tool routing. Reproduce with the separate exec-server identities and a whoami.exe request, then check environment/status against generated schemas. Done means selected external operations cannot silently run on the app-server host, routing failures are closed, execution provenance is verifiable, and advertised methods match runtime support.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100