Skip to content

Security overview for buyers

This page answers the questions a vendor security review asks, in the order those questionnaires ask them. Every answer links to code, an ADR, a test or a CI job. Where the honest answer is “no”, it says no.

Halos is Apache-2.0 open-source software that you run. There is no Halos-operated service and no vendor hosting your data: your security posture is the posture of your deployment. The deeper documents are the security model (the design) and the threat model (assets, attackers, controls). Companion pages: control mapping and data flow and PII inventory.

Question Answer
Hosted by the vendor? No. Self-hosted: you run halo-server, the gateway, the collector and the registry
Independent assurance? None. No third-party penetration test, code audit, SOC 2 report or ISO certificate. SECURITY.md states it
Release integrity ed25519- or ECDSA P-256-signed releases and ring pointers (key in a file, AWS KMS or an HSM via an external signer); cosign-signed checksums and images; build-provenance attestations; SBOMs
Authentication OIDC (console and gateway), SAML 2.0 (console, ADR-0039), SCIM 2.0, short-lived workload and agent credentials
Encryption in transit Not terminated by Halos services. Put TLS in front of them. Outbound calls use Go’s TLS defaults
FIPS 140-3 cryptography FIPS builds, no Halos certificate. _fips archives and -fips images are built against the Go Cryptographic Module v1.0.0; every algorithm on the signing path is inside it. See FIPS 140-3 builds
Audit Hash-chained, tamper-evident audit log with SIEM export
Customer data in prompts Passes through the gateway to the model provider you chose; not stored by Halos unless you enable shadow or prompt telemetry
Vulnerability reporting GitHub private vulnerability reporting; 3-business-day acknowledgment
Untrusted clients and networks; the gateway boundary that verifies OIDC JWTs, strips x-halo headers and fails closed on the model allowlist; a supply chain where a CI or HSM key signs releases into an OCI registry; halod as root verifying pointer and release before writing managed config; and halo-server issuing device tokens and a kill list signed with a separate key. Untrusted clients and networks; the gateway boundary that verifies OIDC JWTs, strips x-halo headers and fails closed on the model allowlist; a supply chain where a CI or HSM key signs releases into an OCI registry; halod as root verifying pointer and release before writing managed config; and halo-server issuing device tokens and a kill list signed with a separate key.

Components (package map, architecture):

Plane Components Trust note
Control halo CLI, policy repo (git), OCI registry, halo-server The policy repo’s merge gate and the release signing key are the root of trust. Halos does not prevent a malicious merge
Client halod (root, on laptops and workspaces), the coding CLIs halod applies only releases that verify against its pinned key, writes only to an allowlist of paths and runs no shell installers by default
Traffic halo-proxy or halo-kong, halo-shadow Cohort comes from the verified identity only; client x-halo-* headers are stripped; unknown models fail closed
Evidence OTel collector, ClickHouse The client-reachable receiver is untrusted; only the authenticated gateway receiver can drive an automatic kill

Boundaries and the control at each are tabulated in the security model. Four design invariants hold across the codebase (AGENTS.md): no permission-bypass modes are ever rendered; adapters warn rather than silently drop a setting; client-supplied identity headers never choose a cohort; and no auto-merge or auto-promote exists, so a human owns every irreversible step. The MCP server has no tool that publishes, retags, merges or pushes to main (internal/mcpserver/write_safety_test.go).

Multi-tenancy: Halos is an organization-scoped tool, not a multi-tenant SaaS. One deployment serves one org; org units scope policy and ownership inside it (ADR-0018).

Subject Mechanism Detail
Developer, console OIDC authorization-code login against your IdP 12-hour session in an HttpOnly, SameSite=Lax cookie, Secure over https; state-changing requests refused when Origin or Sec-Fetch-Site says cross-site; failed logins rate-limited per client IP (internal/server/auth.go)
Developer, gateway OIDC JWT verified at every request iss, aud, exp, nbf; RS256, ES256 or EdDSA only; none and HS* rejected; JWKS refetch throttled to once per 30 seconds per issuer (internal/identity/oidc.go)
Gateway behind your own gateway trusted_header Honored only from trustedProxyCIDRs; fails closed without a pin
Laptop Single-use enrollment token (15 minutes), then a device token, stored as SHA-256, revocable, expiring after 90 days internal/server/portal.go, devices.go
CI job or cloud workload RFC 8693 exchange of the job’s OIDC token for a credential of 15 minutes by default, 1 hour at most; per-issuer claim rules that must pin the tenant ADR-0023
Subagent or A2A caller Delegated credential with an act chain, scope that only narrows ADR-0024
IdP provisioning SCIM 2.0 with a dedicated bearer token held as SHA-256 ADR-0022
Gateway to halo-server Per-capability bearer tokens (kill list, posture, budget, directory, DLP); 32+ characters and 128+ bits of estimated entropy, enforced at startup cmd/halo-server/main.go

Authorization is one table: five roles (viewer, operator, approver, auditor, admin) mapped from IdP groups, recomputed on every request, deny by default, with every denial audited. A test fails when a route is added without an entry (TestRoutePermsCoverMux in internal/server/rbac_test.go; ADR-0015).

MFA, password policy and conditional access are the IdP’s. Halos adds nothing to them.

Every algorithm Halos uses, from the source:

Purpose Algorithm Where
Release and ring-pointer signatures ed25519 over the digest, or ECDSA P-256 over its SHA-256 (ADR-0038); the manifest records the algorithm and key id; halod verifies either against its configured keys, each under its own algorithm only, and never shells out to cosign internal/bundle/sign.go, keys.go, pointer.go
Signing key custody file:// PEM (default), awskms:// (AWS KMS asymmetric key, private half never leaves KMS) or exec:// (external signer: PKCS#11 HSM, GCP/Azure KMS, Vault). Every remote signature is verified against the key before use internal/signer/, key management
Optional co-signature on releases and pointers cosign: key file, KMS URI or keyless (Fulcio and Rekor) cmd/halo/cmd_release.go (--cosign-key, --cosign-keyless)
Artifact (skill, hook, plugin) signatures ed25519 (halo artifact sign) or a cosign key agent supply chain
Kill list ed25519 or ECDSA P-256 over halo-killswitch-v1\n plus the payload, with a key separate from the release key (file or key URI) internal/gateway/killswitch.go
Workload and delegated-agent credentials EdDSA JWTs (halo-workload+jwt, halo-agent+jwt) signed with a third, dedicated key internal/identity/workload.go, delegation.go
Inbound IdP and CI token verification RS256, ES256, EdDSA internal/identity/oidc.go (AllowedAlgs)
Remote AgentCard signature verification ed25519, ECDSA, RSA (PKCS#1 v1.5 and PSS, SHA-256) internal/a2a/card.go
Console session cookie HMAC-SHA256 over base64 JSON. Integrity only: the cookie is not encrypted and carries the subject and groups internal/server/auth.go (seal)
Webhooks (controller notifications, SIEM webhook sink) HMAC-SHA256 over timestamp + "." + body; 5-minute replay window for receivers internal/controller/notify.go, internal/server/audit_export.go
Inbound GitHub cloud-agent webhooks HMAC-SHA256 verification internal/cloudagents/task.go
Audit chain SHA-256 over every other field of each entry internal/server/audit.go (sum)
Token storage SHA-256 (device, enrollment, SCIM) internal/server/devices.go, portal.go
Pseudonyms HMAC-SHA256 under a salt: halo.unit, ledger path and repository hashes internal/telemetry/gwmetrics/gwmetrics.go, internal/ledger/ledger.go
Shadow pairs at rest (optional) AES-256-GCM with a module-generated random 96-bit nonce, 32-byte key from a file internal/shadow/shadow.go
Binary integrity sha256 or sha512 pinned in the signed manifest cmd/halod/install.go
Upstream auth AWS SigV4 for Bedrock (gateway’s own identity); service-account JWT signed RSA-SHA256 for Vertex internal/gateway/upstreamauth/

TLS:

  • halo-server, halo-proxy and halo-shadow have no TLS listener. Terminate TLS at your ingress or load balancer; the Helm chart’s own comments say so (deploy/helm/halos/values.yaml). Only the standard library’s crypto/tls is used, in two places, both setting a TLS 1.2 minimum: the SIEM syslog sink (internal/server/audit_export.go) and AgentCard fetches (internal/a2a/card.go).
  • Other outbound HTTPS (providers, GitHub, vendor consoles, the registry) uses Go’s default client configuration for the Go version in go.mod (1.25). Halos sets no cipher suites.
  • Credentials and tokens go only over https; http is accepted only to loopback for the vendor admin, ROI and copilot clients. The kill-list URL must be https unless explicitly marked in-cluster.
  • Postgres to a non-loopback host requires sslmode=verify-full (internal/pgstore/pgstore.go).

At rest:

  • halo-server does not encrypt its own data at the application layer. The --data-dir JSONL logs (fleet, devices, audit, requests, revocations) and the Postgres tables hold identifiers, group names, hostnames and token hashes, never token or key plaintext. Encryption at rest is the storage layer’s job: an encrypted PVC (CSI driver or cloud disk encryption) for --data-dir, and your managed Postgres’s storage encryption (RDS, Cloud SQL and Azure Database encrypt by default) for the Postgres backend. State this reliance in your own control evidence.
  • Shadow pairs, the one store that holds prompt and response text, are encrypted by Halos itself when -pair-key-file is set (table above).

Key management:

  • The release signing key is the root of trust. Keep it in AWS KMS (--key-uri awskms://...), in an HSM behind an external signer (--key-uri exec://...), or as a file in CI secrets (key management, ADR-0038). Rotation, revocation (revokedKeys) and compromise handling are in key management and production deployment. There is no threshold signing and no key revocation list in the protocol beyond revokedKeys in each halod config.
  • Three keys are kept apart on purpose: release, kill switch, workload credential. A stolen kill key cannot sign a release. The release and kill-switch keys can live in a KMS or HSM; the workload-credential key is still a file.
  • FIPS 140-3: builds, not a certificate. The _fips archives and -fips images are built with GOFIPS140=v1.0.0, Go’s frozen FIPS 140-3 module (the version the Go team submitted for CMVP validation; check the CMVP listing for its current status), so the module runs its self-tests and fips140=on is the default. Every algorithm on the signing, verification, TLS, session and encryption paths (ed25519, ECDSA P-256, SHA-256, HMAC-SHA256, AES-256-GCM) is inside the module, and those packages pass their tests under GODEBUG=fips140=only in CI. Halos itself holds no FIPS certificate, and the standard (non-_fips) artifacts make no FIPS claim. Details and the operator requirements (HMAC secrets of at least 112 bits) are in FIPS 140-3 builds.
  • Audit log. Privileged actions append to a hash chain (seq, prev, hash), fsynced before the response, and fail closed with HTTP 500 if the append fails. Verify with halo audit verify or GET /api/v1/audit. Tail truncation is undetectable from the log alone: the SIEM copy closes that gap (audit log).
  • SIEM export. Every entry, at least once and in order, to Splunk HEC, a signed webhook (Datadog and others) or syslog over TLS, native or OCSF (SIEM export).
  • Session ledger. One redacted record per agent session, who and what and on whose evidence (session ledger).
  • Gateway request log. Method, path, status, bytes, duration, ring, remote address, experiment, variant, upstream, model. Never bodies, headers or query strings (cmd/halo-proxy/proxy.go).
  • Metrics. Prometheus on halo-server, halo-proxy, halo-shadow and halo-kong (reference); example alert rules in the operations guide.
  • Not provided: log retention policy enforcement, time synchronization, log review. Retention defaults are in the inventory.

Of Halos itself:

Control Where
govulncheck on every PR and push to main vuln job in ci.yml; make vuln locally
golangci-lint, go vet, go test -race, go mod tidy drift check lint, test, tidy jobs
Native Go fuzz targets over parsers of untrusted input: 10 seconds per PR, 10 minutes per target nightly fuzz job; fuzz-nightly.yml; scripts/fuzz.sh
OpenSSF Scorecard, weekly and on main; results kept as a workflow artifact for 5 days, not published scorecard.yml
GitHub Actions pinned to full commit SHAs in every workflow; workflows default to contents: read .github/workflows/*.yml
Web and docs builds with npm ci --ignore-scripts in the release path; the npm build runs in an unprivileged job with no signing credentials web job; release.yml
Private vulnerability reporting, severity by CVSS v3.1, fix targets SECURITY.md

Gaps: there is no Dependabot or Renovate configuration in the repository, so dependency updates are manual and govulncheck is the detective control. Scorecard results are not published or tracked. There is no third-party penetration test.

For your estate: versionGate and rollouts get a patched coding CLI to the fleet in stages and refuse unpinned versions (reliable upgrades). Halos does not scan your hosts.

How the code gets written and merged, stated plainly.

  • Agents write and review code. The project is developed with AI coding agents in the loop. AGENTS.md is the instruction file for contributors’ agents, and the sdlc-* workflows run Claude as a builder (on an @claude comment), a reviewer on every PR, a QA pass and a security pass. The repository holds no measure of what share of the code agents wrote.
  • Agents do not merge. The workflows can push only to a claude/-prefixed branch and open a PR. An optional policy-gated auto-approve step (sdlc-autoapprove.yml) is off unless the repository variable SDLC_AUTOAPPROVE=on, only labels a PR, never authors an approval and never merges. The /approve command queues auto-merge only when SDLC_AUTO_MERGE=true and after branch protection is satisfied. The same rule is a product invariant: Halos itself has no auto-merge or auto-promote.
  • Agent review is a check, not a reviewer of record. The review and QA jobs are LLM-driven. CONTRIBUTING.md lists review and qa as required to merge and security as advisory, and says a maintainer merges. That a status check is required is a GitHub branch-protection setting, which is not in the repository: UNVERIFIED here. Verify it in the repository settings before relying on it.
  • One maintainer today. CODEOWNERS lists a single owner for every path, and GOVERNANCE.md says the MAINTAINERS file will be added with the first external maintainer. Load-bearing changes need an ADR and, with one maintainer, a 5-business-day comment window. There is no independent second approver for the maintainer’s own changes.
  • Gates every change passes (CI). Build and vet, go test -race across the module, lint, govulncheck, cross-OS builds (Linux, macOS, Windows), end-to-end tests on real binaries, a kind (Helm) UAT suite, real-CLI and Kong OSS UAT suites, fuzzing, docs and console builds, and a generated-docs drift check (ci.yml, docs-gen.yml).
  • Releases. A tag on main runs gate (strict SemVer, tag on main, CHANGELOG section, go vet, go test -race), an unprivileged web build, GoReleaser with SBOMs, cosign keyless signatures and image signing, a build-provenance attestation, then verify, which downloads what was published and checks the signature, checksums, attestation, image signatures and install.sh the way a user gets them (release.yml, RELEASING.md). Halos’ own artifacts are signed keyless with the workflow identity, so no long-lived signing key for them exists to steal.
  • Design records. Load-bearing changes get an ADR first (docs/adr/); every claim in the security model is bound to a named test that CI fails on if it is renamed or removed (internal/docgen/security_model_test.go).
  • Developer Certificate of Origin. Contributors sign off commits (git commit -s); nothing in CI was found that enforces it.
  • Report through GitHub private vulnerability reporting. Acknowledgment within 3 business days, triage within 10, a fix or mitigation plan within 30 days (critical and high: target 7 days), then a patched release, a published advisory and a CVE request. Coordinated disclosure up to 90 days. No bug bounty (SECURITY.md).
  • Supported versions: the latest 0.x release and main; older releases are not patched (support policy).
  • There is no Halos-operated service, so there is no vendor-side breach of your data to notify you about. A flaw in the software is announced through the advisory channel.
  • Your incident levers in the product: kill switch, incident freeze, session, device and credential revocation, each requiring a reason and written to the audit log (security model). The incident plan around them is yours.

halo-server runs as several replicas on Postgres with a fenced controller lease (ADR-0021). Clients keep the last good release. Gateways keep the last accepted kill list and policy snapshot; their directory and posture lookups follow the configured fail mode and grace window (SCIM provisioning, gateway gates). Backup, restore and the files you cannot lose are in operations. No recovery time or availability figure is promised; the example SLOs there are targets for you to set.

Stated so you do not have to find them.

Gap Detail
No third-party assurance No penetration test, code audit, SOC 2 or ISO certification. The threat model is the maintainers’ own
No Halos FIPS certificate _fips builds use the Go Cryptographic Module v1.0.0, but Halos is not itself validated; fips140=only is tested on the crypto packages, not the whole tree (a FIPS build refuses to start with an HMAC salt or webhook secret under 14 bytes). The workload-credential key has no KMS backend (FIPS 140-3 builds)
One built-in KMS awskms:// is built in; GCP, Azure, Vault and PKCS#11 need an exec:// adapter you supply (key management)
SAML for the console only Gateways authenticate OIDC tokens; no SAML single logout (ADR-0039)
No TLS in the services Terminate TLS in front of halo-server, halo-proxy and halo-shadow. No mTLS, no per-user rate limiting
Single signing key No threshold signing; compromise is fleet-wide (residual risk)
Cookie not encrypted The console session cookie is signed, not encrypted, and carries the subject and groups
Audit retention unbounded, tail truncation undetectable alone Pin the chain head or use the SIEM copy
Root can defeat client enforcement Gateway gates (posture, versionGate) are the backstop; posture is halod’s self-report
DLP is pattern-based Misses secrets with no recognizable format; injection scoring is a tripwire, not a guarantee
Single maintainer; branch protection unverified here See SDLC
No dependency-update bot Dependencies move by hand
Not validated at scale Tested with unit tests, the compose demo, and kind, real-CLI and Kong OSS UAT suites in CI. Not validated on a real fleet, Kong Enterprise, real AWS Bedrock, or Windows fleets; release binaries are not Authenticode-signed

Short answers for a SIG or CAIQ response, by domain. “N/A” means Halos is not the system of record for it.

Domain Question (paraphrased) Answer
Governance, risk and compliance Independent attestation? No. See known gaps
Application and interface security Secure SDLC, code review, SAST, dependency scanning? Yes, with the caveats in SDLC: CI lint, race tests, fuzzing, govulncheck; agent plus human review; no automated dependency updates
Application and interface security Input validation on untrusted data? Yes. Parsers of untrusted input have fuzz targets; request bodies are size-limited; gateway fails closed. Tests
Change control Formal change management, approvals, rollback? Yes for the managed fleet: PR, plan, eval, rings, human merge, quorums, freezes, rollback by re-pointing (change governance). For the Halos project: PR and release gates above
Cryptography, encryption and key management Algorithms, key custody, rotation? Cryptography. Customer holds the keys (file, AWS KMS or HSM via an external signer). Rotation procedure documented. FIPS 140-3 builds on the Go Cryptographic Module; no Halos certificate
Identity and access management SSO, MFA, RBAC, least privilege, deprovisioning? OIDC SSO; MFA at the IdP; five-role RBAC, deny by default; SCIM deprovisioning revokes immediately. Authentication
Logging and monitoring Tamper-evident logs, SIEM integration, retention? Hash-chained, SIEM export native or OCSF; retention is yours (inventory)
Threat and vulnerability management Scanning, patch SLAs, pen test? govulncheck per PR, weekly Scorecard, fuzzing; disclosure SLAs in SECURITY.md; no pen test
Supply chain management SBOM, signed artifacts, provenance, third-party risk? SBOMs, cosign signatures, build-provenance attestations, SHA-pinned Actions. For your fleet: AI-BOM and SLSA-format provenance per release (control mapping)
Security incident management Incident process, breach notification, disclosure? Disclosure per SECURITY.md. No hosted service, so no vendor-side data breach path. Your IR plan uses the product levers above
Data security and privacy Data inventory, classification, retention, residency, deletion? Data flow and PII inventory. Residency is where you deploy. Some stores are unbounded by default and have no erasure API
Data security and privacy Is customer content used to train models? Halos does not train models and sends nothing to its maintainers. The model provider’s terms govern what the provider does with prompts
Business continuity and resilience HA, backup, DR? HA on Postgres; documented backup and restore; gateways and clients degrade to last good state. No tested DR figures
Datacenter and physical security Physical controls? N/A, self-hosted
Human resources Background checks, training? N/A for the software; see the project’s Code of Conduct only
Interoperability and portability Open formats, exit? Apache-2.0; policy is YAML in your git; releases are OCI artifacts; audit is JSONL or OCSF; AI-BOM is CycloneDX 1.6
Infrastructure and virtualization security Network segmentation, hardening? Helm NetworkPolicy is provided; non-root, read-only filesystem and the like are in the chart values; segmentation is yours