Self-hosted AI agents you do not have to trust.

Each one runs sealed in a sandbox that provably cannot phone home, read your host, or rewrite its own rules.

OpenSSF Scorecard OpenSSF Best Practices CodeQL Signed releases (cosign) SBOM: SPDX + CycloneDX SLSA provenance Sandbox scan: PR scorecard Sandbox Isolation Score

Documentation Status: alpha Latest release Go Reference License: AGPLv3 + Commercial GitHub Discussions Good first issues GitHub stars

IronClaw runs autonomous AI agents on infrastructure you control, reached through the chat apps you already use. Each agent can read, write, schedule, and reply like any assistant, but it lives inside a sealed sandbox with network=none: it reaches the model only through a host proxy, and it cannot change its own configuration. It is for anyone who wants what agents can do without handing an autonomous program the keys to their machine.

Now on the GitHub Marketplace. The ironctl scan containment grader ships as a GitHub Action: drop one line into a workflow and every pull request gets a 0 to 100 sandbox isolation scorecard as a sticky comment. Local, read-only, credential-free.

# .github/workflows/scan.yml
- uses: IronSecCo/ironclaw@v1
  with:
    target: my-container   # a container, compose service, or k8s manifest

Report-only by default; set min-score: 90 to gate merges. See scan in CI.

Watch it catch a real escape. A fully-jailbroken agent inside a real sandbox tries to phone home, read the host filesystem, and seize the host through the Docker socket. Each attempt is denied at the isolation boundary, then a containment summary prints. One command, zero credentials, reduced-motion friendly. examples/live-containment/run.sh

⭐ Like the idea of agents you do not have to trust? Star the repo so it is one click to follow along and easier for the next person to find. Then run the exact demo above in 30 seconds, no signup and no API key.

Try it in 30 seconds (zero credentials)

Make sure the Docker daemon is running (start Docker Desktop, or sudo systemctl start docker on Linux), then paste one block:

git clone https://github.com/IronSecCo/ironclaw.git && cd ironclaw
examples/live-containment/run.sh   # builds the sandbox once, engages a real sandbox, proves it holds

That single command runs the whole secured path on your laptop: it starts the offline mock-agent control-plane (no API key), engages a real per-session sandbox, lets a jailbroken agent try to break out, and prints the containment summary you saw above. Want to chat with an agent in a browser first? Run hello-ironclaw or the zero-credential quickstart. Production seals each sandbox with gVisor and network=none.

[!WARNING] Alpha software, work in progress. Please read before relying on it.

  • It's an alpha. Flags, the on-disk format, and the HTTP/contract surfaces can still change without notice or a migration path. Don't point it at anything you can't afford to lose.
  • Not every feature is tested end-to-end. The control-plane, gateway, and encrypted-queue core have real coverage (800+ Go tests plus a black-box parity suite); channel adapters, some tools, multi-provider routing, and a live sandbox launch are exercised more lightly. Treat anything outside the tested core as experimental.

macOS gets a weaker sandbox boundary than Linux+gVisor, and native Windows can't run the agent sandbox at all (use WSL2). See Platform support.

The security model, in one line: each sandboxed agent runs with network=none, reaches the model only through a host proxy, and cannot change its own configuration. Every capability change is held at a gateway for a human decision. The full design is in the architecture overview and the threat model.

See the whole journey, end to end

Zero credentials, one command. The offline mock-agent runs the full chat to per-session sandbox to reply path with no API key. Production seals each sandbox with gVisor and network=none. Quickstart

Zero-cred demo, connect a real provider, first approved task. The one credential step keeps the key host-side; every agent change is held at the gateway for a human, then written to the append-only audit log. Animation freezes on the final frame under prefers-reduced-motion. Quickstart

Get running in under two minutes

One command installs the two host binaries (ironctl + ironclaw-controlplane); in dev mode the control-plane serves its API at http://127.0.0.1:8787. From a cold machine, you'll have a capability change waiting at the security gateway in under two minutes:

# 1. Install — detects your OS/arch and verifies the SHA-256 checksum before installing
curl -fsSL https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.sh | sh

# 2. Start the control-plane in dev mode — API base URL: http://127.0.0.1:8787
export IRONCLAW_API_TOKEN=$(openssl rand -hex 32)
ironclaw-controlplane --dev --api-addr 127.0.0.1:8787 &

# 3. Your first command — submit a change; it is HELD at the gateway for a human decision
ironctl change submit --kind persona --group dev-agent --by you
ironctl change pending                       # see it waiting
ironctl change approve <change-id> --by you   # apply it

On Windows, irm https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.ps1 | iex installs the host binaries (ironclaw-controlplane.exe + ironctl.exe) and --dev runs, but the agent sandbox needs WSL2 or Linux — see Windows via WSL2. Version pinning, system-wide installs, and building from source are all in Installation.

One-click cloud deploy

Run the hardened control-plane on a PaaS in ~2 minutes with zero local tooling — the approval gateway, encrypted per-session queues, host-side credential custody, and the web console:

Deploy to Fly.io Deploy to Render Deploy on Railway

These PaaS paths run the control-plane only — a single container has no gVisor and no Docker socket, so agent sandboxes don't launch there (same boundary as the hardened Compose path). For full agent isolation use a gVisor host or k8s node. Details + env in the deployment guide (Path D).

CLI-first and API-first

This is a feature, not a missing dashboard. Every capability is a documented HTTP endpoint and an ironctl subcommand, so IronClaw is scriptable, auditable, and CI-friendly from the first command — with no public web surface to phish, misconfigure, or leave exposed. (There is now a private, mesh-only web console at /ui/ — but it's additive, never the only way in, and rides the same Tailscale-bound API, so it adds no public port.)


Why it's different

Pillar What it is Attack surface it removes
Sealed runtime The agent ships as a compiled Go binary Agent self-modification — there's no source inside the box to rewrite
Approved by humans Every change to the harness clears a deterministic gateway Silent setting changes — nothing changes without a human seeing and approving it
Encrypted queues Per-session encrypted message queues; read-only inbound Data theft at rest, and cross-session reads
Sealed sandbox gVisor container, no network, host-proxied model calls Data exfiltration and sandbox escape
Private control panel Admin access over a private mesh (Tailscale) only Remote attacks on the controls

The throughline: treat the agent as untrusted, and make the security boundary something you can verify — not something you take on faith.

⚖️ Weighing your options? See Why IronClaw / vs. the alternatives for an honest comparison against hosted agent platforms, raw container + LLM glue, and other self-hosted agent runtimes.

Audit your own sandbox in 10 seconds

Do not take our word for any of that. ironctl scan grades the containment posture of any running container, docker-compose service, or Kubernetes pod on a 0 to 100 scale. It works on your own setups, not just IronClaw's, so you can measure how much isolation you actually have before you hand a sandbox to untrusted code. It is fail-closed: any boundary it cannot observe is scored insecure, never waved through.

ironctl scan my-container

It also grades a Dockerfile statically, at authoring or CI time, with no daemon and no image pull, so you catch a leaked credential, an unpinned base, or a root default in review instead of in production:

ironctl scan --dockerfile Dockerfile --min-score 80

Grade Dockerfiles automatically on every commit with the pre-commit hook, which builds ironctl from source, so there is nothing to install first:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/IronSecCo/ironclaw
    rev: v0.1.x
    hooks:
      - id: ironclaw-scan-dockerfile
        args: [--min-score=80]   # fail the commit below grade B

Walkthrough: How to scan a Dockerfile for security issues takes a deliberately bad Dockerfile from 5/100 (F) to 100/100 (A), one fix at a time.

A container started the usual way (root user, default caps, bridge network, docker.sock mounted in) grades 23/100, F. An IronClaw ic-sbx-* session sandbox grades a clean 100/100, A:

Target Score Grade Posture
Typical docker run container 23/100 F runs as root, docker.sock mounted, writable rootfs, bridge egress
IronClaw session sandbox 100/100 A non-root, all caps dropped, seccomp on, network=none, read-only rootfs, gVisor

Every failing line names the specific hole and why it matters. Drop the grade into your own README with ironctl scan --badge scan.svg, or gate CI with ironctl scan --min-score 90.

See the scan reference for all seven dimensions and every flag. Or browse the Container Isolation Scores directory: the default-config grade for 150+ of the most-pulled public images, so you can see how the containers you already run stack up. Rankings live on the Container Isolation Leaderboard (Hall of Fame vs worst offenders), and the interactive scores explorer lets you filter and grab a badge for your repo.

Head-to-head reads backed by the same scan data: Alpine vs Debian vs Ubuntu (does the base image change isolation?), Docker default vs hardened (the 48-point gap, flag by flag), and gVisor vs runc (when a shared host kernel is the weak link).

Per-image hardening walkthroughs, the default grade, the dimensions that fail, and the exact ironctl scan --fix flags that close the gap. Start at the hardening guides hub, or jump to one: Postgres, MySQL, MariaDB, MongoDB, Cassandra, ClickHouse, Redis, Memcached, Elasticsearch, Kafka, RabbitMQ, Vault, Consul, MinIO, nginx, Grafana, Prometheus, Traefik, InfluxDB, CockroachDB, TimescaleDB, Valkey, HAProxy, and untrusted Node.js.

Show your score

Put your containment grade in your README, the same way a coverage or build badge does. Generate a shields.io endpoint file, commit it (no server, so a badge hit never triggers a remote scan), and embed it:

ironctl scan my-container --badge-json .ironclaw/sandbox-isolation.json
git add .ironclaw/sandbox-isolation.json && git commit -m "chore: add sandbox isolation badge"
[![Sandbox Isolation Score](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/OWNER/REPO/main/.ironclaw/sandbox-isolation.json)](https://ironsecco.github.io/ironclaw/scan/)

The badge at the top of this README is IronClaw's own, graded 100/100 A from .ironclaw/sandbox-posture.yml. Full walkthrough: Add a live Sandbox Isolation Score badge to your repo.

How it works

Two compiled Go programs that never share memory and talk only through a pair of encrypted SQLite files per conversation:

flowchart TB
  CHAT["Chat platforms<br/>12 channel adapters"]
  CLI["ironctl CLI"]
  WEB["Web console"]

  subgraph host["Trusted host — control-plane (cmd/controlplane)"]
    API["HTTP API<br/>Tailscale mesh-only + bearer"]
    GW["Gateway<br/>deterministic verifiers · human approval"]
    CORE["Router · delivery · sweep · key custodian"]
    CHAD["Channel adapters"]
    MP["Model proxy<br/>holds provider keys"]
    ISO["Isolation launcher<br/>gVisor / runsc"]
  end

  subgraph queues["Encrypted SQLCipher queues · per session"]
    INQ[("inbound.db<br/>read-only to agent")]
    OUTQ[("outbound.db<br/>append-only by agent")]
  end

  subgraph box["Agent sandbox · gVisor · network=none"]
    LOOP["Agent loop · tools · model provider"]
  end

  PROV["Model providers<br/>Anthropic · OpenAI · OpenRouter"]

  CHAT <--> CHAD
  CLI -->|mesh only| API
  WEB -->|mesh only| API
  CHAD --> CORE
  API --> GW --> CORE
  CORE -->|write| INQ
  OUTQ -->|read| CORE
  ISO -->|launch| LOOP
  INQ -->|ro bind mount| LOOP
  LOOP -->|append| OUTQ
  LOOP -->|unix socket| MP -->|HTTPS · key injected host-side| PROV

  classDef host fill:#eaf2ff,stroke:#1d4ed8,stroke-width:1px,color:#0b1124;
  classDef store fill:#b9d4ff,stroke:#1d4ed8,stroke-width:1px,color:#0b1124;
  classDef box fill:#1d4ed8,stroke:#63a0ff,stroke-width:2px,color:#ffffff;
  classDef control fill:#16224a,stroke:#63a0ff,stroke-width:2px,color:#ffffff;
  classDef ext fill:#f4f9ff,stroke:#8fb4ff,stroke-width:1px,color:#16224a;

  class API,CORE,CHAD,MP,ISO host;
  class GW control;
  class INQ,OUTQ store;
  class LOOP box;
  class CHAT,CLI,WEB,PROV ext;
  • The control-plane receives chats, routes them, holds the keys, runs the approval gateway, and performs every privileged action on the agent's behalf — after its own checks.
  • The sandbox — one per conversation, wrapped in gVisor with no network of its own — reads its encrypted inbox (read-only), calls the AI model through the host proxy, and writes its encrypted outbox. It can request a capability change but can never apply one.
  • The frozen contract (internal/contract) is the only package both sides import: typed IDs, row shapes, the embedded SQL schema, pinned cipher params, and the gateway protocol.

A single message rides a clean loop; anything that would change what the agent can do takes the separate dashed path through the human-approval gateway:

flowchart LR
  SENDER["External sender<br/>Slack · email · …"]
  ADAPTER["Channel adapter"]
  ROUTER["Router<br/>authorize + fan-out"]
  INQ[("inbound.db")]
  LOOP["Agent loop"]
  MODEL["Model provider"]
  OUTQ[("outbound.db")]
  DELIVERY["Delivery"]
  GW{"Gateway<br/>human approval"}
  APPLY["Control-plane<br/>applies change"]

  SENDER -->|message| ADAPTER --> ROUTER
  ROUTER -->|write · encrypted| INQ
  INQ -->|ro| LOOP
  LOOP <-->|model call via host proxy| MODEL
  LOOP -->|reply · append| OUTQ
  OUTQ --> DELIVERY --> ADAPTER
  ADAPTER -->|reply| SENDER
  LOOP -.->|capability-change request| GW
  GW -.->|approved| APPLY

  classDef host fill:#eaf2ff,stroke:#1d4ed8,stroke-width:1px,color:#0b1124;
  classDef store fill:#b9d4ff,stroke:#1d4ed8,stroke-width:1px,color:#0b1124;
  classDef box fill:#1d4ed8,stroke:#63a0ff,stroke-width:2px,color:#ffffff;
  classDef control fill:#16224a,stroke:#63a0ff,stroke-width:2px,color:#ffffff;
  classDef ext fill:#f4f9ff,stroke:#8fb4ff,stroke-width:1px,color:#16224a;

  class ADAPTER,ROUTER,DELIVERY,APPLY host;
  class INQ,OUTQ store;
  class LOOP box;
  class GW control;
  class SENDER,MODEL ext;

For the full design, see docs/architecture.md, docs/threat-model.md, and the plain-language tour in docs/ironclaw-explained.md.

📚 Full documentation site: ironsecco.github.io/ironclaw — quickstart, architecture, threat model, channels, skills, the OpenAPI reference, and security, all in one navigable place (built from docs/ and published on every push to main).

🧭 New here? The hands-on tutorials take you from git clone to a running agent: your first sandboxed agent in 5 minutes, connecting Slack, and writing a custom channel adapter.

Platform support

IronClaw's security model rests on gVisor (runsc) — a user-space kernel that intercepts the agent's Linux syscalls and is the layer that actually enforces network=none, the seccomp syscall allowlist, dropped Linux capabilities, and a read-only rootfs. gVisor is Linux-only, and that one fact drives the whole platform story:

Capability Linux + gVisor (production target) macOS / Windows
Host side — control-plane, gateway, API, ironctl, web console ✅ native ✅ native (incl. native Windows)
Real agent sandbox ✅ gVisor (runsc) ⚠️ --runtime docker only — runc in Docker Desktop's Linux VM. macOS: Docker Desktop. Windows: WSL2 (native Windows can't reach it — see below)
Per-sandbox syscall interception ❌ not available
Seccomp syscall allowlist ✅ enforced ❌ not applied on the Docker path
network=none ✅ enforced by the OCI spec ⚠️ not auto-enforced — you must point IRONCLAW_DOCKER_NETWORK at a no-egress network
Dropped capabilities · read-only rootfs ✅ enforced by the runtime ⚠️ only as strong as the Docker Desktop VM kernel

On macOS you can build, script, demo, and develop against the entire system natively, and you can even run agents through Docker Desktop — but understand that the sandbox boundary then comes from runc inside the Docker Desktop Linux VM, not gVisor. There is no per-sandbox syscall interception, the curated seccomp profile is not applied, and network=none is not enforced for you (the Docker isolator passes whatever network you configure straight through — set IRONCLAW_DOCKER_NETWORK to a no-egress bridge yourself). That is weaker than the posture the threat model assumes.

Windows via WSL2

The install.ps1 PowerShell installer gives you the host plane natively on Windows: ironclaw-controlplane.exe and ironctl.exe run, the encrypted SQLCipher queue works, and --dev mode (no real sandbox) runs end-to-end. A real agent sandbox does not run on native Windows — gVisor (runsc) is Linux-only, and the Docker fallback talks to the Docker Engine over a Unix socket (/var/run/docker.sock), which native Windows Docker Desktop does not expose (it serves a Windows named pipe instead). So on native Windows you get the control plane and ironctl, but the agent runtime has nowhere to launch.

To actually run agents on Windows, use WSL2:

wsl --install -d Ubuntu          # one-time: install WSL2 + Ubuntu, then reboot

Then, inside the WSL2 Ubuntu shell, install the Linux build and run it exactly as on Linux:

curl -fsSL https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.sh | sh

Inside WSL2, /var/run/docker.sock is present (Docker Desktop's WSL integration, or Docker installed in the distro), so IRONCLAW_RUNTIME=docker launches real Linux sandbox containers. For the full gVisor posture, install runsc inside the WSL2 distro just as you would on bare-metal Linux. Treat a WSL2 host the same as the Linux row above.

For anything past local development, run the sandbox host on Linux with gVisor (bare-metal, a VM, or WSL2). The control plane can live wherever you like — including native Windows — but it's the agent sandbox that needs the Linux + gVisor substrate to give you the boundary IronClaw is built around.

Project status

Alpha. The architecture is settled and the full control-plane and sandbox pipelines are implemented and tested. The encrypted-queue binding is now wired:

  • Encrypted-SQLite queue binding — ✅ wired (RFC-0001 applied). contract.Open* open per-session SQLCipher databases via cgo (github.com/mutecomm/go-sqlcipher/v4); a round-trip test covers write→read, read-only-write rejection, wrong-key failure, and no-plaintext-on-disk. The build now requires CGO_ENABLED=1 (a C toolchain). internal/host/queue uses the live binding; in-memory backends remain for --dev and tests.
  • Sandbox rootfs provisioning — ✅ wired via a pluggable provisioner: isolation builds the hardened OCI spec, provisions the bundle rootfs (with image digest/signature verification against a trust policy), and execs runsc. A real launch still needs runsc and a provisioned/signed image present in the environment.
  • Production hardening (Wave 4) — durable/pluggable master-key custody, a Prometheus /metrics surface, structured logging, host respawn + sandbox provider backoff, and model-proxy rate caps/audit/redaction have landed and are composed into cmd/controlplane. The API-server hardening knobs (optional TLS, rate-limit, body limits, /readyz readiness gate) exist as api.With* options but aren't attached in the entrypoint yet (see the roadmap).

See the roadmap for what remains. You can build, test, and run the control-plane today; a live sandbox launch needs runsc plus a provisioned image.

Prerequisites

Requirement For Notes
Go 1.23+ and a C toolchain building everything CGO_ENABLED=1 is required — the encrypted-SQLite binding builds via cgo
containerd + gVisor (runsc) production sandboxing runtime io.containerd.runsc.v1; not needed for --dev
Tailscale remote admin access the control-plane API binds to the tailnet IP; no public port
SQLCipher (vendored) encrypted queues the SQLCipher C amalgamation is vendored by the driver; no system lib needed
A model credential live model calls an Anthropic / OpenAI / OpenRouter key, or a gateway like OneCLI — injected host-side into the model proxy, never into the sandbox (Model providers)

The three external runtime dependencies (gVisor, Tailscale, the encrypted-SQLite binding) are intentionally not vendored. See deploy/README.md for host setup.

Installation

Homebrew (macOS / Linux)

brew tap IronSecCo/ironclaw https://github.com/IronSecCo/ironclaw
brew install ironsecco/ironclaw/ironclaw

This installs ironctl, ironclaw-controlplane, and ironclaw-sandbox from the latest release. The formula pins each archive to the SHA-256 recorded in the release's signed SHA256SUMS, so Homebrew verifies the download before installing. Confirm it with ironctl version.

Homebrew always installs the latest release. To pin an older version, use the installer script's IRONCLAW_VERSION (below) or grab the archive by hand — the tap carries only the current release.

Use the fully-qualified name. homebrew-core ships an unrelated formula also called ironclaw, and core wins the bare name — so install ironsecco/ironclaw/ironclaw, not bare ironclaw. The explicit tap URL is required too: our tap lives in this repo, not a homebrew-ironclaw repo.

In production the control plane usually runs as the GHCR container image (see the deployment guide); the native ironclaw-controlplane binary is convenient for local / --dev runs.

Prebuilt binaries (installer script)

One command installs the latest release — ironctl and ironclaw-controlplane. The script detects your OS/arch, downloads the matching archive from GitHub Releases, and verifies its SHA-256 checksum before installing.

macOS / Linux

curl -fsSL https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.sh | sh

Windows (PowerShell)

irm https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.ps1 | iex

This installs the host binaries (ironclaw-controlplane.exe + ironctl.exe) and runs --dev natively, but it cannot run a real agent sandbox — that needs Linux. To run agents on Windows, install inside WSL2; see Windows via WSL2.

A fresh release is published on every push to main, with prebuilt archives for:

OS Architectures
macOS Intel (amd64) · Apple Silicon (arm64)
Linux amd64 · arm64
Windows amd64

The installer reads a few environment variables (pass them on the sh side of the pipe):

# Pin a version instead of latest
curl -fsSL https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.sh | IRONCLAW_VERSION=v0.1.102 sh

# Install system-wide (a normal user defaults to ~/.local/bin)
curl -fsSL https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.sh | sudo sh

# Choose the install directory
curl -fsSL https://raw.githubusercontent.com/IronSecCo/ironclaw/main/scripts/install.sh | IRONCLAW_BINDIR="$HOME/bin" sh

Then confirm what you installed:

ironctl --version

Prefer to grab files by hand? Download the archive and SHA256SUMS for your platform from the latest release.

Version managers (mise / asdf)

Pin IronClaw per project with mise or asdf, no account and no sudo. The quickest path uses mise's ubi backend to install the ironctl CLI straight from the GitHub release (no plugin repo):

mise use -g "ubi:IronSecCo/ironclaw[exe=ironctl]@latest"
ironctl --version

For both host binaries (ironctl + ironclaw-controlplane) and a pinned .tool-versions, use the asdf-style plugin under packaging/asdf-ironclaw/. It downloads the release tarball, verifies it against the published SHA256SUMS, and drops both binaries on the managed PATH:

ironclaw 0.1.217

The plugin resolves as a standalone repo (asdf clones plugins by URL), so asdf plugin add ironclaw and mise use asdf:... become available once the plugin lands in its own IronSecCo/asdf-ironclaw repository. Until then the scripts in packaging/asdf-ironclaw/ are runnable directly (see that directory's README.md).

Verifying a release

Releases are signed and attested — a keyless cosign signature over SHA256SUMS, an SBOM (SPDX + CycloneDX), and build-provenance attestations for every archive and the container image. For how releases are cut, verified, and yanked, see the release runbook.

Each release carries SHA256SUMS plus SHA256SUMS.sig + SHA256SUMS.pem (the cosign signature and its certificate), *.spdx.json / *.cdx.json SBOMs, and per-archive + image attestations.

Verify the checksum signature (no key to manage — the identity is the release workflow):

cosign verify-blob SHA256SUMS \
  --signature SHA256SUMS.sig --certificate SHA256SUMS.pem \
  --certificate-identity-regexp '^https://github.com/IronSecCo/ironclaw/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum -c SHA256SUMS        # then confirm your archive matches

Verify build provenance for an archive, an extracted binary, or the image:

gh attestation verify ironclaw_<version>_<platform>.tar.gz --repo IronSecCo/ironclaw
gh attestation verify ./ironctl --repo IronSecCo/ironclaw   # a binary extracted from the archive
gh attestation verify oci://ghcr.io/ironsecco/ironclaw-controlplane:latest --repo IronSecCo/ironclaw

The container image also carries a signed SBOM attestation (CycloneDX) you can verify and read anonymously:

gh attestation verify oci://ghcr.io/ironsecco/ironclaw-controlplane:latest \
  --repo IronSecCo/ironclaw \
  --predicate-type https://cyclonedx.org/bom

Every third-party GitHub Action is pinned to a commit SHA, builds use a pinned toolchain + -trimpath and are checked for bit-for-bit reproducibility by a double-build CI job (ironctl and sandbox are verified byte-identical; the larger control-plane binary is reproducible under newer Go and tracked for the pinned toolchain), and the project's supply-chain posture is scored continuously by OpenSSF Scorecard (see the badge above).

From source

Requires Go 1.23+ and a C toolchain (CGO_ENABLED=1 — the encrypted-SQLite binding builds via cgo).

# Clone
git clone https://github.com/IronSecCo/ironclaw.git
cd ironclaw

# Build all binaries
make build            # == go build ./...

# Or install the two host binaries onto your PATH
go build -o /usr/local/bin/ironclaw-controlplane ./cmd/controlplane
go build -o /usr/local/bin/ironctl               ./cmd/ironctl

For a full system install — build and install the binaries, provision /etc/ironclaw and /var/lib/ironclaw, and enable the service (systemd on Linux, launchd on macOS) — run sudo deploy/install.sh. It needs root to write under /etc and /var/lib. The external runtime dependencies it relies on (containerd + gVisor and Tailscale) are set up separately — see deploy/README.md.

With Docker (docker compose)

Self-host the control-plane in one command. From a clone:

cp .env.example .env          # fill in ANTHROPIC_API_KEY (optional to boot)
docker compose up -d          # builds locally on first run, or pulls the GHCR image
docker compose logs -f controlplane   # CLAIM the admin token printed once on first run

The admin/API token is minted on first run and printed once in the logs (there is no recovery) unless you set IRONCLAW_API_TOKEN yourself. The admin API is published on 127.0.0.1:8787 only — front it with Tailscale for remote access.

Prefer the published image? It is pushed to GitHub Container Registry on every release