Every laptop runs a different agent.
CLI versions, models, permissions and MCP servers drift per machine, per team, per week.
One policy repo becomes signed releases, rolled out ring by ring, gated by evals and live telemetry, and killed automatically when a guardrail measured at the gateway breaks (with halo-server's kill list on).
Works with Claude Code · Codex · Gemini CLI · Copilot CLI · Cursor CLI · OpenCode
halo init --org acme create halos.yaml next: halo explain --policy-dir . (the full policy this expands to) next: halo validate --policy-dir . halo upgrade start claude-code 2.1.286 # needs HALO_REGISTRY + HALO_KEY, and ring3-ga already published as the baseline Signing ring ring1-canary.x-claude-code-2.1.286-canary.next → version claude-code-2.1.286-x-claude-code-2.1.286-canary.next (digest sha256:534762a6afe095d3bfd4991ea507241f9a301c68dd2ba3257f2555ff8325670a, seq 1790874803) ok published 127.0.0.1:5056/acme/halos vclaude-code-2.1.286 (sha256:8350dc789e85dfde1450643bc88f3818294f9fbf47b6c4389946644cca0d65ba); no ring pointer moved ok published channel ring1-canary.x-claude-code-2.1.286-canary.next (sha256:534762a6afe095d3bfd4991ea507241f9a301c68dd2ba3257f2555ff8325670a): reached only once the experiment runs create experiments/claude-code-2.1.286-canary.yaml create profiles/claude-code-2.1.286.yaml create rings/ring0-team.yaml create rings/ring1-canary.yaml create rings/ring2-early.yaml create rings/ring3-ga.yaml create rollouts/claude-code-2.1.286.yaml next: start it: merge this, then `halo rollout advance claude-code-2.1.286 --reason ...` opens the first step's PR next: after it completes, set halos.yaml tools.claude-code to 2.1.286 so new releases keep the pin halo rollout simulate claude-code-2.1.286 --scenario regression Simulating claude-code-2.1.286 (scenario regression, tick 1h, 1000 users/h, seed 1; PRs assumed merged at once) AT STEP DECISION WHY T+0 - advance not started: propose step ring0-team T+1h ring0-team hold bake: baking, 23h left T+1d ring0-team advance all gates passed: propose step canary-5 T+1d1h canary-5 hold bake: baking, 11h left; samples: 950/50 of 200 per arm; guardrail:halo.tool.error_rate: inconclusive, upper... T+1d4h canary-5 rollback primary failed: primary halo.tool.error_rate significantly worse (effect +0.09415, p=0.0000) (threshold not... Outcome: rollback after 1d4h
a23f464 against a local registry (HALO_REGISTRY and HALO_KEY set, --plain-http omitted from the display, and ring3-ga already published as the rollback baseline).Transcripthalo init --org acmeWrite policy: one git repo of YAML for CLI versions, models, permissions, MCP servers and rings.
halo release publishA human merges; halo signs an immutable release into your OCI registry.
halo rollout advanceRings point at releases; halod applies them on every machine. Each step is gated and reversible.
The problem
CLI versions, models, permissions and MCP servers drift per machine, per team, per week.
A new CLI or model reaches 100% of engineers on day one, with no canary and no numbers.
When it regresses, someone notices days later and asks everyone to downgrade by hand.
How it works
Your platform team owns one repo. Developers keep running their CLI and change nothing.
A git repo of Halos YAML: CLI versions, models, permissions, MCP servers, rings and rollouts. halo init scaffolds it; CI validates, plans and evals every PR.
A human merges; halo release publish signs an immutable, content-addressed release into your OCI registry. Nothing moves until a ring points at it.
Rings point at releases and halod applies its ring's release on every machine. Each rollout step bakes, counts samples, checks its gates and opens a PR a human merges.
A gateway-measured breach trips the signed kill switch; gateways pick it up within ~10 s. Other breaches open a rollback PR.
What you get
Progressive rings, canary ramps, blue-green, dark launch and holdout. Each step bakes, counts samples and checks its gates; a passed step opens a PR.
Learn more →A/B on the client, canary on the traffic path, both assigned per verified identity. Shadow mirrors sampled sessions to a candidate.
Learn more →Turn one MCP server, hook or model route on for a ring, group or percentage. Kill it fleet-wide with one signed flip.
Learn more →pass@k and pass^k, a pinned LLM judge, flake detection and a harness × model × provider matrix. Scorecards gate rollout steps; mSPRT judges live telemetry.
Learn more →Anthropic, Bedrock, Vertex, OpenAI and Azure behind halo-proxy: model aliases, weighted routes, wire translation and failover before the first byte. Or Kong with halo-kong.
Learn more →Tested on macOS, Linux and Kubernetes (kind + the Helm chart). Windows binaries build in CI. Guides for dev containers, Coder and MDM-managed laptops.
Learn more →See it run
make demo: sign in as alice and kill the opus-5-5-canary experiment. Then open the github-mcp toggle, propose a ramp to 25% (a policy PR; a local branch in the demo), kill the toggle with a reason, and find both kills in the audit log. Kills take effect at the next kill-list poll: gateways every 10 s by default (2 s in the demo), halod every 60 s by default.Recorded from the real binaries; the orange cursor ring is the only overlay. Scripts: record-media.sh · record-console.mjs
See it decide
Six steps: a dark launch, a canary ramp and a 14-day holdout. Flip the scenario to inject a latency regression.
halo rollout simulate opus-5-5-upgrade
gateway alias opus -> treatment route of experiment opus-5-5-canary
Decisions from halo rollout plan|simulate opus-5-5-upgrade --policy-dir examples/acme-corp --output json, recorded 2026-10-01 (halo a23f464). Synthetic evidence, real state machine.Data
Proof, not promises
halo toggle kill to changed routing at the gatewayone recorded run; demo stack polls every 2 s, so expect 0-2 s (default 10 s) · tutorials/kill-a-bad-change.md16cross-component end-to-end tests on real binaries and a real registrytest/e2e (counted at build)Not yet run against real Bedrock, Vertex, OpenAI, Azure or Gemini accounts, Kong Enterprise, MDM-managed devices, Terraform, Codespaces or Coder: those paths are UNVERIFIED.
Security
ed25519-signed bundles and ring pointers in your OCI registry. halod refuses a foreign key and keeps the last good release.
Cohorts come from a verified JWT. Client x-halo-* headers are stripped at the gateway.
bypassPermissions and danger-full-access never render, in any ring or profile.
No auto-merge, no auto-promote. Rollback is the only automatic direction.
Enterprise controls: RBAC, SCIM 2.0, org units, cost budgets, SIEM audit export, workload and agent identity, HA on Postgres, vendor admin console sync.
Security model and threat model →Quickstart
Console with a mock login, gateway, shadow, mock models, Grafana, and a seeded repo with a running experiment, rollout and toggle.
# needs Go and Docker
git clone https://github.com/dshakes/halos && cd halos
make demo # prints the URLs; make demo-down removes it
# or start your own policy repo
make build && export PATH=$PWD/bin:$PATH
mkdir acme && cd acme && halo init --org acme && halo validate