Skip to content

Adapter isolation contracts

This is the operator‑facing reference for the runtime isolation contract of each agent execution adapter. Pick adapters by risk profile, not by name. For multi‑tenant or internet‑exposed deployments, the sandboxed adapters (claude_sandbox, codex_sandbox) and the hardened docker adapter are the recommended choices; the unsandboxed local adapters are for trusted single‑tenant hosts only.

See adapters.md for the configuration knobs and local-adapter-deprecation.md for the deprecation path of the unsandboxed local adapters.

Quick reference

Adapter Runs as Host filesystem Host env vars Network Container hardening Recommended for
claude_local child process on the Trimus host full (constrained by agent CWD) inherited from host host network trusted single‑tenant dev
codex_local child process on the Trimus host full (constrained by agent CWD) inherited from host host network trusted single‑tenant dev
claude_sandbox claude CLI inside a hardened container only --tmpfs /tmp curated set only trimus-egress-allowlist full sandbox hardening preferred for production
codex_sandbox codex CLI inside a hardened container only --tmpfs /tmp curated set only trimus-egress-allowlist full sandbox hardening preferred for production
docker container (your image) only the image + --tmpfs only what's forwarded per Network config full hardening flags production agents per‑company
lambda HTTP call to a configured endpoint — (off‑host) — (off‑host) outbound HTTPS to endpoint upstream is operator's job managed inference service
remote A2A / worker on another host — (peer‑owned) — (peer‑owned) outbound HTTPS to peer peer's responsibility cross‑host / cross‑instance

claude_local — host‑process Claude CLI

What runs: a child claude CLI process via exec.CommandContext on the Trimus host (internal/engine/adapters/claude_local.go).

Filesystem: cmd.Dir is the agent's home under TRIMUS_HOME, but the process otherwise inherits the full host root filesystem — no chroot, namespace, or bind‑mount restriction. Any file the Trimus service account can read is reachable.

Environment: cmd.Env = BuildClaudeEnv(os.Environ(), …) — every var on the Trimus service is inherited, including DATABASE_URL and TRIMUS_ENCRYPTION_KEY unless the operator strips them in the unit.

Network: whatever the host has. No --network=none analog.

Hardening: none — there is no container.

Recommended: trusted single‑tenant development. The dev machine is the sandbox. Not for production multi‑tenant.

Risks: a prompt‑injection or CLI bug escalates directly to host‑level access (read secrets, disclose TRIMUS_ENCRYPTION_KEY, reach the network). Cross‑company isolation rests only on the application layer (RLS); a process escape bypasses it.

Server‑side guardrails (even on the unsandboxed path):

  • TRIMUS_FORBID_DANGEROUS_CLAUDE_PERMISSIONS truthy ⇒ the --dangerously-skip-permissions flag is silently dropped and the drop is logged (SEC‑015), even if a per‑agent config asks for it.
  • TRIMUS_CLAUDE_BINARY_ALLOWLIST pins which claude binary may run (basename or absolute‑path entries).

Migration: switch the agent to claude_sandbox — same behaviour, hardened container around the same CLI.


codex_local — host‑process Codex CLI

Same shape as claude_local (internal/engine/adapters/codex_local.go): CWD set to the agent home, rest of filesystem inherited, env inherited via BuildCodexEnv(os.Environ(), …), host network, no container.

Recommended / risks: identical to claude_local — the runtime is OpenAI's codex rather than Anthropic's claude, but the host‑process escape surface is the same. Switch to codex_sandbox for production.


claude_sandbox / codex_sandbox — sandboxed local CLIs

What runs: the same claude / codex CLIs the *_local adapters invoke, but inside a Docker container with full sandbox hardening (internal/engine/adapters/sandbox_wrap.go, parallel to the plugin sandbox in internal/plugins/sandbox.go).

Differences from the local adapters:

  • Container root with --read-only + a writable /tmp tmpfs.
  • No host env inheritance — only the curated provider/context vars the CLI needs. DATABASE_URL and TRIMUS_ENCRYPTION_KEY do not enter the container.
  • The container joins the trimus-egress-allowlist Docker network. With the egress DNS resolver enabled, it can reach only allow‑listed hostnames; other lookups return NXDOMAIN and write an egress_anomaly_events row for operator review.
  • --pids-limit via TRIMUS_SANDBOX_PIDS_LIMIT (default 100), plus the shared hardening flags: --cap-drop ALL, --security-opt no-new-privileges, --memory-swap pinned to --memory, and any TRIMUS_DOCKER_SECURITY_OPTS profiles.

Recommended: production and multi‑tenant deployments. These are the preferred default whenever you'd otherwise reach for a local CLI.


docker — generic Docker container

What runs: an operator‑specified image, started via docker run with argv from internal/engine/adapters/docker.go.

Filesystem: the image's root, no host bind‑mounts by default; --read-only + --tmpfs /tmp:rw,nosuid,size=64m (root read‑only, only /tmp writable, 64 MiB cap). Opt out per agent with "writable": true.

Environment: only the explicit cfg.Env map plus Trimus‑injected context (TRIMUS_AGENT_ID, TRIMUS_RUN_ID, TRIMUS_ISSUE_*). Host env is not inherited.

Network: default none (the DockerConfig default). Point Network at trimus-egress-allowlist for production SSRF safety.

Hardening:

  • --security-opt no-new-privileges (always)
  • --cap-drop ALL (always)
  • --pids-limit 100 (default; override via pids_limit in adapter_config or TRIMUS_DOCKER_PIDS_LIMIT)
  • --memory-swap = --memory (swap disabled so the memory limit is honest — SEC‑020 parity with the plugin sandbox)
  • --memory + --cpus (operator‑supplied)
  • --security-opt <opt> per TRIMUS_DOCKER_SECURITY_OPTS entry (seccomp/AppArmor hook, SEC‑029)

Recommended: production agents that ship their own runtime image.

Risks: the image is operator‑trusted — embedded secrets or a vulnerable runtime become the blast radius. Network policy is per‑container; you must opt into the egress network for SSRF protection.


lambda — generic HTTP endpoint

What runs: nothing on the Trimus host — the adapter is an HTTP client (internal/engine/adapters/lambda.go). EndpointURL is the target (despite the name, any HTTP service that speaks the payload contract: AWS Lambda Function URLs, Cloud Run, self‑hosted inference). Method, headers, auth header, and timeout are operator‑configured.

Filesystem / env / hardening: none on the Trimus host (no process).

Network: outbound HTTPS to the configured endpoint. Egress allowlisting does NOT apply — the call originates from the Trimus server, not a sandboxed container.

Recommended: delegate execution to a managed service you already run.

Risks: the upstream service is the entire trust boundary; ensure EndpointURL is not attacker‑controllable.


remote — worker / A2A on another host

What runs: nothing on the Trimus host. The adapter queues a task that a registered trimus-worker (or A2A peer) claims and runs with its own locally‑configured adapter, posting the result back.

Filesystem / env / network / hardening: all owned by the worker / peer host.

Recommended: run execution on a different host — e.g. the server in a container but local‑CLI agents on a host with ~/.claude / ~/.codex. See remote-workers.md.

Risks: worker / peer credentials are the trust boundary. A compromised worker can exfiltrate whatever the dispatching company shares with it.


The egress allowlist + DNS enforcement

At boot, adapters.EnsureEgressNetwork creates the trimus-egress-allowlist Docker network; sandboxed adapters land in it. When TRIMUS_EGRESS_DNS_ADDR is set, Trimus starts a deny‑by‑default DNS resolver (miekg/dns): containers resolve only allow‑listed hostnames, and any unregistered lookup returns NXDOMAIN (so the host isn't leaked) while writing an egress_anomaly_events row.

Manage the per‑company allowlist and review recent denials at /companies/{id}/egress (admin). TRIMUS_EGRESS_DNS_DEFAULT overrides the resolver address published into containers (default: the bridge gateway).

Picking an adapter (decision tree)

  1. Single‑tenant dev box you trust? claude_local / codex_local — cheapest, no Docker.
  2. Multi‑tenant production running the local CLIs? claude_sandbox / codex_sandbox on the egress‑allowlist network.
  3. Production with your own runtime image? docker with the hardening flags + Network=trimus-egress-allowlist.
  4. Runtime lives in a managed service? lambda against your Function URL / Cloud Run service (egress allowlist does not apply).
  5. Work delegated to another host / Trimus instance? remote with a tagged worker or A2A peer.

Cross‑references

  • adapters.md — per‑adapter configuration.
  • security.md — production hardening checklist.
  • local-adapter-deprecation.md — the TRIMUS_LOCAL_ADAPTERS gate roadmap.
  • ARCHITECTURE.md — the application‑layer RLS isolation that complements the runtime isolation here.