Security Hardening Checklist
What you'll accomplish: Walk through a production security checklist to ensure your Hoziron deployment is locked down.
The checklist
- Enable TLS — native in-process TLS per surface (
[surfaces.<name>.tls]), or at a reverse proxy in front of Hoziron - Restrict network access — set
surfaces.<name>.allowed_ips(on a main-route surface) to known CIDR ranges - Enable authentication — configure
[auth]withmode = "local"ormode = "oidc"; this is enforced, not just advisory — the daemon refuses to boot with auth disabled on a non-loopback bind (details) - Use role-based API keys —
hoziron-cli auth create-key --role operator --name "deploy-bot" - Store secrets properly — use a secrets manager, inject as env vars (never in config.toml)
- Enable audit trail — configure
[audit]section - Run as non-root — UID 65532 in Docker, dedicated user on bare metal
- Mount config read-only — only
/datashould be writable - Use structured logging —
HOZIRON_LOG_FORMAT=jsonfor parseable, auditable logs - Restrict egress — configure your host/container firewall to allow outbound traffic only to LLM providers and internal services Hoziron needs
- Review CORS settings — only enable if a web dashboard connects directly
- Set key expiration — all non-admin keys should have
--expires-inset - Verify audit chain integrity —
hoziron-cli security verify - Never bypass the credential vault — provider/registry/integration secrets should be
api_key_env/auth_token_envreferences or vault entries, never inline inconfig.toml;vault:read/vault:manageare Admin-only by design, with no key read-back endpoint (details) - Review
carrier-pii-policy.toml— confirm[pii_policy.llm].local_onlycovers every PII type that must never leave the box even tokenised, and that[[pii_policy.tool_rules]]only grantshydrateto destinations that are genuinely licence-designated systems of record (details) - Check licence validity and expiry —
hoziron-cli licence status; an expired or missing carrier licence is a hard boot/runtime block, not a soft warning - Confirm role assignment matches least privilege — use
auditor(notadmin) for compliance reviewers; useservice(notoperator) for machine-to-machine automation (details)
Network security
IP allowlist
[surfaces.api]
listen = "0.0.0.0:4200"
allowed_ips = ["10.0.0.0/8", "172.16.0.0/12"]
Set on a main-route surface only — it's enforced once per merged listener and covers everything sharing that address, including any surface mounted onto it and the unified /__server/health endpoint (unlike a surface's own /health route, which the middleware exempts by path). If you restrict allowed_ips on a listener, make sure your orchestrator's probe source IP is included, or point liveness/readiness probes at a surface's own /health path instead.
Credential management
config.tomlstores the name of the env var (api_key_env = "ANTHROPIC_API_KEY"), never the value- Keys are resolved lazily at request time from the environment
- Error messages reference the env var name, never the key value
- The key store database has 0600 permissions (owner-only)
Container security
- Read-only root filesystem
- Non-root user (UID 65532)
- No shell or package manager in distroless image
- SIGTERM handling for graceful shutdown
Sovereignty attestation is a trust assumption
Hoziron's local_only PII guarantee (ADR-053) is structural for two of the three driver classes:
AlwaysLocaldrivers (Ollama, vLLM, LM Studio, Lemonade) — always resolveSovereignty::Local. No config can change this.AlwaysClouddrivers (Anthropic, OpenAI) — always resolveSovereignty::Cloud. No config can make these local; asovereignty_attestationon either is a hard boot error.
The third class is where operator trust re-enters:
-
Ambiguousdrivers (e.g. the OpenAI-compatible shim, used for in-house/self-hosted endpoints that speak a public protocol) resolveSovereignty::Localonly when the[[provider]]entry setssovereignty_attestation:[[provider]] id = "inhouse.shim" driver = "openai-compatible" url = "http://10.4.2.7:8000/v1" sovereignty_attestation = "carrier confirms 10.4.2.7 is in-boundary"
What this does and does not guarantee:
- Setting
sovereignty_attestationis a claim, not a proof. The platform does not verify that the endpoint is actually inside your network boundary — it trusts the string the operator wrote. If yourlocal_onlyPII policy (see PII Engine) is routing on the assumption this provider never leaves the building, that assumption rests entirely on this attestation being correct. - A carrier who misattests a cloud endpoint as local (by mistake, or deliberately) silently defeats
local_onlyfor that provider. Deliberate misattestation is out of scope for the platform to prevent — it's the carrier's call by design, and no boot check can distinguish "I meant to attest this" from "I lied." - The platform runs one best-effort boot check to catch the accidental case: if an attested-local provider's
urldoesn't parse to a loopback or RFC-1918 private address (127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16), it logs a warning naming the provider and URL. This is informational only — it never refuses to boot, and it is a literal-IP heuristic, not a topology check:- It can false-positive: an internal DNS name (
inhouse.internal.corp), a VPN-peered address, or a non-RFC-1918 private range will all warn even when genuinely in-boundary. If your topology is legitimately more complex than "raw private IP," expect and ignore this warning. - It cannot catch a deliberate misattestation dressed up as a private IP, or any misattestation behind a hostname the carrier controls.
- It can false-positive: an internal DNS name (
- Treat
sovereignty_attestationas an audited, operator-signed statement, not a technical control. The resolved reason (attested-localvs.native-local) is retained specifically so a POPIA auditor can distinguish "structurally guaranteed local" from "operator attested in-boundary" — review attested entries during audits with that distinction in mind.
Verification commands
# Check for security misconfigurations
hoziron-cli security status
# View recent audit trail
hoziron-cli security audit --limit 50
# Verify audit trail Merkle chain integrity
hoziron-cli security verify
# Full system diagnostics
hoziron-cli doctor
Next steps
Related: