Skip to content

Releases: dshakes/halos

v0.5.2

Choose a tag to compare

@github-actions github-actions released this 02 Nov 03:23
6887118

Upgrade notes

  • npm: the packages are @dshakesnotbot/halo and @dshakesnotbot/halo-<os>-<arch> (npx @dshakesnotbot/halo quickstart). This is the first version on npm.
  • halo-server now rate-limits each signed-in user and service token on console/API routes (20/s, burst 100, 429 with Retry-After). Automation that bursts harder should back off on 429 or raise -api-rate-limit / -api-rate-burst (0 disables).
  • halo doctor --output json (and the MCP doctor tool) can now report "status": "skip" for optional CLIs that are not installed; consumers that switch on ok|warn|fail must accept it.
  • Binaries are built with go1.26.10 (12 reachable standard-library vulnerabilities fixed).

Added

  • halo-server sends Strict-Transport-Security (browsers honour it only over HTTPS); every Prometheus/Grafana alert carries a runbook_url to the operations guide.
  • halo-server rate-limits each signed-in user and service token on console/API routes (-api-rate-limit 20/s, -api-rate-burst 100; 429 with Retry-After; 0 disables). Fleet, gateway and SCIM traffic is not charged.
  • halo --version (same output as halo version).

Changed

  • halo doctor (and the MCP doctor tool and --output json) report an optional CLI that is not installed with the new check status skip instead of ok; it is informational, never a warning or failure, and does not affect ok. JSON consumers that switch on status (ok, warn, fail) must accept skip. halo onboard local|install collapse a harness's adapter warnings (more than two) into one count line, --verbose lists them all and --output json is unchanged; halo plan prints a per-file summary first and (none) for a first release's empty side.
  • The npm packages publish as @dshakesnotbot/halo (and @dshakesnotbot/halo-<os>-<arch>): the @dshakes npm scope does not exist, so v0.5.1 never reached npm. Use npx @dshakesnotbot/halo quickstart.
  • Every command that writes only with --apply also accepts --dry-run (its default), matching quickstart, enable, eject and model switch; the two flags are mutually exclusive.

Fixed

  • halo onboard company names every missing required flag in one error (--gateway, --issuer, --registry, --policy-repo, --portal-url) instead of failing one unnamed field per run; README points at --help.
  • halo quickstart clones the release tag that matches the binary (cached per version), not main, and says when the Docker daemon is not running.
  • A kind: Halos document outside halos.yaml and an unknown halo model switch alias now say how to fix them.

Security

  • Build toolchain go1.26.6 -> go1.26.10: fixes 12 standard-library vulnerabilities govulncheck reports as reachable (net/http, crypto/tls, crypto/x509, net/textproto, os). Every binary and image is rebuilt with it.

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 01 Nov 21:42
3f5e29c

Upgrade notes

  • Releases need an NPM_TOKEN secret: the release gate fails without it (RELEASING.md).

Added

  • npx @dshakes/halo: the release workflow's new npm job repackages the verified release archives as @dshakes/halo (a Node launcher) and one @dshakes/halo-<os>-<arch> package per platform, published with npm provenance. CI builds the packages from the snapshot release and runs halo version through them. Releases now need an NPM_TOKEN secret (RELEASING.md).

Documentation

  • README capability table, docs site and diagrams cover v0.5.0: admin credentials, scoped RBAC, external signing keys and FIPS, the OpenAPI document, promotion PRs, notifiers, the Helm ops kit, fleet sizing and compliance mapping. New guide for the Terraform and OpenTofu provider (dshakes/halos, separate repository). The release-pipeline, trust-zones, security-boundaries and architecture diagrams show key URIs (file, AWS KMS, external signer) instead of a fixed ed25519 key.

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 01 Nov 00:17
9a5dc42

Upgrade notes

  • Rollout-step approvals recorded before the upgrade stop counting (the approval key now includes the step's and the experiment's rings): give them again.
  • Upgrade every halo-server replica, the halo CLI, halo mcp serve and halo controller run before adding the first identity.roleBindings entry.
  • halo-server takes an exclusive lock on --data-dir: a second process on the same directory refuses to start.
  • FIPS builds refuse webhook, audit-export, cloud-webhook and telemetry-salt secrets under 14 bytes at startup; standard builds warn.
  • When shadow pair keys are configured, unencrypted pair rows are refused; pass --allow-plaintext-pairs (MCP allow_plaintext_pairs) once to migrate a store sealed part way through.
  • POST /api/v1/service-tokens rejects unknown, repeated or case-variant keys with 400.

Added

  • Fleet-scale load test for halo-server (make load-fleet, test/load/fleet, .github/workflows/load-fleet.yml): simulated halod devices enroll, poll the signed kill list, fetch rings and post reports against file and Postgres modes, with latency percentiles, error rates and server CPU and memory; a manual or monthly 10,000-device workflow with a Postgres service container. Sizing guidance with the measured numbers is in the Operations guide ("Sizing halo-server"): up to 10,000 devices per replica, one replica more than needed, memory and Postgres storage per device.
  • Scoped role bindings for halo-server (ADR-0037): identity.roleBindings grant a console role to groups and users over a scope of rings and units (a unit includes its descendants; rings and units intersect). Every route declares how its resource is scoped (routeScopes); a scoped grant covers a resource only when it lies wholly inside the scope, derived from policy and server records, never the request. Lists (fleet, devices, releases, experiments, rollouts, toggles, kill list, freeze, requests, service tokens) are filtered, unknown resources deny scoped callers with the same 403 as out of scope, and the audit log, budgets, policy and a few admin routes stay global only. Service tokens take a scope within their creator's grants (halo service-token create --scope-ring/--scope-unit); scoped approvals count toward ADR-0030 quorums only while their scope covers the change; scoped actions are audited with authzRole/authzScope/authzBinding. /api/v1/me and /capabilities return bindings; halo whoami shows console roles and scopes. identity.roles, adminGroups and existing tokens are unchanged. Upgrade every halo-server replica, the halo CLI and halo mcp serve before adding the first binding: older binaries refuse such policies and scoped tokens.
  • Controller notifications beyond Slack: Microsoft Teams (Workflows/Power Automate webhook, Adaptive Card; the retired Office 365 connector URLs are refused), PagerDuty (Events API v2: trigger with a stable dedup_key per rollout or experiment for rollbacks, kill-switch holds and paused rollouts, resolve when a human merges or advances, or the kill is lifted; informational events never page) and the signed generic webhook, which can now receive all-clears. Per-sink event filters (--notify-<sink>-events, server.controller.notify.<sink>.events), bounded retries with backoff on 429 and 5xx that honour Retry-After (never on other 4xx), halo_controller_notifications_total{sink,result} and a HaloControllerNotifyFailing alert. Secrets stay in files and Secrets (--notify-teams-url-file, --notify-pagerduty-routing-key-file). Slack output is unchanged. See the notifications guide.
  • PagerDuty and webhook all-clears also fire when an experiment or rollout is deleted from policy while an incident is open, so a deleted name no longer stays paged forever.
  • GitLab merge requests and a GitHub REST path for every change Halos proposes (promotion, rollback, rollout, controller, console proposals, halo upgrade, MCP propose_*): --forge gh|github|gitlab and --forge-base-url on halo exp promote, halo rollout advance|pause|rollback, halo controller run, halo upgrade check|watch, halo mcp serve and halo-server; portal config policyForge / policyForgeBaseURL; Helm server.controller.policyRepo.forge, forgeBaseURL, gitlabToken. GitLab uses REST v4 with GITLAB_TOKEN (api scope, nested group paths, self-managed hosts), GitHub uses GITHUB_TOKEN/GH_TOKEN (GitHub Enterprise Server via base URL); both are idempotent by marker and send no merge, auto-merge or approve call (a human still merges). The default is unchanged: with no flag the forge is autodetected from the origin remote (gitlab if the host names it, else the gh CLI). Tested against httptest fakes only, not a real GitLab or GHES (UNVERIFIED). halo doctor reports GITLAB_TOKEN instead of gh when the policy repo's origin is a GitLab host, --forge-base-url under a URL prefix (https://host/gitlab/api/v4) strips that prefix from the remote project path, and the MCP propose_* tools now embed a change marker and return the existing open PR/MR instead of opening a second one (merged or closed ones never block a new proposal). See the new GitLab and GitHub Enterprise Server guide.
  • An OpenAPI 3.1 description of halo-server's HTTP API: api/openapi.yaml, published at /halos/openapi.yaml on the docs site and served unauthenticated and read-only at GET /api/v1/openapi.yaml. It lists every route with auth schemes, request and response schemas derived from the Go types, status codes, the RBAC permission each route needs (x-halo-permission, from the route table) and a stability marker (x-halo-stability, stable on every operation under the existing /api/v1 promise). Generated by make openapi (also run by make docs-gen); go test ./internal/server -run OpenAPI fails when a route is registered without documentation, a permission differs from the route table, an example stops decoding into its Go type, the file is not valid OpenAPI 3.1, or a handler answers a status or body the file does not describe.
  • Signing keys in AWS KMS or an HSM (ADR-0038): --key-uri file://|awskms://|exec:// on every signing command (HALO_KEY_URI), halo-server --killswitch-key-uri, and halo keys public --key-uri to export the public key and key id. awskms:// signs with ECC_NIST_P256 or ECC_NIST_EDWARDS25519 keys through the KMS API (no new dependency); exec:// runs an external signer speaking the documented halos.signer.v1 JSON protocol (PKCS#11, GCP, Azure, Vault adapters). Every remote signature is verified against the key before use. Guide: Signing keys in a KMS or HSM.
  • ECDSA P-256 (ecdsa-p256-sha256) release, pointer, attestation and kill-list signatures, verified by halod, halo-proxy, halo-kong, the portal and exports next to ed25519. Each signature is checked under its recorded algorithm only; unknown algorithms are refused. Manifests and kill envelopes record the signing key id. --co-key-uri co-signs with the old key of the other algorithm while rotating, so halods that trust only it (including older releases of halod) keep verifying. Releases signed before this change verify unchanged.
  • FIPS 140-3 builds: *_fips_* archives and -fips image tags built with GOFIPS140=v1.0.0 (Go Cryptographic Module), make build-fips / make test-fips, and a CI job running the signing, verification, TLS, session and encryption packages under GODEBUG=fips140=only. Guide: FIPS 140-3 builds. Halos itself holds no FIPS certificate.
  • Service-account API tokens for halo-server (ADR-0036): halo service-token create|list|revoke and /api/v1/service-tokens. A token (halo_sat_ + 80 hex digits, for secret scanners) holds one role no stronger than its creator's, always expires (--service-token-max-ttl, default 90 days), is stored only as SHA-256, revocable alone on every replica, and audited as service-account:<name>:<id>. Only an interactive admin session can create one. The CLI's server-backed commands and halo mcp serve --server read HALO_TOKEN, which wins over HALO_SESSION. Postgres: migration 4 adds halo_service_tokens (run halo-server migrate); halo-server import copies a data dir's tokens.
  • Break-glass local admin: halo-server breakglass-hash (PBKDF2-HMAC-SHA256), --breakglass-hash-file, --breakglass-user, --breakglass-enabled (Helm server.breakGlass). Sealed unless enabled; POST /auth/breakglass gives a fixed 30-minute admin session; every login, failure and request is a high-severity audit entry, and a login whose audit entry cannot be written is refused. SIEM export marks high-severity entries (OCSF severity High, syslog critical). Runbook: Admin access guide.
  • Console session policy: --session-ttl (default 12h, unchanged) and a sliding --session-idle-timeout (default 1h; 0 turns it off), Helm server.session.
  • Policy identity.groupsOverage (deny default, or graph to read groups from Microsoft Graph, --entra-graph-url for national clouds) and identity.requireAcr / identity.requireAmr for the console login. Okta and Entra ID setup guides.
  • Helm chart operations kit. monitoring.prometheusRule.enabled renders a PrometheusRule (only when the cluster serves the CRD) from deploy/helm/halos/files/prometheus-rules.yaml, now the single source of the alert list: the Grafana alerting provisioning (deploy/observability/grafana/provisioning/alerting/halos-alerts.yaml) is generated from it by make obs-assets, and go test ./internal/obsassets fails when the rules, the generated file, the dashboards or the operations guide drift, or when a rule or panel names a metric no b...
Read more

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 30 Oct 14:36
1ee21f2

Fixed

  • Codex: egress allowlists now work. Halos rendered [experimental_network.domains], which codex 0.99.0 ignores (its proxy then refused every host), plus a managed network_access = false that outranked the user's opt-in, so no allowlisted host was reachable from a sandboxed command. Releases now render the allowed_domains / denied_domains lists (read by 0.99.0 and 0.166.0) and leave network_access to the user under an allowlist. Relative sandbox.protectedPaths are no longer rendered as /**/ globs, which codex 0.166.0 refuses for every sandboxed command; they warn instead (Codex has no workspace-relative deny_read), and pins before 0.122.0 warn that deny_read is ignored. A new uat-clis check runs the rendered files through the real codex sandbox linux. Re-publish releases with Codex egress or protected paths.

Changed

  • Release warnings name the remaining local overrides found by testing the current CLIs (gemini-cli 0.66.0, opencode 1.19.5, codex 0.166.0, whose managed keys all matched): any SANDBOX environment variable skips Gemini CLI's sandbox and its default Seatbelt profile does not restrict network; OPENCODE_PERMISSION or an agent's permission.webfetch in user config overrides OpenCode's managed webfetch.
  • halo doctor warns when SANDBOX is set, which makes Gemini CLI skip its sandbox; the egress and sandbox guide lists the Gemini and OpenCode overrides and Gemini CLI's root-owned system settings requirement.

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 12:33
5865e73

Added

  • Cloud agent governance (ADR-0034) for the Claude Code GitHub Action and cloud sessions, Codex cloud, the Copilot cloud agent, Cursor cloud agents and the Gemini CLI GitHub Action. halos.yaml cloudAgents sets per vendor: enabled, allowed repositories and models, maxConcurrent per user and group, budgets (ADR-0017 fields), sandbox network mode, identity: workload (gateway credential through the ADR-0023 exchange instead of a repository secret) and onOverLimit: alert|pause; identities map GitHub logins to Halos users. halo cloud-agents render writes SHA-pinned, permissions: {} workflows (halos-claude.yml, halos-gemini.yml, copilot-setup-steps.yml) checked against the never-bypass invariant, and releases carry them signed in manifest.json cloudAgents (never written by halod). halo-server receives GitHub workflow_run webhooks (POST /api/v1/cloud-agents/webhooks/github, X-Hub-Signature-256 HMAC, --cloud-webhook-secret-file), stores tasks in the audit chain (cloudagent.task; finished ones also as session ledger records with source cloudagents), raises alerts (halo_server_cloud_agent_alerts_total) and, with onOverLimit: pause, reversibly disables the workflow or archives the Cursor agent. GET /api/v1/cloud-agents (read) and halo cloud-agents list show tasks and alerts; POST /api/v1/cloud-agents/kill (operate) and halo cloud-agents kill --scope user|ring|experiment cancel a user's, ring's or experiment's tasks through the GitHub and Cursor APIs, dry run by default, reason required, audited before any call. An experiment kill reports its users' active cloud tasks. halo admin plan|export add the console steps for Claude cloud sessions, Codex cloud, Cursor and Copilot repository access, and read each repository's Copilot cloud agent firewall.

  • Egress and sandbox policy (ADR-0032). A profile sandbox block (mode = permissions.sandbox, writableRoots, protectedPaths) and an extended egress block (default: allow|deny, deniedDomains, presets: package-registries|vcs|model-providers beside allowedDomains; deny wins, *.x is subdomains only). deniedDomains and protectedPaths union down extends and across units; a unit switching egress to allow, dropping a denied domain or protected path, or adding a writable root is a loosening. Validation rejects bypass-shaped combinations: a writable root of /, ~ or a glob, over a protected path or under read-only, and mode off with egress or path rules. Rendered natively: Claude Code sandbox.network allowed/denied domains with allowManagedDomainsOnly, sandbox.filesystem and Read/Edit/WebFetch(domain:) deny rules; Codex requirements.toml [experimental_network] and [permissions.filesystem] deny_read, writable_roots; Copilot CLI managed sandbox with userPolicy.network/filesystem (pins >= 1.0.93); Gemini CLI tools.sandbox (macOS); OpenCode webfetch and path deny rules; Cursor warns (per-user files only). New sandbox and egress capabilities in halo harnesses. halod reports per-user settings that weaken the managed posture (~/.codex/config.toml danger-full-access, ~/.gemini/settings.json tools.sandbox: false, ~/.cursor/sandbox.json insecure_none) as posture: drift with a fix; halo doctor shows each installed harness's effective sandbox and egress posture, including environment overrides. halo egress test <url> --ring R [--unit U] names the deciding rule and the mechanism each harness enforces it with (exit 1 on deny). Guide: Egress and sandbox. Existing allowlist profiles now also render Codex [experimental_network] with network_access = false, and permissions.sandbox now renders Gemini CLI tools.sandbox on macOS.

  • Gateway data-loss prevention and prompt-injection defense (ADR-0033). Policy gateway.dlp makes halo-proxy and halo-kong scan every model-call request (user text, tool results, files; Anthropic, Bedrock, OpenAI Responses, Gemini) with twelve high-precision secret detectors (AWS, GitHub, GitLab, Slack token and webhook, Stripe, Anthropic, OpenAI, JWT, PEM private keys, GCP service-account keys, key-name plus high-entropy) and org RE2 patterns, and monitor, redact ([REDACTED:<detector>:<hash8>], the JSON otherwise byte-for-byte unchanged, also in the shadow mirror) or block (HTTP 400 in the caller's wire format). Tool results are scored for prompt-injection signals (instruction overrides, fake turns, invisible Unicode, HTML-comment imperatives, exfiltration URLs, permission-bypass tokens) and monitored or blocked above a threshold; model content is never modified. rules set the actions per ring and org unit. halo-proxy also watches streamed responses for echoed secrets (monitor only, bytes unchanged). Bounded by maxScanBytes and a 250 ms budget (200 KB request: about 5 ms p99). Findings never carry the secret: halo_proxy_dlp_* and halo_kong_dlp_findings_total metrics, a dlp request-log attribute, audit entries dlp.secret / dlp.injection through the new POST /api/v1/gateway/dlp/events (--gateway-dlp-token-file; halo-proxy dlp.url, halo-kong dlp_url) so the SIEM exporter carries them, and dlpSecrets / dlpInjections in the session ledger. halo dlp test <file> dry-runs the scan locally against a policy, ring and unit.

  • Policy copilot (ADR-0035): the model proposes, Halos verifies, a human approves. halo copilot draft "<intent>" has a model draft whole policy files and loops on Halos' own checks (bypass-token scan over every written byte, validation, a release plan per ring, the impact preview) until they pass or a hard ceiling (--max-iterations, --max-tokens, --max-cost-usd) stops it; --apply commits a passing draft to a new local halos/copilot-* branch and never pushes. halo copilot experiment "<hypothesis>" takes the arms and metrics from the model and the baseline, sd and MDE from the human; stats.SampleSize computes the per-arm sample size and duration, and Halos renders a status: draft experiment. halo copilot explain release|diff|experiment rejects any point that does not cite a Halos fact key. halo copilot next suggests next steps from verdicts, eval scorecards, ROI and budgets, each with evidence and the exact command. Model replies are validated against a Go-side schema and retried; each call logs token counts only (prompts with --debug); the default model is claude-opus-5-5 through the policy's gateway with the eval judge's client (eval.HTTPLLM now reports token usage). --offline uses deterministic templates. MCP: copilot_draft, copilot_experiment (dry run by default; commits need --allow-writes and a reason), copilot_explain, copilot_next and the copilot prompt; the calling agent is the model, so they never call a provider.

  • Console: a Rollouts page (step timeline, per-gate status, bake countdown, halt and rollback events, ring-to-release pointers) and a Cost & budgets page (spend against limit per scope, window, warn/enforce mode and alertAt thresholds); Fleet gains per-ring compliance, a drift count per device and an "in sync" filter; Device lists the drifted settings; Overview links to both new pages. Read-only; no server API changes.

  • Copilot CLI lines of code in halo.loc. github.copilot.code.lines_added and github.copilot.code.lines_removed are mapped to halo.loc with a type attribute (added / removed), like Claude Code's lines-of-code metric. Codex's native lines metric stays unmapped (vendor metric name not verified).

  • Randomized delivery ROI (ADR-0027): halo roi ingest github|file imports pull-request outcomes (cycle time, review rounds, lines changed, a revert/hotfix change-failure proxy) read-only from GitHub GraphQL or a generic CSV/JSONL export into ClickHouse halo_delivery_prs or a local JSONL store. halo roi report --experiment X maps authors to Halos subjects with an identity roster, recovers each developer's variant with the fleet's own assignment, and estimates per-arm effects with developer-clustered bootstrap CIs, an SRM check, novelty time slices, an honest insufficient_power state and cost per merged PR; --rings comparisons are labelled observational. The MCP tool roi_report is read-only, and promotion PRs (halo exp promote --roi-identities, propose_promotion) carry an informational ROI section that never affects the verdict. internal/stats gains ClusterBootstrapDiff and SRM.

  • Agent session ledger (ADR-0031): one vendor-neutral, identity-linked record per agent session (who and the delegated agent chain, harness and version, release/ring/experiment variant, models, MCP servers and tools, hashed or dropped repos and files, tokens, cost, start/end, outcome, evidence sources and confidence). halo sessions import reads halo-proxy GenAI spans and Claude Code OTel events from ClickHouse, the Anthropic Compliance API, the GitHub enterprise audit log and JSONL; records are redacted fail-closed (no field can hold prompt text; paths dropped or HMAC-hashed) and stored by halo-server in the hash-chained audit log (session.record), so they are tamper-evident and reach the SIEM through the ADR-0016 exporter. Identity is joined server-side from the SCIM directory only. halo sessions list|show|export, GET /api/v1/sessions[/{id}] (audit role), POST /api/v1/sessions (admin), the read-only MCP tool session_ledger, and the ClickHouse table halo_session_ledger (--write-clickhouse).

  • Change governance (ADR-0030). halos.yaml governance.freezes decla...

Read more

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 23:25
c147a42

Added

  • Vendor admin sync (ADR-0025): halos.yaml vendorAdmin drives the vendors' cloud admin consoles from the GA ring's profile. halo admin plan diffs Gemini Code Assist (logging, release channel) and GitHub Copilot (content exclusion, CLI policy) against the live consoles and exits 2 on drift. halo admin apply writes the settings that have a public API, after confirmation, and never loosens a control. halo admin export writes Claude server-managed settings, Codex requirements and config, and a Cursor model list, with manual steps for consoles that have no write API. The MCP tool admin_plan is read-only.
  • Telemetry: the collector normalises Copilot CLI metrics into halo.* (github.copilot.tool.call.count becomes halo.tool.calls; its gen_ai.client.* metrics are stamped halo.harness=copilot-cli by service.name, where they used to be mislabelled as Gemini CLI). Cursor CLI and OpenCode emit no usable OTel and are documented as unmapped, with halo-proxy gen_ai.* metrics as their per-request source.
  • README, landing page, docs and diagrams cover all six harnesses and the enterprise controls.
  • High-availability halo-server on Postgres (ADR-0021): --database-url-file runs several replicas behind one load balancer, with advisory-lock migrations and a fenced leader lease for the rollout controller. Gateway budget counters (atomic batch dedupe), the workload-token replay cache, the SCIM directory (deprovisioning holds on every replica), SIEM export cursors (one forwarder per sink under a lease), pending-request uniqueness and verdicts are shared across replicas. Failed-auth limiters stay per replica (up to N× the limit). halo audit verify --database-url-file; Helm server.gatewayTokens for per-capability gateway tokens.
  • Agent identity and delegation (ADR-0024): POST /api/v1/token/exchange with actor_id mints short-lived delegated agent credentials (RFC 8693 act chain, root principal as subject, down-only model scope, depth/cycle/TTL limits, audited). halo-proxy and halo-kong route them as the root, enforce agents.* per call and stamp x-halo-actor. agents.remote is an A2A allowlist with pinned AgentCard digests, checked by halo agents verify and POST /api/v1/agents/egress.
  • Multi-turn agent shadowing (ADR-0014, amends ADR-0004). A shadow experiment with turns: all mirrors every step of a sampled session, not only the first turn. Each step replays the real prefix (developer prompt, production's actions, real tool results and terminal output) to both arms, and nothing either arm returns is executed. halo eval online reads each step without a judge across Anthropic, Bedrock (invoke and Converse), OpenAI Responses and Gemini:
    • tool and argument agreement with the control;
    • fidelity to the action production actually took, recovered from the session's next mirrored step and prefix-checked so subagent and compacted conversations are skipped (the control's fidelity is the measured noise floor);
    • invalid tool calls (a tool the request did not offer, or a missing required argument);
    • premature stops;
    • divergence rate and first divergent step;
    • agreement by depth.
  • halo.eval.agent.fidelity and halo.eval.agent.invalid_call_rate are registered metrics, one sample per hashed session and arm, usable as shadow-experiment guardrails in halo exp analyze and the controller. Shadow evidence still never promotes.
  • stats.ClusterBootstrapMean / PairedClusterBootstrap.
  • Session-to-eval harvesting (ADR-0020). halo eval harvest turns shadow steps into redacted step-level regression cases. It selects steps where the candidate diverged, made an invalid call, stopped early or lost on judge score, plus a depth-stratified sample. Cases are written 0600 with hashed sessions; redaction is mandatory and fail-closed. halo eval steps run replays them against a model route and grades the next action with session-clustered CIs. MCP: eval_harvest (preview without --allow-writes).
  • Role-based access control for halo-server (ADR-0015).
    • identity.roles maps IdP groups to viewer, operator, approver, auditor and admin. adminGroups still grants admin.
    • Every console and API route is enforced from one deny-by-default permission table, and every denial is audited as authz.deny.
    • /api/v1/me and /api/v1/capabilities report the caller's roles and permissions, and the console hides actions accordingly. New --dev-insecure-roles for demos.
  • Audit export to SIEM (ADR-0016).
    • --audit-config forwards every audit entry at least once and in order to Splunk HEC, an HMAC-signed HTTPS webhook (Datadog-ready) or RFC 5424 syslog over TLS.
    • Formats are native (hash-verifiable) or OCSF API Activity.
    • Each sink has its own cursor and backoff, and chain checks halt export on tampering. Metrics: halo_server_audit_forward_*.
    • The audit log rotates by size or age into segments the hash chain runs across. GET /api/v1/audit streams instead of loading the log into memory, and halo audit verify --data-dir checks every segment. Helm: server.audit.
  • Org hierarchy (ADR-0018).
    • The new Unit kind covers business units and teams with delegated owners, member selectors and overlays.
    • Enforced security fields can only be tightened, and halo validate reports each loosening with its field path.
    • New halo units codeowners, and halo explain --user, which shows the effective config with the layer that set each field.
    • Unit files ship inside the signed release, and halod applies them.
  • Cost budgets and quotas at the gateway (ADR-0017).
    • Optional gateway.pricing and gateway.budgets: user, group, ring or org scope; day, week, month or rolling window; USD and/or tokens; warn or enforce, with alertAt.
    • halo-proxy meters streamed usage and answers 429 with Retry-After in each CLI's wire format.
    • halo-server holds the counters (POST /api/v1/gateway/budget/{check,usage}, and GET /api/v1/budgets, which needs the read role). budgets.failMode is open or closed.
    • New halo budget status, and halo_proxy_budget_* metrics.
  • SCIM 2.0 provisioning (ADR-0022).
    • halo-server serves /scim/v2 for Okta and Entra ID.
    • Deactivating, deleting or renaming a user immediately revokes their sessions, device tokens and pending enrollments, audited as actor scim.
    • --scim-authoritative makes directory groups decide rings, toggles and roles.
    • halo-proxy directory.url and halo-kong directory_url make the directory authoritative at the gateway too; deprovisioned users get default routing.
  • Workload identity federation for CI and cloud agents (ADR-0023).
    • New workloads policy section.
    • halo-server POST /api/v1/token/exchange (RFC 8693) trades a GitHub Actions, GitLab, Buildkite, CircleCI or cloud OIDC token for an ed25519-signed gateway credential that lasts at most 1h. Exchanges are audited, and a token's jti cannot be replayed.
    • halo-proxy reads identity.workloadPubkeyFile and halo-kong reads workload_pubkey to accept these credentials.
    • New halo ci token and halo ci config. Workloads are excluded from experiments by default.
    • Examples in examples/ci-agents.
  • Agent extension governance. Profiles can govern extensions.{marketplaces,blockedMarketplaces,plugins,skills,subagents,outputStyle,statusLine} and login.
    • These render only where the vendor documents a key (Claude Code, Codex, Gemini CLI, Copilot CLI) and warn in the release manifest everywhere else.
    • mcp.denied now renders for Gemini (mcp.excluded) and Copilot (deniedMcpServers). For Copilot, mcp.managedOnly with no servers renders allowedMcpServers: [].
    • The harness matrix gains an extensions capability.
  • OpenTelemetry GenAI semantic conventions (semconv v1.40.0, Development stability).
    • halo-proxy exports gen_ai.client.token.usage, gen_ai.client.operation.duration and gen_ai.server.time_to_first_token alongside the unchanged halo.gateway.* series, on by default.
    • Opt-in telemetry.genai.traces sends one content-free chat {model} span per call.
    • halo telemetry collector-config gains a traces/gateway pipeline.

Security

  • Bump golang.org/x/text to v0.41.0 (GO-2026-6629, GO-2026-5970, reachable via the Postgres store); halo-server closes its data-dir files when startup fails part way.
  • Workload token exchange matches a rule before recording the subject token's jti, so tokens anyone can mint from a shared CI issuer can no longer fill the replay cache and refuse every tenant; workload.token.denied is audited at most once per issuer and client IP per minute, the rest counted in halo_server_workload_denied_audit_suppressed_total.
  • A login right after a session revocation is stamped a whole second past the revocation epoch, so credentials delegated from it are no longer refused as revoked; sessions issued before the revocation stay dead.
  • A workload rule removed by a refused policy stays in place as a deny tombstone, so its tokens never fall through to a broader rule below it.
  • halo admin vendor API clients never follow redirects (net/http would resend Authorization over http or to a subdomain); a 3xx is an error.
  • Units can no longer set any GIT_* env key except commit identity, nor CC, CXX, CGO_*, Go module sources (GOPROXY, `GO...
Read more

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 12 Oct 03:39
e275c56

Fixed

  • halo-server's JSON request bodies (experiment status, kill switch, portal enrollment, access requests, toggles) now reject any bytes after the JSON value. The decoder stopped at the end of the first value, so a body larger than the size cap was accepted whenever its JSON ended before the cap. Found by the nightly FuzzAPIJSONDecoders run; the crasher is kept as a regression seed.

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 11 Oct 05:21
111ab4a

Added

  • Your own API gateway (Gateway.engine: external, simple mode gatewayEngine: external, halo onboard local --gateway-engine external, halo onboard company --gateway-kind external): for a company whose model traffic already goes through its own gateway (Kong, an auth gateway, an orchestrator, Bedrock). Halos runs no data plane there, so Claude Code, Codex and Gemini CLI are rendered with each alias's provider model id instead of the alias, routes need no Halos upstream (simple mode generates none, so a provider's Halos-side requirements such as a Vertex project do not apply), Claude Code's built-in fallback tiers are pinned to the default model when models.enforce is set (nothing behind an external gateway rejects an unlisted model), and validation warns that the gateway-side posture and version gates, kill switch and model-axis experiment metrics do not apply. halo onboard company now also records kong in the generated policy.

Changed

  • The nightly soak's p99 gate is armed: test/load/baseline.json is the result of a green 60-minute run on ubuntu-latest (run 37253557695: 0 errors at ~1245 rps, p99 53.4 ms, all SSE streams intact, no heap or goroutine growth).

Fixed

  • Claude Code: a profile may set ANTHROPIC_CUSTOM_HEADERS (the one ANTHROPIC_* variable the reserved-env guardrail now allows), and those headers (e.g. a tenant or routing header a company gateway requires) are no longer overwritten by Halos's x-halo-ring / x-halo-release headers: they are kept, with Halos's appended, and CR or x-halo-* lines in them are rejected.
  • The .halos-backup of a replaced managed file keeps the original's permissions; it was root-only, so a manual restore left Claude Code unable to read the file.
  • halo onboard local stops with explicit next steps (compare in a scratch root, deliver through MDM, or point at your own gateway) when a managed file on the machine was not written by Halos.
  • halo onboard install --apply replaced a managed config file it had not written (for example a Claude Code managed-settings.json an MDM had pushed), keeping only a .halos-backup and previewing it as a plain update. Such a file is now planned as foreign and --apply refuses and writes nothing unless --replace-existing is set; the MCP local_install tool never replaces one. Halos records the hash of each file it writes at <file>.halos-sha256 to tell its own files from rewritten ones, on every install (an earlier .halos-backup does not count as ownership), and a second replacement keeps the earlier backup and writes a timestamped one. A file with no hash is never adopted silently, even one that matches the render (an MDM push can match): files installed by halo 0.1.1 or earlier report as foreign on the next change; confirm they are yours and re-run with --replace-existing, which also records the hash of matching files.
  • The nightly soak's load tool minted a one-hour JWT for a 60-minute soak, so the first scheduled run (3 Oct) ended in 401s at t+58 m with every other gate green; the token now outlives the run.
  • CI's released-halo jobs validate examples/acme-corp again; the test/ci/policy-v1alpha1 fixture is gone.

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 04 Oct 02:30
fdf369d

Fixed

  • Homebrew and Scoop publishing. GoReleaser accepts a tap or bucket token only as {{ .Env.NAME }}; the index .Env form passed goreleaser check and every prerelease (which skip the tap and bucket) and failed on the first final tag. v0.1.0's release job stopped at that step: its GitHub release assets, signed checksums and 0.1.0 images were published, but it has no Homebrew formula, no Scoop manifest and no provenance attestation. v0.1.1 is otherwise identical and is the release the installers and docs point at.

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 00:46
e7a8505

The first general-availability release. Everything in this section and in the two release candidates ships here. Verified in CI on every change, and run end to end before this tag: Linux, macOS and Windows builds; the installers, halod as a launchd daemon and as a Windows SYSTEM scheduled task; the GitHub Action, the GitLab template and the dev container Feature; Helm on a kind cluster with pod-kill, rolling-upgrade and eviction fault injection under load; halo-kong in open-source Kong 3.9.3; the real pinned Claude Code, Codex, Gemini CLI and Copilot CLI driven headlessly through halo-proxy; halo-proxy against the real Anthropic and OpenAI APIs nightly; a headless browser click-through of every console page.

GA scope. Linux and macOS hosts, dev containers, the four CLIs' managed configuration, halo-proxy with Anthropic and OpenAI upstreams, and halo-kong on open-source Kong are GA. Windows hosts, MDM-delivered laptops (Jamf, Kandji, Intune), Codespaces and Coder, the Codex and Copilot CLI onboarding entry points, and the Bedrock, Vertex and Azure OpenAI upstreams are supported but not yet field-verified: each is built, unit- and fixture-tested, and exercised in CI as far as a hosted runner can, but has not run against a real tenant, device fleet or cloud account. See Known gaps.

Added

  • Device posture gate (Ring.posture: off|warn|enforce, default warn): halo-proxy and halo-kong ask halo-server (GET /api/v1/gateway/posture, gateway token, rate-limited) whether the calling device's halod reported it stale, drifted or off its ring's release, and refuse model calls in enforce. Verdicts are cached (1 m) and the last verdict serves for a 15 m grace window when halo-server is unreachable, after which requests pass and are counted outcome="unknown". halo_proxy_gate_total metrics. ADR-0011.
  • CLI version gate (Ring.versionGate: off|warn|enforce, default warn): the gateway reads the CLI version from the User-Agent and compares it with the ring's pinned versions (ring profile and the variant profiles of running client-axis experiments). This covers Codex, Gemini CLI and Copilot CLI, which do not enforce a pin themselves. User-Agent formats come from recorded headers and vendor source, cited in ADR-0011.
  • Immutable managed files: halod.yaml immutable: true sets chflags schg (macOS) / chattr +i (Linux) after each atomic write and clears it before a rewrite; a missing tool or permission is logged once and skipped.
  • Policy API v1: apiVersion: halos.dev/v1 is the stable document version; every kind and field is frozen as it was in v1alpha1. halo migrate --policy-dir DIR [--dry-run] rewrites v1alpha1 documents in place, keeping comments and formatting (idempotent). halo init and the intent commands write v1; schemas/*.schema.json describe v1 and schemas/v1alpha1/ keeps the old ones. Compatibility states the v1 guarantees, the deprecation policy and the supported version skew across halo, halod, halo-server and halo-proxy. ADR-0012.
  • Agentic onboarding from whichever AI CLI you have. halo quickstart brings up the demo and prints a tour. halo doctor reports installed CLIs and versions, tools, which credential variables are set (never their values), policy validity and gateway reachability, with a fix per problem. halo onboard local|install|proxy|verify takes a machine from nothing to a validated halos.yaml, a dry-run (then --apply) install of the managed config, an optional loopback halo-proxy, and a real claude -p / codex exec / gemini -p / copilot -p round trip. halo onboard company interviews an admin (IdP, gateway, provider, delivery) and generates the policy repo, Helm values, OIDC client, enrollment config, CI workflow and smoke eval, then stops before anything outward. All --output json, idempotent, dry-run by default. MCP tools of the same names plus an onboard prompt; entry points for Claude Code (/halos:halo-onboard), Codex ($halos-onboard), Gemini CLI (extension) and Copilot CLI (.github/mcp.json). New Start here page.
  • Helm HA: halo-proxy runs with probes, a PodDisruptionBudget, topology spread, an optional HPA, a hardened securityContext and an optional NetworkPolicy; values.schema.json pins halo-server and halo-shadow to one replica because each is a single writer over local JSONL files (documented in the chart). make uat-k8s now kills a proxy pod mid-traffic, performs a rolling upgrade and tests the PDB under 200 rps with zero client-visible errors required.
  • Operations docs: Operations (HA model, SLOs, Prometheus alert rules on the real metric names, backup and restore of every --data-dir file and key, runbook for killing a change, rolling back a ring, rotating release and kill-switch keys, a lost signer state file, gateway or server down) and Support policy (SemVer scope per surface, supported versions, upgrade order halo-server → halo-shadow → halo-proxy/kong → halod → halo, EOL).
  • Nightly soak (soak.yml): 60 minutes of test/load against a real halo-proxy, failing on errors, cut SSE streams, goroutine or heap growth, and p99 regression against a committed baseline (records only until test/load/baseline.json is committed).
  • Fuzzing: 16 native Go fuzz targets over every parser of untrusted input (policy YAML, JWTs, ring pointers, bundle tar and manifest, kill lists, SSE, identity headers, webhook signatures, server JSON, audit chain, shadow replay, halod paths and halod.yaml), run briefly on every PR and for 10 minutes per target nightly. An OpenSSF Scorecard workflow and a SECURITY.md with the disclosure process and supported versions.
  • Security model, bound to tests: every claim in the security model names the test that proves it, and TestSecurityModelClaimsBound fails if a named test disappears.
  • CI verifies every delivery path on every PR: install.ps1, enroll.ps1 and halod service install on Windows; install.sh, enroll.sh and launchd on macOS; the composite Action on three OSes; the GitLab template under gitlab-ci-local; the dev container and the Feature under the devcontainer CLI; Playwright clicks through every console page and button (make demo-e2e).
  • One-command releases: scripts/release.sh [--dry-run] [--yes] <tag> runs the preflight (main, clean, HEAD == origin/main, SemVer, CHANGELOG section, green CI, go test, goreleaser check) and pushes the tag; release.yml gates, builds, attests provenance and then verifies the published artifacts (cosign bundle, checksums, attestation, image signatures, install.sh, tap and bucket); release-drift.yml fails daily if the tap or bucket drift from the latest release. RELEASING.md.
  • Demo kiosk: make demo serves a pinned halod with its checksum and configures every launcher, so "Set up my laptop" and the dev container launcher work end to end locally. Unconfigured launchers never show a raw server error: GET /api/v1/capabilities reports each launcher's availability, the kiosk shows only usable ones, and admins see what to set. Harness cards list only the model aliases that harness can use (the same wire check as halo validate).
  • halo eval online reports per-arm error rate, mean latency and output tokens over every shadow pair, errored ones included, and exports halo.eval.shadow.* (ADR-0004: regressions in refusal rate, latency and cost are now caught, not only judge scores).
  • Guardrails guardSandboxEgress (a ring profile with permissions.sandboxRequired must set a non-empty egress.allowedDomains) and guardGARing (a default ring that ships hooks must make them managed-only); guardNoBypass now also scans profile env and harness overrides. ExtraGuardrails lets an org add checks that can only add issues. halo release publish refuses a ring pinned to a different release: digest. Every ADR 0001–0010 carries an Implementation status table mapping each commitment to code and a test, with dated addenda where a decision was superseded.
  • halo plan no longer requires --against: a first release has no baseline.

Changed

  • halo init and the intent commands write halos.dev/v1.
  • Release candidates are published as GitHub prereleases and never move the :latest image tag or the Homebrew and Scoop manifests; only a final vX.Y.Z tag does.
  • The Scoop manifest is halo.json; the Homebrew formula lives in the tap's Formula/ directory.

Deprecated

  • apiVersion: halos.dev/v1alpha1 still loads with identical semantics and a validation warning per file. It is removed only in halos.dev/v2.

Fixed

  • halod on Windows: state.json is written with the managed var directory's DACL instead of an owner-only one, so the SYSTEM scheduled task no longer rejects a state file first written by an admin during enrollment.
  • GitLab template: .halos no longer sets a job-level HALOS_VERSION default that shadowed the pipeline-level pin.
  • halo exp analyze could fail with json: unsupported value: -Inf when a guardrail's two arms had no variance yet; the sequential guardrail now reports the exact estimate instead of an unbounded interval.
  • halo-proxy no longer logs a spurious export error when a telemetry flush is cut short by shutdown.
  • test/load paces requests with -rps; an unthrottled run exhausted the kind ingress's ephemeral ports and reported harness 502s as chart failures.

Security

Each finding...

Read more