Files
codex-mmo/docs/SECURITY.md
T
2026-08-24 08:11:59 -07:00

261 lines
18 KiB
Markdown

# Security Model
Codex MMO coordinates tool-using models that may read or modify a workspace and may call external routes. Security depends on boundaries around profile installation, credentials, process execution, delegation authority, isolated writes, network access, and result trust.
## Threat model
The platform assumes these may be untrusted or fallible:
- Downloaded profile packs.
- Model-generated tool calls and prose.
- Repository content containing prompt injection or malicious instructions.
- Low-capability or contradictory worker output.
- External route availability, serving identity, and metadata.
- Third-party Tool MCP server instructions, schemas, tool results, and side effects.
- A worker that exceeds its delegated logical scope.
- A local compatible endpoint that does not faithfully implement its advertised protocol.
The platform does not assume a compromised operating-system account, root-level attacker, or malicious replacement of installed Codex/Switchyard binaries can be contained by application-level policy.
## Static profile packs
Profiles are data, not plugins. Installation rejects:
- Unexpected top-level files.
- Executable files.
- Python and shell code.
- Symlinks.
- Paths that escape the pack.
- Archive traversal and unsafe members.
- Unsupported instruction or contract file types.
The package validator repeats static safety checks for all bundled profiles. Profile instructions can still influence model behavior, so inspect third-party Markdown and TOML before installation. A profile may grant a Tool MCP server by stable ID, but cannot introduce or modify its command, URL, authentication, headers, timeouts, approval policy, or operator tool allowlist. Those definitions remain in the operator-owned registry outside the profile pack.
## Credential isolation
API credentials belong in:
```text
~/.config/codex-mmo/credentials.env
```
or the process environment. Profile, catalog, Tool MCP registry, and snapshot files contain environment variable names, never literal keys.
For Switchyard-backed models:
- The managed gateway binds only to an IPv4 or IPv6 loopback literal. Non-loopback, hostname, bracketed, and scoped-address settings are rejected before startup because Switchyard 0.2.0's generated ingress is not authenticated.
- The snapshot determines exactly which routes are needed.
- The gateway explicitly receives the selected routes' declared credentials; common unrelated credential-like names are filtered.
- Worker shell environments filter common credential-like names unless a direct selected route requires them.
- Discovery reports never serialize key values.
- Catalog discovery rejects cross-origin redirects before a configured or explicit authorization header can be forwarded.
- Credentials are preserved across a validated current-generation reinstall and excluded from release archives.
For third-party Tool MCP servers:
- Schema version 8 supports environment-backed credentials, not inherited Codex MCP OAuth state.
- Generated homes receive only credential variable names; values are selected from `credentials.env` or the launch environment immediately before process execution.
- Each Codex process receives only variables referenced by the Tool MCP servers compiled into that process.
- Native roles share a parent Codex process, so their Tool MCP credential values are visible to that parent and its in-process native siblings. Use a separately launched Agent-MCP role when credential isolation is required.
Built-in ChatGPT/Codex authentication remains in the operator's base Codex home. The default `shared` mode gives generated homes a symlink to its file-backed `auth.json`; `copy` deliberately creates a protected `0600` copy, and `none` disables propagation. Codex 0.149 keyring entries are bound to canonical `CODEX_HOME`, so keyring-only auth cannot be inherited by an isolated home and fails the doctor credential gate. MMO never propagates Codex's separate `.credentials.json` MCP OAuth store.
Logs and prompts can still contain sensitive repository data or model output. Protect the state directory with normal user permissions and apply retention appropriate to the workspace.
Environment filtering is deliberately name-based so ordinary toolchain, locale, terminal, and path configuration continues to work. It recognizes conventional `KEY`, `TOKEN`, `SECRET`, `PASSWORD`, `CREDENTIAL`, `COOKIE`, `AUTH`, and `PRIVATE` components; no process can infer that an arbitrary variable such as a project-specific URL contains a secret. Sanitize the parent environment or use conventional credential names before launching MMO. This is a documented containment limit, not a claim of complete secret discovery.
## Process isolation and durable hosting
Every root and Agent-MCP worker has a separate detached runner. Each privately hosts
`codex app-server --listen unix://…` with:
- A minimal generated `CODEX_HOME`.
- A role-pinned model/route.
- A sandbox ceiling from the profile and root session.
- No Tool MCP servers except explicit grants for that role, and no internal `mmo_mesh` server unless generated for authorized Agent-MCP nesting.
- Network and web search disabled unless the role permits them.
- A profile-compiled `goal` token budget/ceiling or clock-free `turn` lifecycle.
- A warning-only stall interval and bounded explicit terminal-serialization grace.
- A separate process group for cancellation.
- A non-ephemeral thread plus authoritative terminal history, stderr, headerless app-server protocol events, prompt,
partial/final result, pending requests, goal state, metadata, patch, and audit artifacts.
The app-server host plus persistent thread is execution identity; any TUI/noninteractive/control
connection is a detachable client. A runner reconnects to the exact host/thread after transport
failure, or starts one replacement host only to resume that exact persisted thread. If hosting
cannot recover, work becomes `suspended` with trace, terminal history, partial result, pending
requests, and isolated patch retained. Graceful stop asks agents to synthesize from retained
evidence; immediate cancellation targets exact process groups. Neither path erases durable evidence.
MMO records the isolated process group, PID, and Linux process-start token as soon as app-server
is spawned, before protocol initialization or `thread/start`. Cleanup rechecks that fingerprint
before signalling, so PID reuse cannot redirect cancellation. Worker-runner loss retires that
job's app-server/Tool-MCP group before an explicit same-thread continuation, while ordinary root
controller loss preserves a separately owned live root host for reattachment. Bootstrap failure,
stale terminal transitions, stop, and cancel still retire their exact owned groups, so they do not
leave unowned descendants behind. Detach deliberately preserves the host fingerprint and socket while
work continues; only full stop/cancel retires them.
`approval_policy = "never"` permits autonomous work only inside the compiled sandbox and
automatically declines app-server escalation requests. `untrusted` and `on-request` keep an
approval request pending; only a role with an exact per-target `respond` grant can inspect it and send
the method-specific response through `agent_respond`.
## Delegation authority
MCP caller identity is capability-based. Root/native bearer values are stored only in a protected `0600` session capability file so a detached host can be controlled after the original client exits; public session/run state contains only SHA-256 digests. The values remain stable for the one immutable run and the private file is destroyed when the session becomes terminal. Every MCP job receives a distinct inherited job capability, and a native participant that may call the mesh additionally receives a role capability in protected generated configuration. Together those capabilities are tied to:
- Snapshot hash.
- Root session.
- Execution run.
- Agent role.
- Parent lineage.
- Declared child allowlist.
The supervisor validates the capabilities against durable session/job state before accepting the caller identity. A caller cannot gain authority by changing `MMO_CALLER_AGENT` or writing another role or parent into tool arguments. These are same-user process capabilities, not an operating-system isolation boundary: a sufficiently privileged process running as the same account may inspect another process or the protected state tree.
Persistent resume also validates that exactly one run exists, every session-owned Codex home and run record remains contained under its recorded session without symlink redirection, and generated model catalogs still match their recorded SHA-256 digests. MMO-owned `AGENTS.md` and orchestration skills are refreshed from the same integrity-checked snapshot; neither grants authority beyond runtime capabilities. Drift suspends the same run closed rather than substituting current data or creating a replacement.
Admission checks include:
- Directed `can_spawn` edge.
- Maximum depth and ancestor-role rejection.
- Reusable active-agent and per-caller active-child limits.
- Role and resource capacity.
- Task kind and size.
- Task-size, goal/turn mode, token-ceiling, stall-warning, and finalization-grace bounds.
- Sandbox ceiling.
- Attachment permission and containment.
- Write-scope requirements and conflicts.
- Per-route credential/endpoint availability and per-role active caps.
- Result visibility.
Pure native nested agents do not pass through this enforcement boundary. Native nesting is disabled by default in hybrid profiles; enabling it is an explicit advisory tradeoff.
## Tool MCP authority
Tool MCP is a separate authority boundary from Agent MCP. The internal `mmo_mesh` server controls bounded participant delegation; a third-party Tool MCP server can act on whatever external system, local application, account, or data source that server itself can access.
Profile grants can only narrow the operator registry's finite `enabled_tools` allowlist. Generated role configuration explicitly disables ungranted servers and tools, including across Codex's recursive native-agent config merge. The operator also owns server-wide and per-tool approval modes. Keep mutating operations out of the allowlist when they are unnecessary, and use prompt approval for consequential operations.
Codex role declarations do not sandbox the server behind a Tool MCP connection:
- `permissions = "read-only"` constrains Codex workspace access, not an MCP tool's ability to modify an IDA database, browser session, SaaS account, or remote repository.
- `network_access = false` constrains sandboxed shell networking, not an HTTP MCP connection or a local MCP subprocess's own network activity.
- `required = false` changes startup-failure handling; it does not reduce a server's authority when the server is available.
Review the MCP implementation, deployment identity, advertised schemas, and downstream authorization independently. Use separate service credentials and least-privilege accounts where the external system supports them.
## Workspace writes
MCP writers execute in isolated Git worktrees pinned to a captured base. Admission compares canonical scopes and rejects overlap even when two sessions use different, nested workspace roots. The runner captures changed paths, modes, base fingerprints, text/binary patch artifacts, and hashes. Out-of-scope changes, symlinks/special artifacts, unsafe patch members, or boundary failures fail the job and taint the session; they do not modify the canonical checkout.
A successfully completed result starts unread. An authorized ancestor must read it and explicitly accept or reject it. Suspended jobs retain their thread and can be continued; stopped, failed, and cancelled jobs remain readable but cannot be dispositioned as successes. Only an accepted result may be integrated, and integration rechecks patch identity, base fingerprints, scope, lease, and `git apply --check`. Disposition transitions are monotonic and audited.
Limitations:
- Patch/hash correlation does not prove semantic correctness.
- Disjoint paths can still represent logically coupled state.
- Native workers do not use MCP worktrees, scopes, disposition, or integration; all featured bundled native roles are read-only.
- A root with workspace-write permission remains powerful.
- Integration can conflict with canonical changes made after the worker base; the runtime rejects rather than guesses a merge.
The root must inspect accepted diffs, integrate deliberately, reconcile coupled changes, and run canonical validation.
## Low-trust models
A low-trust role is mechanically constrained to supervised Agent-MCP execution with read-only Codex workspace access, at most one active instance, no descendants, no network or attachments, a single-slot resource, strict structured output, and mandatory verification. A terminal instance releases its slot, but every later invocation is subject to the same constraints. Callers must use a typed `literal_task`; the runtime validates and supplies the literal inputs instead of accepting an interpretive free-form brief. That workspace restriction does not make a granted Tool MCP server read-only; only grant independently reviewed, non-mutating tools to a low-trust role.
Do not promote low-trust output directly into:
- Architecture decisions.
- Security conclusions.
- Concurrency or data-integrity reasoning.
- Migrations.
- Implementation changes.
- Final recommendations.
Resolve conflicts from primary evidence. A structured evidence document is easier to inspect; it is not proof that the evidence was interpreted correctly.
Durable app-server events, partial evidence, and contract-shaped worker output remain untrusted
claims. MMO filters controller-visible traces, validates final contracts and correlated command
or artifact evidence, and preserves interrupted work, but none of those mechanisms proves the
worker's interpretation. Only a successfully completed result may enter the accept/reject
lifecycle, and only an accepted isolated patch may be considered for audited integration.
## Prompt injection
Repository files, logs, web pages, route output, MCP server instructions, and MCP tool results may contain instructions that conflict with the user or role policy. Agents are instructed to treat these as data unless the root explicitly authorizes them.
Operational controls:
- Keep web search and shell network access disabled by default.
- Grant only reviewed Tool MCP servers and finite tool subsets to roles that need them.
- Use read-only reviewers for untrusted content analysis.
- Delegate only bounded tasks with explicit success criteria.
- Do not expose credentials through prompts or command output.
- Review proposed commands that cross trust boundaries.
- Use the narrowest workspace and route permissions that satisfy the task.
## Network and web access
`network_access` and `web_search` are independent role declarations. Network-enabled work broadens the attack surface and may transmit repository data to external services. Neither declaration disables or confines an explicitly granted Tool MCP server; its transport and its own downstream network behavior are separate.
Use live web access only when current external information is genuinely required. Prefer cached/read-only evidence roles for research and keep implementation roles offline unless dependency access is necessary.
## Multimodal data
Images and documents may contain sensitive information. A profile must explicitly allow attachments and require the modality. The compiler validates the model/transport path, but the operator remains responsible for route/operator data-handling policy.
Avoid protocol conversions known to flatten image/document blocks into text when visual fidelity is material. Use a native capable path or reject the binding.
## Catalog and discovery trust
The release catalog is a dated baseline. Live discovery verifies IDs but does not infer unknown capabilities. A newly observed model remains non-executable until an operator supplies reviewed metadata.
This prevents a hosted inventory name addition from automatically receiving tool access, workspace permissions, or multimodal claims.
## State and cleanup
Default state contains prompts, outputs, logs, snapshots, and evaluation artifacts. Inspect it with:
```bash
codex-mmo session list
codex-mmo jobs list
codex-mmo gateway list
```
Apply retention with:
```bash
codex-mmo clean --job-days 14 --session-days 14
```
Use `--dry-run` first. Cleanup does not replace organization-specific secure deletion or backup policy.
Only terminal sessions become cleanup candidates after their `last_active_at` age exceeds `session_retention_days`. Detached, paused, suspended, and running sessions remain protected regardless of age. Removing a terminal record also removes its generated home and retained thread artifacts, never an external workspace or Tool MCP system.
## Release integrity
Release archives include `PACKAGE-MANIFEST.json` with per-file SHA-256 values, normalized modes, and sizes. Verification rejects exact permission drift as well as path, type, size, and digest drift. Tar inspection and extraction are streamed under progressive member-count and expanded-byte ceilings. The release builder emits archive checksums and verifies byte-for-byte reproducibility with an independent second build.
Before installation:
```bash
sha256sum -c codex-multimodel-orchestrator-8.0.0-SHA256SUMS.txt
```
The package acceptance validator scans for common credential formats, private keys, unresolved placeholders, executable profile files, symlinks, bytecode, syntax errors, and catalog/profile drift.
## Operational boundary
Codex MMO is an orchestration and enforcement layer, not a substitute for:
- Operating-system isolation.
- Container or virtual-machine boundaries for hostile repositories.
- Provider access controls and billing limits.
- Source-control review.
- Secret scanning and dependency security tools.
- Authorization to test or modify a system.
Use a disposable container or VM for code that is actively malicious or not trusted to be opened by a tool-using agent.