Skip to content

Add P0 execution substrate behind frozen MCP tools - #46

Merged
novelKR merged 5 commits into
mainfrom
cursor/p0-execution-substrate
Sep 17, 2026
Merged

novelKR merged 5 commits into
mainfrom
cursor/p0-execution-substrate

Conversation

@novelKR

@novelKR novelKR commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Issue

Closes #

Summary

P0 execution substrate lands behind the existing MCP tools. Gateway maps exec_command onto command/exec-shaped Runner DTOs (cwd, env, timeout, output cap, tty: false, policy summary) without work_id / operation_id. crates/policy owns PermissionProfile (Read/Write/Deny, independent process_exec, and a recorded network axis) and operator-registered Environments. linux-container may be configured but exec/patch fail closed with UNAUTHORIZED. Store leases are generalized into an in-memory resource serializer; SQLite schema is unchanged; apply_patch and live shells still take workspace-exclusive ownership (WORKSPACE_BUSY).

Default backend remains host InProcessRunner. Opt-in CODESPACE_RUNNER=uds uses UdsRunner over a CodeSpace-owned framed JSON RPC. The isolated codespace-codex-runtime worker uses pinned Codex execution primitives (codex-process-hardening, codex-uds) and dispatches the same InProcessRunner operations. apply_patch remains one Runner RPC.

The UDS path is a same-host transport boundary, not Linux/container isolation. Gateway-owned workers use a unique private 0700 runner directory, live-socket connect probing, Hello readiness, a transport deadline, process-exit events, and owned child lifecycle. Response-loss cases distinguish confirmed execution errors from transport ambiguity: patch may become Unknown, while ambiguous exec keeps its process-owned workspace lease until worker/process resolution.

Contract changes

None. Live tools, LIVE_TOOLS, and MCP fields stay frozen: no environment_id, no process_resize. workspace_info does not expose environment ids. Public MCP paths remain workspace-relative. Runner wire request ids are internal and separate from operation_id, operation_key, and process_id.

Tests / CI

Coverage added or updated for:

  • Runner DTO shape and async Runner backend abstraction.
  • Profile mapping, including independent exec permission and non-granting network metadata.
  • Environment registration and fail-closed linux-container exec/patch rejection before operation minting.
  • Resource serializer exclusive/process-owned lease behavior.
  • UDS framing, request ids, replay, Hello, protocol mismatch handling, and transport timeout behavior.
  • UDS read/exec/apply-patch round trips and process-exit lease release.
  • Patch transport loss recording Unknown instead of Rejected.
  • Ambiguous exec preserving workspace occupancy.
  • Private runner directory permissions and parent permission preservation.
  • Live socket collision protection and stale refused-socket cleanup.
  • Runtime worker lifecycle and gateway shutdown/disconnect cleanup.
  • Real worker-binary integration tests in crates/codex-runtime/tests/runtime_binary.rs.

CI runs:

  • ./scripts/check-no-model-deps.sh
  • cargo check --workspace --offline --all-targets
  • cargo clippy --workspace --offline --all-targets -- -D warnings
  • cargo test --workspace --offline
  • adapter format/build/test checks for crates/patch and crates/codex-runtime
  • python3 -B scripts/check_docs.py

Security / architecture notes

  • Client approved / user_id still do not raise the profile.
  • Network policy is recorded only; it does not grant write/exec and is not yet enforced at the OS/network layer.
  • Unknown environment ids fail config load. Unimplemented linux-container is a closed runner failure, not a host sandbox claim.
  • Path sandbox, symlink/special-file rejection, and workspace-exclusive write/shell occupancy remain Gateway/Runner-owned invariants.
  • UDS is a private Gateway↔worker transport. It is not a Docker socket mount and does not itself provide Linux isolation.
  • Gateway remains the only authorization authority. Codex crates provide execution mechanics only.
  • Core crates (domain, policy, runner, store, server) still have no codex-* path dependencies or Codex types. Approved Codex dependencies remain isolated in crates/patch and crates/codex-runtime.
  • Worker/helper process hardening is not command sandboxing; Landlock/seccomp/network enforcement remain future work.

Out of scope

PTY / process_resize, codex-file-system integration, Linux sandbox/Landlock/seccomp, network enforcement/proxy, approval tools, diff/audit ledger, internal watch, codex-file-search, remote Environment, Git/worktree provisioning, compose→exec, and making UDS the default backend.

This PR remains draft until P0 review/CI is complete.

novelKR and others added 5 commits September 17, 2026 18:39
Land command/exec-shaped Runner DTOs, a gateway-owned PermissionProfile
and operator Environment registry, in-memory resource serialization, and
an opt-in ContainerRunner over CodeSpace JSON. Default exec stays host
InProcessRunner; linux-container environments fail closed. Isolated
codespace-codex-runtime takes process-hardening and UDS only.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep MCP schemas frozen. Exec DTOs send WorkspaceRoot and runner-local
env defaults instead of host absolute cwd or gateway PATH. PermissionProfile
gains a process_exec axis; path globs stay domain-only. Rename ContainerRunner
to UdsRunner so Host+UDS is transport, not Linux isolation. Leases distinguish
request-owned patch locks from process-owned exec occupancy, with
release_process and ProcessExited. Runner errors split execution from
before-dispatch and ambiguous transport; apply_patch records unknown on
socket loss instead of rejected.

Co-authored-by: Cursor <cursoragent@cursor.com>
Gateway allocates a unique 0700 leaf and always binds runner.sock; workers
do not chmod /tmp or other parents. Live sockets are connect-probed and
unlinked only on ConnectionRefused leftovers. RuntimeProcess owns the
child, directory, and socket, with Hello readiness and kill_on_drop.
Protocol mismatch answers then closes; calls have a transport deadline.
Replay stays same-connection. Binary tests and CODESPACE_RUNTIME_BIN
cover the FS path. MCP schemas stay frozen.

Co-authored-by: Cursor <cursoragent@cursor.com>
RuntimeProcess already owns the child in a wait task with kill_on_drop.
Drop and wait_exit now only Notify that task to Child::start_kill, so a
reaped PID cannot be reused as a SIGKILL target.

Co-authored-by: Cursor <cursoragent@cursor.com>
CARGO_BIN_EXE_* is set for integration tests, not lib unit tests, so
clippy --all-targets on crates/codex-runtime no longer fails compiling
src/lib.rs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@novelKR
novelKR marked this pull request as ready for review September 17, 2026 11:36
@novelKR
novelKR merged commit 942c9ad into main Sep 17, 2026
4 checks passed
@novelKR
novelKR deleted the cursor/p0-execution-substrate branch September 17, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant