Skip to content
docker-compose for Claude Code agents

Autonomous agents,
contained by the kernel.

clem runs a team of Claude Code agents 24/7 on any Linux host you own. Each agent is a real OS user. Its secrets and network access are controlled by the kernel and a separate user, not by the agent itself.

Get started on GitHub Read the security model
quickstart
# install the latest release (Linux x86-64)
$ curl -fsSL https://github.com/jahwag/clem/releases/latest/download/clem_linux_amd64 \
    -o /usr/local/bin/clem && chmod +x /usr/local/bin/clem

# scaffold config, provision the OS users, start the fleet
$ clem init && clem vault init
$ sudo clem provision && sudo clem login && sudo clem up

Open source · Go · MIT licensed · runs on any host with systemd

The problem

An autonomous agent is an untrusted workload.

Agents need secrets and network access to do real work. But you can't trust an autonomous agent to hold a raw credential, or to decide for itself what it's allowed to reach.

Telling the agent "don't leak this" is a policy, not containment. If enforcement depends on the agent cooperating, it isn't enforcement.

The answer

Contain it at the OS layer.

clem puts the boundary below the agent. Each one runs as its own non-root Linux user; secrets and egress are enforced by the kernel and a separate user the agent can't read or override.

  • A non-root agent cannot disable a firewall it doesn't own.
  • A brokered agent holds placeholders, never the real secret.
  • Enforcement lives in the OS layer, below the agent.
Security model

Every credential takes one disposition.

Four ways to contain what an agent can hold and where it can reach. Each is enforced by the kernel and a separate user, never by the agent.

kernel
Egress firewall

Per-UID nftables rule

A per-UID nftables rule forces all traffic through pipelock, an auditing loopback proxy. A non-root agent can't disable a firewall it doesn't own.

separate user
Secret-zero broker

The agent holds a placeholder

The agent carries only a placeholder; a separate user (agent-vault) injects the real credential on egress. The agent never sees the secret. cat ~/.env yields nothing usable for the brokered keys.

separate user
Sidecar

Key stays in another user

For credentials the broker can't reach (non-HTTP protocols, or tools that need the raw key in-process), the tool runs as a separate user behind a loopback MCP endpoint. The agent calls it but never holds the key.

n/a
Remove

Not present at all

Unused secrets simply aren't present. No placeholder, no broker, nothing to leak.

Containment is enforced below the agent. The real credential lives in a vault owned by a separate user the agent cannot read, and egress is pinned by a kernel rule the agent cannot rewrite. Compromise the agent and the boundary still holds.

Built on pipelock (egress proxy) Infisical agent-vault (secret broker) composed at the OS layer by clem.
How it works

Just systemd, tmux, and a loop.

Each agent is a systemd service running a tmux session that loops claude on a timer. Parts your host already has: systemdtmuxrunner loopclaude.

Linux host: laptop · Pi · VPS
OS user: myteam-lead
systemd service
tmux session
runner loop
claude TUI
OS user: myteam-worker
systemd service
tmux session
runner loop
claude TUI
↑ coordinate over MCP (stdio) ↑
watchdog timer (every 5m): restarts dead or stalled agents → #alerts
Discord or Slack  ·  #general #tasks #lessons
clem.yaml
# clem.yaml: one file describes the whole team
project: myteam            # OS user prefix → myteam-lead

coordination:
  backend: discord         # or: slack
  server_id: "${DISCORD_SERVER_ID}"
  channels:
    tasks:  "${DISCORD_TASKS_CHANNEL}"
    alerts: "${DISCORD_ALERTS_CHANNEL}"

# egress disposition: kernel-pin all traffic to a loopback proxy
egress:
  enabled: true
  domains: ["*.anthropic.com", "github.com"]

agents:
  lead:                    # → OS user myteam-lead
    name: Atlas
    role: Lead Software Engineer
    model: claude-sonnet-4-6
    effort: high           # extended-thinking budget per session
    iteration: 10m
  worker:
    name: Scout
    role: Software Engineer
    model: claude-sonnet-4-6
    effort: medium
    iteration: 5m

# broker + sidecar dispositions: see samples/secure-fleet/
What you get

Real Linux users, real systemd, real PRs.

Each agent is a normal Linux user. Log in, attach to its tmux, read its logs. It's all right there on your box.

Per-agent OS identity

Each agent is its own Linux user, with its own home dir, git identity, GitHub PRs, and bot account. One agent crashing can't take down another.

Kernel egress containment

A per-UID nftables rule routes every byte through an auditing proxy. The agent can't disable what it doesn't own.

Secret-zero broker

Agents hold placeholders; a separate user injects real credentials on egress. The secret never lands in the agent.

Privileged MCP sidecars

Secret-holding tools run as another user behind a loopback MCP endpoint. The agent reaches it but never holds the key.

24/7 self-healing

A systemd watchdog restarts dead or stalled agents. Alerts fire only after repeated failures.

Discord / Slack coordination

Agents pick up tasks and report in a channel you already watch. Swap backends with one config knob.

Runs on any Linux host

Anything with systemd: a laptop, a Raspberry Pi, a home server, a small VPS.

Self-hosted, your infra

Everything runs on the host you control. Source, secrets, and MCP servers all stay local.

In production

clem runs the consultant.dev engineering team.

Agents review and ship real pull requests around the clock, coordinating over a Discord channel. Each one is a separate OS user, contained below the agent by the OS and a separate user.

live · 24/7 under systemd

Run your fleet.

One config file describes the whole team. Free and open source · MIT licensed.

install
$ curl -fsSL https://github.com/jahwag/clem/releases/latest/download/clem_linux_amd64 \
    -o /usr/local/bin/clem && chmod +x /usr/local/bin/clem

Runs on any Linux host with systemd.