## Why
Older executors do not support resolving HTTP header values from their own
environment. Sending environment-backed bearer tokens to them can prevent MCP
servers from starting.
## What changed
- Advertise executor support for environment-backed HTTP headers with the
`httpHeaderEnvVars` capability.
- Delegate bearer-token resolution only when the selected executor advertises
that capability; otherwise, resolve tokens available in the host environment.
- Exclude executor-owned MCP servers that require environment-backed bearer
tokens when the executor cannot resolve them, while retaining compatible
host-owned and executor-owned servers.
## Testing
Added protocol and environment-variable coverage plus an end-to-end test that
simulates an older executor and verifies compatible MCP servers remain usable.
GitOrigin-RevId: 43545926f6f7e0f3b1091eb29d34647f6372bc4a
## Why
Executor-owned HTTP MCP servers need to read their bearer credentials from the
selected executor environment instead of the host process.
## What changed
- Preserve `bearer_token_env_var` for executor-owned HTTP MCP configurations and
resolve it when the executor sends each request.
- Extend delegated HTTP headers with executor-local environment references while
rejecting missing, empty, or protected credential variables.
- Keep transport-provided bearer authentication compatible with MCP redirect and
OAuth handling without sending a placeholder authorization value.
## Testing
- Cover authenticated executor-owned MCP requests end to end.
- Cover delegated header resolution and rejection of protected variables.
- Cover parsing executor-owned bearer configuration and transport-provided bearer
behavior.
GitOrigin-RevId: 442bf7382198ffb69eba797eb36d4c74faabda88
## What changed
- Apply all per-request environment overrides, including `PATH`, after restoring
the captured shell environment.
- Remove `runtime_path_prepends` from `ShellSnapshotRequest` and the associated
PATH replay and deduplication logic.
- Keep the shell snapshot integration test's runtime PATH setup in the command
being executed.
GitOrigin-RevId: a6f8d2e144bf9977c7434bc58b60ac972f7e0449
## What changed
- Add the `shellSnapshotV2` executor capability and an optional shell snapshot request to `ExecParams`.
- Capture and restore Unix shell state and profile exports from an in-memory, attachment-scoped cache for `bash`, `zsh`, and `sh`.
- Apply environment policies, runtime `PATH` entries, sandbox context, and live managed-proxy settings when preparing restored commands.
- Bound snapshot size, capture time, scope length, and cache capacity, and fall back to the original command when capture fails.
## Testing
- Cover local, remote, TTY, sandboxed, and supported-shell execution, plus environment filtering, proxy handling, in-memory reuse, and capture failure fallback.
GitOrigin-RevId: 624f747972c249c88c6f10f42cf0af97b75b5541
## What changed
- Create remote plugin metrics directories through the standard executor filesystem API.
- Remove the `private` directory-creation protocol option and its platform-specific handling.
- Update the executor temporary-directory documentation to describe child-visible sidecars without an owner-private guarantee.
GitOrigin-RevId: 9a8532403a3ad2bf998281735be0b668893918c9
## Why
An `apply_patch` path can be replaced with a symlink after verification, allowing an unsandboxed patch operation to reach a different file than the one that was approved.
## What changed
- Add `follow_symlinks` options to executor filesystem reads, writes, metadata lookups, directory creation, and removal, including the corresponding `followSymlinks` protocol fields.
- Implement no-follow filesystem operations on Unix and Windows that reject links in any path component and restrict file access to regular files.
- Run `apply_patch` with symlink traversal disabled when an otherwise-required sandbox is bypassed, while retaining the existing follow-symlink default for standalone callers.
## Testing
- Cover leaf and ancestor symlinks across patch add, update, delete, and move operations, including a path swap after verification.
- Exercise local and remote no-follow filesystem behavior, concurrent directory creation, special-file rejection, and Windows reparse points.
GitOrigin-RevId: 43fd479084891493ce13564fbd894b98f329c6dd
## Why
Filesystem permission paths can use a convention that differs from the host
running Codex. Converting them immediately to native absolute paths can change
the meaning of ambiguous paths such as `/C:/secret` or Windows UNC paths.
## What changed
- Store literal filesystem permission paths as `PathUri` values through the
runtime policy and execution protocol.
- Keep legacy string-based serialization at explicit protocol boundaries and
reject conversions that cannot be represented losslessly.
- Encode native paths as opaque URIs when a normal file URI would imply the
wrong path convention.
## Testing
Added coverage for cross-platform and ambiguous path round trips, UNC path
variants, permission-profile serialization, and deny-policy enforcement.
GitOrigin-RevId: 5247713796d1f2bb4e02f94eb9fc82d4698060f0
## What changed
- Add a best-effort `network/policyDecision` notification for final domain and non-domain policy decisions made by executor-local proxies.
- Validate notifications against the active process on the controller and emit audit events with controller-trusted session and execution metadata.
- Reserve outbound RPC capacity so audit notifications cannot block control messages, and expose valid `chatgpt-account-id` header values for audit attribution.
## Testing
- Cover notification serialization, proxy decision capture, executor-to-controller delivery, trusted metadata handling, and reserved RPC capacity.
GitOrigin-RevId: a39f96a6b3d9401c03d54eaef5b9a6d3fe0da78b
## Why
Streaming reads previously rejected requests that used a platform filesystem
sandbox.
## What changed
- Open streamed files in the sandbox helper and return the open file to the
exec-server by passing a file descriptor on Unix or duplicating a file handle
on Windows.
- Advertise support through the `sandboxedFileStreaming` environment capability.
- Preserve close-on-exec behavior for transferred descriptors, including the
required inherited-descriptor cleanup on macOS.
## Testing
- Cover bounded sandboxed streams, continued reads after path replacement, and
rejection of symlink escapes outside readable roots.
GitOrigin-RevId: 677b2444b74e834b78b87a8554bc119c1c6e08b2
## What changed
- Resolve manifest-declared metric operations against the executor filesystem for remote plugin commands.
- Create the measurement sidecar in an executor-native, owner-private temporary directory, stream its bounded output back for validation, and clean up the directory afterward.
- Extend the exec-server protocol with the executor temporary directory and atomic private-directory creation.
## Testing
- Cover remote unified-exec measurements for foreground and background commands.
- Verify private directories use owner-only permissions on Unix and fail closed on unsupported platforms.
GitOrigin-RevId: dbbd0a84717b91237fc5728e510e18994eb46dd4
## What changed
- Add `environmentConfigRead` to exec-server environment capabilities and advertise it for local executors.
- Default the capability to `false` when deserializing responses from older executors.
## Testing
- Verify legacy capability responses remain compatible and the environment config integration reports support.
GitOrigin-RevId: c2110fea9e0e6e756eaeaaad096c50a943668c99
## What changed
- Add optional `temporaryDirectories` file URIs to `EnvironmentInfo` so clients can resolve `:tmpdir` against executor-local defaults.
- Populate local environment info from `TMPDIR` on Unix and `TEMP`/`TMP` on Windows, resolving relative Unix paths against the working directory and removing duplicates.
- Cover protocol round trips, platform environment discovery, relative Unix paths, and the exec-server response.
GitOrigin-RevId: a41580f783b004011c064c7a4f9fdc7adf4fba10
## What changed
- Add the `environmentConfig/read` RPC for selecting literal TOML paths from executor-local config and requirements layers.
- Return layer precedence, cloud insertion points, source and base-directory metadata, and executor home and hostname context without normalizing path-bearing values.
- Expose the operation through both remote clients and local `Environment` instances, with invalid selectors reported as invalid parameters.
## Testing
- Cover projected remote config reads and rejection of empty selectors.
GitOrigin-RevId: fee15bf833de6bcb2058d405ed5b1d5d928d7218
## Why
Filesystem denials and managed-network blocks did not share a structured event
shape, requiring downstream consumers to rediscover enforcement paths and parse
backend-specific output. See https://github.com/openai/codex/pull/17573.
## What changed
- Add normalized filesystem and network violation types in `codex-sandboxing`
and emit them through a shared tracing seam.
- Classify filesystem denials by backend and reason, retaining an optional path
and bounded output snippet, and preserve managed-network block context.
- Report the sandbox type through exec-server responses so unified exec can
classify remote denials without guessing; omitted values remain compatible
with older peers.
- Record violations from exec, apply-patch, shell-escalation, unified-exec, and
managed-network enforcement paths without changing denial behavior.
## Testing
- Cover filesystem classification, path extraction, `SIGSYS`, network event
conversion, protocol compatibility, and remote sandbox-type propagation.
GitOrigin-RevId: d673173b4fa6bdf0a24421194a8a61c81fab9c96
## Why
Executor capability discovery can traverse plugin and skill roots, including
symlinks. Under restricted filesystem permissions, discovery must not expose
files outside the permitted paths.
## What changed
- Pass each environment's filesystem sandbox context through capability root
discovery and apply it to metadata, directory walks, and file reads.
- Enable discovery for restricted sessions so permitted executor skills remain
available while inaccessible roots and symlink targets are omitted.
- Key discovery caches by sandbox context and reject sandboxed discovery on
executors that do not advertise support for it.
- Split requests with more than 128 roots into supported-size batches.
## Testing
- Cover permitted and denied external symlink targets.
- Verify restricted skill listing excludes inaccessible skills.
- Verify cache separation across permission contexts and discovery of 129 roots.
GitOrigin-RevId: 44d16468ca003403bdb8b71a04ae8c9ff94ed494
## Why
The executor protocol supports Codex releases back to `0.145.0`, so compatibility needs to hold when either side of the app-server/exec-server connection is upgraded first.
## What changed
- Define `MINIMUM_SUPPORTED_CODEX_VERSION` in `codex-exec-server-protocol`.
- Add a Unix test harness that runs current-to-released and released-to-current command execution over authenticated Noise connections.
- Test the current binary against itself, the latest release, and the minimum supported release by default, while allowing explicit release versions.
- Verify that the remote command runs successfully and relay payloads remain encrypted, and increase the relay test timeout to accommodate the end-to-end scenarios.
GitOrigin-RevId: faea8d44fce161f40ed15170876a1282a6de4c22
## Why
Guardian review commands can run without the proxy-port environment used to
configure the parent session. Reconciling persistent Windows sandbox settings
for those commands can discard the parent's proxy configuration.
## What changed
- Add a session-level Windows sandbox proxy-settings mode and use `Preserve`
for guardian review sessions while keeping `Reconcile` as the default.
- Carry the mode through unified exec and the exec-server sandbox context to
Windows process launches.
- In preserve mode, reuse the proxy settings recorded by the existing sandbox
setup marker.
## Testing
Added coverage for guardian session configuration, exec-server transport, and
preserving an existing setup marker when proxy-port environment variables are
absent.
GitOrigin-RevId: 214655c6e6d97360906431773dc0de0fdda2db6e
## What changed
- Define the `network/policyRequest` RPC payloads for associating a network request with a process and returning an `allow`, `deny`, or `ask` decision.
- Cover HTTP, HTTPS CONNECT, and SOCKS5 TCP/UDP requests, with shared size limits for callback fields.
- Add the backward-compatible `requestPolicyDecisions` remote proxy option so executors can opt in to sending policy decisions to clients.
- Re-export the new protocol types from `codex-exec-server`.
## Testing
- Verify the request and decision JSON shapes.
- Verify that the proxy opt-in is omitted and defaults to `false` for legacy configurations, while an enabled value round-trips.
GitOrigin-RevId: 65b0a0723d1abb6e4f504a41d3a0dd41f5f21bf7
## Why
Default read-only protections for project metadata should apply when paths such
as `.git`, `.agents`, and `.codex` exist, without causing sandbox setup to
materialize missing paths as ACL targets.
## What changed
- Add an optional `missing_path_behavior` to filesystem sandbox entries and
mark default project-metadata protections with `skip`.
- Preserve the behavior through permission transforms and exec/MCP protocol
serialization while keeping existing path wire variants stable.
- Ignore skip-missing entries when projecting configuration or Windows sandbox
overrides, while retaining explicit metadata carveouts.
## Testing
- Cover protocol round trips for path and special-path entries.
- Verify default metadata protections and Windows explicit carveout handling.
GitOrigin-RevId: 6df13dadacdd131c44aab9f15a967c81051355c1
## Why
Remote executions need managed-network proxy listeners in the executor so their
loopback proxy addresses are reachable by the launched process.
## What changed
- Add a capability-gated exec-server protocol field for executor-local proxy
launch configuration, including network policy, audit metadata, and execution
attribution.
- Start the proxy while preparing a remote process, replace inherited proxy
environment variables with its local addresses, and derive the sandbox
context from its listeners.
- Keep the proxy alive until inherited output streams close, then shut it down.
- Reject unsupported remote settings such as MITM and credential injection.
## Testing
- Cover configuration round trips and rejection of unsupported settings.
- Verify executor-local startup, blocked-domain enforcement, protocol
compatibility, and proxy lifetime through process closure.
GitOrigin-RevId: c984f54e3e600aa9ebcbf8cf4574046e2c199d11
## Why
Selected capability roots can contribute plugins, MCP servers, connectors, and
skills. Discovering each contribution separately requires repeated access to the
executor filesystem.
## What changed
- Add the `capabilityRoots/discoverV1` exec-server RPC to scan selected roots and
materialize recognized plugin manifests, configuration files, skill
instructions, and skill metadata in one bounded request.
- Add the opt-in `executor_capability_discovery` feature, with a thread-scoped
cache and per-step snapshot shared by MCP and skill discovery.
- Parse MCP, connector, and skill contributions from the materialized snapshot,
including serving cached skill instructions without another filesystem read.
## Testing
- Cover discovery limits, manifest precedence, root-local failures, cache reuse,
plugin contributions, and parity with the existing environment skill loader.
GitOrigin-RevId: f98fd2321cafb58c596db02da1f83c09d8eb375d
## Why
Core filesystem permission models operate on native absolute paths, while exec
sandbox contexts must serialize paths as portable URIs.
## What changed
- Make the core filesystem permission and profile types concrete over
`AbsolutePathBuf` instead of generic over their path representation.
- Add exec-specific permission types that convert native permission paths to
`PathUri` values at the sandbox protocol boundary.
- Replace the legacy read/write-roots tuple with a named
`LegacyReadWriteRoots` struct.
## Testing
- Add coverage that exec sandbox permission paths serialize as URIs and round
trip through the filesystem protocol.
GitOrigin-RevId: a0ef5f8aba9bbfdf00a00fcc4f199314836bc85d
## Why
Exec-server JSON-RPC payloads can contain decimals, exponent notation, and
integers outside the native 64-bit range. Decoding these values should preserve
their original JSON representation without weakening the existing message
complexity limit.
## What changed
- Enable `serde_json` arbitrary-precision number and raw-value support for the
exec-server protocol.
- Decode serde's number and raw-value wrappers in the bounded JSON visitor,
charging nested raw values against the 256K-value limit.
- Select the JSON-RPC envelope variant from its fields after bounded decoding so
arbitrary-precision values survive message deserialization.
## Testing
Add coverage for exact arbitrary-precision number round trips and for enforcing
the value limit inside raw-value wrappers.
GitOrigin-RevId: a6e0a3fba6f88e4e8a6ff414832f078beb2d2a70
## Why
Compact JSON arrays can expand into millions of heap values during decoding, and duplicate object keys make a message ambiguous.
## What changed
- Limit exec-server JSON-RPC messages to 256K JSON values and reject duplicate object keys.
- Cap `fs/read_directory` results and retained `process/read` output at 50,000 entries or chunks so locally produced responses remain within the decoder budget.
## Testing
Add coverage for all JSON-RPC variants, large scalar payloads, duplicate keys, compact array amplification, and retained process output at the chunk limit.
GitOrigin-RevId: e31d1f25ab0a7e2272015c98174fd2b7cdd669d7
## What changed
- Add the initialized `environment/status` RPC, which reports `ready` when the exec server can handle requests.
- Expose environment IDs and `ready`, `pending`, or `disconnected` status through `EnvironmentManager` and `Environment`.
- Keep status checks non-mutating: they do not start or recover lazy remote environments, and probe only an existing connection.
## Testing
- Cover the status RPC over WebSocket and the in-process request processor.
- Verify that checking an unstarted stdio environment leaves it pending and that failed connections report as disconnected.
GitOrigin-RevId: 22febeb6a3457849292128a8991c6400c22b3fd8
## Why
Frame-count backpressure does not bound the amount of executor-controlled data
retained in streamed HTTP response queues, and a single body delta could exceed
the intended wire size.
## What changed
- Limit each decoded `http/request/bodyDelta` payload to 1 MiB, split locally
produced response chunks at that boundary, and reject oversized incoming
deltas.
- Apply a shared 16 MiB byte budget across queued HTTP response streams. Release
capacity as deltas are consumed and fail a stream when the budget is
exhausted.
## Testing
Added coverage for rejecting an oversized delta and for failing a stream after
its queued deltas exhaust the shared byte budget.
GitOrigin-RevId: be9205fef44b0ec96b84b31a8e059b1cb9cd3f3b
## Summary
- complete unified-exec processes from the ordered event stream instead
of issuing a final zero-wait `process/read`
- add optional executor sandbox-denial state to `process/exited`
- retain `process/read` as a retained-output and compatibility fallback
for receiver lag, sequence gaps, and legacy servers
- recover sandbox-denial state across transport reconnection
- cover the real `TestCodex` remote-exec path without adding a public
test-only event constructor
## Why
A successful one-shot tool call currently receives its output and
terminal notifications, then pays another wide-area `process/read` round
trip before returning. Staging traces showed that remote response wait
accounted for more than 99.8% of RPC time; local serialization,
queueing, and deserialization were below 0.6 ms.
## Measured impact
A direct staging A/B used the same build and route and changed only
completion mode. Each arm ran three times with 30 one-shot
`/usr/bin/true` calls per run. The table reports the median of the three
per-run percentiles.
| Metric | Final `process/read` | Pushed events | Change |
| --- | ---: | ---: | ---: |
| End-to-end completion p50 | 159.5 ms | 118.7 ms | -40.8 ms (-25.6%) |
| End-to-end completion p95 | 182.4 ms | 131.7 ms | -50.6 ms (-27.8%) |
| Completion-wait p50 | 80.1 ms | 41.5 ms | -38.5 ms (-48.1%) |
| Final `process/read` RPC p50 | 79.9 ms | eliminated | -79.9 ms |
TCP_NODELAY was enabled in both A/B arms, so its effect cancels out. The
successful, complete, in-order event path issued zero final
`process/read` calls.
## Compatibility and recovery
- new servers send `sandboxDenied` on `process/exited`
- legacy servers omit it, which triggers one compatibility
`process/read`
- broadcast lag or a sequence gap triggers a retained-output read
- recovery remains bounded by the server's existing 1 MiB
retained-output window
- complete, in-order event streams issue no completion read
- sandbox denial is attached to the exit event before consumers can
observe process completion
- server-first and client-first rollouts remain wire-compatible;
server-first realizes the latency win immediately
## Integration coverage
The `TestCodex` suite exercises four distinct remote-exec contracts:
- complete pushed output/exit/close with zero reads
- direct pushed sandbox denial with zero reads
- legacy missing denial metadata with exactly one compatibility read
- count-bounded replay eviction recovered from retained output without
duplication
## Validation
- `just test -p codex-core
exec_command_consumes_pushed_remote_process_events`: 4 passed
- `just test -p codex-core unified_exec::process_tests::`: 4 passed
- `just test -p codex-exec-server`: 294 passed, 2 skipped
- `just test -p codex-exec-server-protocol`: 5 passed
- `just test -p codex-rmcp-client`: 89 passed, 2 skipped
- focused Bazel `//codex-rs/core:core-all-test`: passed across 16 shards
- scoped `just fix` passed for core and exec-server
- `just fmt` passed
The complete workspace suite was not rerun; focused Cargo and Bazel
coverage passed for the changed behavior.
## Why
#28522 routes selected-plugin HTTP MCP traffic through the owning
executor, but OAuth bootstrap and refresh still used host-local clients.
Executor-only servers therefore cannot complete discovery or login
through the same network boundary as the MCP connection.
## What changed
- adapt `codex_exec_server::HttpClient` to RMCP 1.8's `OAuthHttpClient`
contract
- let RMCP own discovery, dynamic registration, PKCE, token exchange,
and refresh
- route auth status, persisted-token startup, and app-server login
through the server runtime while preserving the existing local discovery
path
- add optional `threadId` to `mcpServer/oauth/login` and echo it in the
completion notification
- implement RMCP's redirect policy and 1 MiB OAuth response limit over
executor HTTP
- cover selected-thread OAuth discovery and login through an
executor-only route
Depends on #28522.
## Why
Exec-server JSON-RPC calls can cross local and remote transports, but
trace context stopped at the RPC boundary. That made client and server
work difficult to correlate when diagnosing latency or failures.
## What changed
- Propagate the current W3C trace context on outbound JSON-RPC requests.
- Parent inbound request spans from received trace context.
- Record the received JSON-RPC method on server spans and keep each span
open through response enqueue.
- Add only the OTEL dependencies required by the exec-server crate.
## Stack
Review and land this stack in order:
1. #27466 — trace exec-server JSON-RPC requests **(this PR)**
2. #27467 — record bounded connection, request, and process lifecycle
metrics
3. #27470 — observe remote registration and Noise rendezvous lifecycle
## Validation
- `just test -p codex-exec-server --lib` (153 passed)
- `just bazel-lock-check`
- `just fix -p codex-exec-server`
Stack 1 of 3. Follow-ups: #29842 and #29844.
## What changes
Adds a general bounded `fs/walk` operation to the exec server.
The operation returns file and directory entries plus recoverable
per-path errors. It skips symlinks, preserves the existing filesystem
sandbox routing, and enforces depth, directory, entry, and response-size
limits.
This PR only defines and wires the filesystem operation. It does not
change any callers yet.
## Why
The app-server and exec-server expose separate JSON-RPC APIs, but
exec-server currently sources its serialized protocol and envelope types
through app-server-oriented code. Giving each API an explicit owner
makes the crate boundary legible without introducing shared generic
envelopes.
## What changed
- Added `codex-exec-server-protocol` to own exec DTOs, process IDs, and
JSON-RPC envelopes.
- Updated exec-server clients, transports, handlers, and tests to use
the new crate.
- Exposed app-server's existing JSON-RPC types through a public `rpc`
module while retaining root re-exports.
- Preserved existing wire shapes, including exec `PathUri` behavior.
## Stack
This is PR 1 of 6. Next: [PR
#29721](https://github.com/openai/codex/pull/29721), which moves auth
mode below the app wire boundary.
## Validation
- Exec-server protocol and server coverage passed in the focused
protocol test runs.
- App-server protocol schema fixtures passed.