Run Claude Code with Olivares (co-deployment)
This is the Operate half of the Anthropic-first story: not just observe and
govern Claude Code, but conduct it. The control plane launches a real claude
process, bridges its I/O into a governed stream, anchors every lifecycle transition in
the audit ledger, and tears it down — over a shared workspace, from the API/CLI (and,
later, the portal), without SSH. This page co-deploys both halves on one Linux host
in four topologies, secure by default.
For the cooperative observe path (OTLP telemetry → access map) see Connect Claude Code; for the govern path (PreToolUse hooks as a PEP) see the govern-claude-code example. This page is co-deployment: getting the two runtimes running together.
Two principles before you start
Section titled “Two principles before you start”- Opt-in. The base Olivares image is distroless and carries no
claude. The Operate-Claude-Code layer is a separate artifact — a combined image (Dockerfile.agentops) or a native install add-on. If you do not run governed Claude Code, you never pull it, and its extra surface never touches your control plane. - Official source, never redistributed. Anthropic’s terms do not permit
redistributing the
claudebinary, so we install it from Anthropic’s official, GPG-signed source at build/first-run (the signed apt/dnf/apk repositories), pinned and with the auto-updater disabled. We ship no third-party binary. You can also bring your ownclaudeand point the engine at it.
The four topologies at a glance
Section titled “The four topologies at a glance”| # | Olivares | Claude Code | How the engine conducts it | Status |
|---|---|---|---|---|
| 1 | Docker | Docker | Same container (combined image), procRunner child | Recommended (same governed path as 2) |
| 2 | Native | Native | Same host (systemd), procRunner child | Recommended, smoke-tested end-to-end |
| 3 | Docker | Native (host) | Cross-namespace — not governable as-is | Co-locate instead (see below) |
| 4 | Native | Docker (per-session) | Per-session container via the Docker API | Follow-up (documented) |
The two co-located topologies (1, 2) are the secure default. Topology 2 (native) is
tested end-to-end by scripts/smoke-agentops.sh;
topology 1 reuses the same governed procRunner path (the combined image’s build/run
is not yet wired into an automated test). Topologies 3 and 4 want the governor and the governed in
different containers; bridging stdio across that boundary needs Docker-API access (a
privilege the engine deliberately does not take by default). Their honest paths are
spelled out in Mixed topologies.
Topology 1 — both in Docker (recommended)
Section titled “Topology 1 — both in Docker (recommended)”One hardened container runs the engine and claude; a workspace volume is the
shared working directory. Loopback-only, non-root, read-only root filesystem — identical
posture to the base compose, plus the conducted runtime.
Build the combined image
Section titled “Build the combined image”claude is installed at build time from Anthropic’s signed apt repository, with the
signing-key fingerprint pinned (31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE) and
auto-update disabled. Pin the engine base by digest and verify it first:
# verify the engine image you build FROM (it is cosign-signed)cosign verify docker.io/olivaresai/olivares:26.8.0 \ --certificate-identity-regexp '^https://github\.com/olivaresai/olivares/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com
docker build -f Dockerfile.agentops \ --build-arg OLIVARES_IMAGE=docker.io/olivaresai/olivares@sha256:<digest> \ --build-arg CLAUDE_CHANNEL=stable \ -t olivares-agentops:26.8.0 .Bring your own claude instead with --build-arg CLAUDE_INSTALL=byo (the image ships
without claude; mount yours at runtime and set OLIVARES_SESSION_RUNTIME_CLAUDE_BIN).
Bring it up
Section titled “Bring it up”export OLIVARES_AGENTOPS_IMAGE=olivares-agentops:26.8.0docker compose -f deploy/compose/docker-compose.yml \ -f deploy/compose/docker-compose.agentops.yml up -dThe override changes only what Operate needs: the combined image, four writable volumes
(engine data, workspace, claude’s ~/.claude home, the short-lived inference token),
and the session-runtime env. Everything else — 127.0.0.1-bound ports, uid 65532,
read_only root, cap_drop: ALL, no-new-privileges — is inherited from the base.
Topology 2 — both native (no Docker)
Section titled “Topology 2 — both native (no Docker)”Engine and claude on the host; systemd runs the engine, which conducts claude. The
workspace lives at /var/lib/olivares/workspaces.
One command
Section titled “One command”curl -fsSL https://raw.githubusercontent.com/olivaresai/olivares/main/scripts/install-agentops.sh | shIt auto-detects the native topology, installs the verified engine binary (the
cosign-gated install.sh), installs claude from the signed apt/dnf/apk repository (with
key-fingerprint verification — or OLIVARES_CLAUDE_INSTALL=byo to skip), creates the
no-login olivares service user and the workspace dir, and drops in the hardened systemd
override + env example. It does not auto-start a governance plane — running one is
your explicit decision.
What the installer wires (and why)
Section titled “What the installer wires (and why)”packaging/systemd/olivares.service.d/agentops.conf— a drop-in that gives the conductedclaudea writableHOMEfor~/.claude(kept under/var/lib/olivares, soProtectHome=truestill shields real users), ensures the workspace dir exists, and lifts exactly one sandbox property:MemoryDenyWriteExecute(theclauderuntime JIT-compiles and needs W→X memory). Every other hardening directive from the base unit stays in force./etc/olivares/agentops.env— the session-runtime config (token file, TTL, optional gateway base URL, optional BYOclaudepath).
Then, deliberately:
sudo nano /etc/olivares/agentops.env # wire the short-lived inference token (refresher)sudo systemctl enable --now olivares # loopback-only by defaultLaunch the first governed session
Section titled “Launch the first governed session”Same steps in either co-located topology. Authenticate the CLI, register the shared workspace, launch:
export OLIVARES_SERVER_URL=https://127.0.0.1:8443export OLIVARES_TOKEN=<your-api-token>export OLIVARES_TENANT=<your-tenant-id>
# 1) register the shared workspace (the session's working dir; jailed file API on top)olivares agent workspace add /var/lib/olivares/workspaces/project-x --name project-x --mode rw
# 2) launch a governed session over the stream-json transportolivares agent session create --transport stream-json \ --permission-mode acceptEdits --model opus \ --workspace <workspace-ref> --isolation native
# 3) attach to its live, bridged I/O (lossless replay from a cursor); send input; stopolivares agent session attach <run-ref>olivares agent session input <run-ref> --line '{"type":"user","message":{"role":"user","content":"…"}}'olivares agent session stop <run-ref>Every transition (created → launched → … → stopped) is anchored in the signed audit
ledger (olivares agent session events <run-ref>); the workspace file API
(olivares agent workspace files|get|put|…) is jailed and audited. The reproducibility
contract for all of this is scripts/smoke-agentops.sh,
which brings the native co-deployment up against a hermetic fake claude and asserts the
session is governable end to end.
Mixed topologies (3 and 4)
Section titled “Mixed topologies (3 and 4)”These split the governor and the governed across a container boundary. Be clear-eyed about what that costs.
Topology 3 — Olivares in Docker, Claude on the host
Section titled “Topology 3 — Olivares in Docker, Claude on the host”There is no clean governed path: a containerised engine cannot own the stdio of a
process in the host’s namespaces, and the governed transport is stdio. Reaching a host
claude would require sharing the host PID namespace and mounts into the engine
container — a large, deliberate de-isolation that defeats the point of containing the
engine. Co-locate instead: run both in the combined image (that is topology 1), or
run both native (topology 2). This is a real limit, stated rather than papered over.
Topology 4 — Olivares native, Claude in a per-session container
Section titled “Topology 4 — Olivares native, Claude in a per-session container”This is the natural home for per-session fresh-container isolation: each session gets
a brand-new hardened claude container (workspace bind-mounted, read-only root, non-root,
cap-drop), created and torn down by the engine through the Docker API, with stdio bridged
via Docker attach/hijack. The data-model seam already models it (--isolation container
is a valid value, and the executor mount primitive it will consume already ships) — but the
runner behind it is not wired yet, so the native runner refuses that value today (see the
note above).
It is a documented follow-up, not shipped in this release. Driving sibling containers
means giving the engine Docker-API access (ideally through a least-privilege socket
proxy) — a trust surface this release deliberately avoids in favour of the socket-free
combined image. Choosing this topology is choosing stronger governor/governed isolation
at the cost of that Docker-API grant; it will arrive behind the existing
isolation=container seam. Until then, the secure default is co-location.
Security posture (all topologies)
Section titled “Security posture (all topologies)”- Loopback by default. Host ports publish on
127.0.0.1only. In a container the engine listens on0.0.0.0inside the container, so the host port mapping is the exposure boundary — never publish it on a non-loopback host address without your own TLS-terminating auth proxy. The native/systemd default bind is loopback. Expose deliberately. - Non-root, least privilege. uid/gid 65532, read-only root filesystem,
cap_drop: ALL,no-new-privileges(Docker) / the fullProtect*/Restrict*set minus the one documented W^X relaxation (systemd). - Minimal-data, allowlisted env. The child
claudeinherits only an explicit allowlist (PATH, HOME, locale…) plus the in-memory inference token — noOLIVARES_*signing keys, no ambientANTHROPIC_*/CLAUDE_CODE_*that could shadow the minted credential. - Verified supply chain. The engine is cosign-signed (verify it / pin by digest);
claudeinstalls from Anthropic’s signed repos with the key fingerprint pinned. The installer refuses to run an unverified engine unless you explicitly opt out. - Anchored audit. Every lifecycle transition and every workspace mutation is sealed in
the hash-chained, signed ledger by
PayloadHash— the bytes of files and the contents of frames are never persisted.
See also
Section titled “See also”- Connect Claude Code — the cooperative observe path.
- Security & hardening — the engine’s baseline posture.
- Verify a release — cosign / SBOM / SLSA verification.
- INSTALL.md — the install matrix, including this co-deployment.