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. To launch Claude, Codex or Grok under a provider profile after the runtimes exist, see Operate a provider session.
Two principles before you start
Section titled “Two principles before you start”- Opt-in. The Olivares image carries no
claude. In Docker, Olivares installs the agent tools into its data volume and signs them in through the product when you ask for them; natively, the install add-on below does it. If you do not run governed Claude Code, nothing is installed. - 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 install time (natively the signed apt/dnf/apk repositories; in the image Anthropic’s signed release, checked against the same key), 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 (the standard 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 | Per-session container (podman or Docker) | Per-session container through the container CLI | Available where the host has podman or Docker |
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, and CI runs the image’s agent
runtime check (scripts/qualify-container-agent-runtime.sh). Topology 3 wants the governor and the governed in
different namespaces, which the engine cannot bridge. Topology 4 starts the session
container with the host’s own container CLI, so it takes no Docker-API access. Both are
spelled out in Mixed topologies.
Topology 1 — both in Docker (recommended)
Section titled “Topology 1 — both in Docker (recommended)”The standard engine container runs claude as well: Olivares installs the agent tools into
its data volume and signs them in through the product. Non-root, read-only
root filesystem — the base compose posture, with no second image and no override file.
Bring it up
Section titled “Bring it up”Pin the engine by digest and verify it first:
# verify the engine image you run (it is cosign-signed)cosign verify docker.io/olivaresai/olivares:0.4 \ --certificate-identity-regexp '^https://github\.com/olivaresai/olivares/\.github/workflows/release\.yml@refs/tags/[0-9]+\.[0-9]+$' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com
export OLIVARES_IMAGE=docker.io/olivaresai/olivares@sha256:<digest>export OLIVARES_BIND=127.0.0.1docker compose -f deploy/compose/docker-compose.yml up -dInstall and sign in Claude Code
Section titled “Install and sign in Claude Code”From AI tools in the console, or with the CLI:
olivares tool install claudeolivares tool login claudeThe tool lives in the data volume and the sign-in belongs to your organization; sessions never use a sign-in stored in the server account’s home.
To see what the tool itself reports about a login (the email, masked, the plan, the usage
windows with their reset time, and the models), run olivares tool providers. It also lists
the login of the user that runs the engine, read through the tool’s own commands and never from
its credential files; it is shown for information, and sessions still use only the sign-in made
through Olivares.
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.
Download, verify, then run
Section titled “Download, verify, then run”Do not pipe the installer into a shell. Verify the release’s signed checksum list, check out
the exact commit it names, and run the installer from that checkout. It then reads
install.sh and its packaging files from the checkout, never from a branch. The block runs
in a subshell with set -eu, so it stops at the first failed step and never reaches the
installer after a failed check.
(set -eucd "$(mktemp -d)"ver=0.4base=https://github.com/olivaresai/olivares/releases/download/$vercurl -fsSLO $base/checksums.txtcurl -fsSLO $base/checksums.txt.sigcurl -fsSLO $base/checksums.txt.pemcurl -fsSLO $base/release-commit.txtcosign verify-blob \ --certificate checksums.txt.pem --signature checksums.txt.sig \ --certificate-identity "https://github.com/olivaresai/olivares/.github/workflows/release.yml@refs/tags/$ver" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ checksums.txtgrep ' release-commit.txt$' checksums.txt | sha256sum --checkgit clone https://github.com/olivaresai/olivares.gitcd olivares && git checkout --detach "$(cat ../release-commit.txt)"OLIVARES_TOPOLOGY=native OLIVARES_VERSION=$ver sh scripts/install-agentops.sh)It 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), ensures the workspace dir exists, and restates the base unit’s file-system policy:ProtectSystem=full,ProtectHome=false,PrivateTmp=falseandMemoryDenyWriteExecute=false(theclauderuntime JIT-compiles and needs W→X memory). Homes and/tmpstay visible to the engine so the folders you choose stay reachable; each session’sclaudeand its stdio MCP servers run under the product’s per-folder Landlock confinement instead. 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 # listens on every interface 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 standard 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”Each session runs in a brand-new container of its own: the root filesystem is read-only,
no capability is kept, no privilege can be gained, and the session sees only its workspace,
the tool’s account home and the tool’s own program, mounted from the host. The engine
starts it with the host’s container CLI (podman run, or docker run) and bridges its
stdio exactly as it does for a native child. The engine needs no Docker socket and no
extra privilege: it runs the CLI as its own user, which is rootless with podman.
olivares session start . --isolation containerThe runtime. The engine uses podman when it is installed and Docker otherwise; podman
is what AlmaLinux and RHEL 10 ship. OLIVARES_SESSION_CONTAINER_RUNTIME pins podman or
docker, or turns container isolation off. To use Docker CE on an Olivares Server,
install it from Docker’s official repository for your distribution, remove podman-docker
(it owns the docker command), and set OLIVARES_SESSION_CONTAINER_RUNTIME=docker.
Restart the engine; olivares doctor shows the runtime it found.
The image. OLIVARES_SESSION_CONTAINER_IMAGE names it. The default,
localhost/olivares-session:latest, is the name an Olivares Server image carries its session
image under; nothing in this repository builds it, so on any other host name an image you have.
It must be compatible with the host’s tool program, which is mounted into it, and hold what
the tool shells out to: a shell and git.
Rootless and labels. podman runs rootless when the engine runs as a user other than root,
which is how the service user of a server runs it. An engine that runs as root gets rootful
containers, as it gets root sessions. On a host that enforces SELinux the session’s mounts
carry the z label, which changes the label of the folder and the account home for good, and
a large folder takes time to relabel.
Governance and network. A server that governs tool calls with hooks (the default) keeps
them in the container. The hook program and the run’s settings are mounted read-only, the
host’s Claude Code managed settings are mounted where the container reads them
(/etc/claude-code), and a hook that cannot run in the image (another architecture, no
/bin/sh) blocks the tool call instead of letting it through. The hooks call the engine on
its loopback address, so a container whose environment names the engine there shares the
host’s network. Be clear about what that means: the container isolates files, processes and
privileges, and its network is the host’s, as a native session’s is. It does not isolate
the network, and it does not scope the host’s abstract Unix sockets the way a native
session’s Landlock policy does. A container that names no engine endpoint keeps the
runtime’s own network.
What it does not do yet. A session bound to a provider’s endpoint from Providers stays
native: its network boundary is a proxy and a network namespace that only the native runner
holds, and the engine will not start a container session that is meant to reach one endpoint
on a wider network. That includes a local model such as Ollama added in Providers. A tool that is a single program file works; one that needs its own directory beside it, or a
runtime such as Node, needs an image that holds it.
The image needs /bin/sh, git and what the tool shells out to. Assigned skills and git
worktrees need native isolation.
Co-location, the standard image, remains the default.
Security posture (all topologies)
Section titled “Security posture (all topologies)”- Published ports. Docker Compose publishes HTTPS and gRPC on every host interface
(
0.0.0.0) by default. SetOLIVARES_BIND=127.0.0.1, as in the command above, to restrict them to this host. The engine listens on0.0.0.0inside the container; the host port mapping controls exposure. Native/systemd listeners also bind every interface by default; configure their listen addresses to restrict access. - Non-root, least privilege. uid/gid 65532, read-only root filesystem,
cap_drop: ALL,no-new-privileges(Docker) / the kernel and namespaceProtect*/Restrict*directives, with homes and/tmpvisible, W^X allowed and each session confined to its folder by Landlock (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, with no verification bypass. - 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.