Security
Identity is proven.
Everything else is denied.
Kirra treats its own fleet as a hostile network: every node proves who it is cryptographically, every carrier is untrusted, every privileged route demands a scope, and every decision lands in a ledger that can't be quietly edited.
Node attestation
Challenge–response, rooted toward hardware
Trust is never asserted by an admin — it's proven per node: an Ed25519 signature
over a fresh 30-second nonce, checked against the node's registered key. Nodes under a
require_tpm_quote policy must additionally present a TPM2 quote whose PCR16 digest matches
their registered measured-boot value, bound to the same nonce.
crates/kirra-safety-authority/src/attestation.rssrc/tpm_quote.rsSECURITY.md
Authorization
Scoped roles, constant-time everywhere
Fail-closed RBAC
Four roles — admin, integrator, auditor, operator — with per-scope gates on every privileged route. The authorization core is a pure function: no env reads, no store access, no way to accidentally default-allow. A missing admin token means 503, never 200.
No timing oracles
Every security-critical byte comparison goes through constant_time_compare — standard
equality on secrets is forbidden by invariant. Token plaintexts are never stored; only SHA-256 hashes.
mTLS with expiry teeth
Client certificates are CA-verified at the TLS layer and pinned by SHA-256 fingerprint to a role. A cert at or past its expiry fail-closes exactly like a revocation; a half-configured TLS listener aborts startup rather than falling back to plaintext.
Audit
A memory that can't be edited
Every trust decision, config digest, campaign mutation, and denied command is a SHA-256 hash-chained ledger entry. The chain survives SIGKILL mid-append as a valid prefix — proven by a power-loss drill in CI — ships off-box to WORM storage, and re-verifies independently with no access to the source database.
Explainable denials
A denied actuator command mints a verdict id and becomes a signed, human-readable artifact: machine
deny-code, operator explanation, the recorded inputs, and the chain fields — served to auditors at
GET /verdicts/{id}.
Federation without trust
Cross-controller trust reports are Ed25519-signed, freshness-windowed (5 s), and replay-proof — nonces burn atomically with the report commit. Generation ordering reconciles conflicting reports deterministically.
Release tokens
The governor signs the digest of the exact validated contract bytes; the actuator verifies “the governor approved exactly these bytes” before anything moves. Key provisioning is fail-closed: no configured source, no key — a non-production dev key never loads by default.
crates/kirra-release-token/src/lib.rsdocs/safety/GOVERNOR_KEY_PROVISIONING.md
Supply chain
Pinned, audited, signed
| Control | Mechanism | Gate |
|---|---|---|
| Dependency policy | cargo-deny: license allowlist, wildcard ban, crates.io-only sources | blocking CI lane |
| Vulnerability audit | cargo-audit on every push | blocking (pipefail) |
| Actions pinning | every GitHub Action pinned to a 40-hex commit SHA, checked by script | blocking CI lane |
| Container provenance | digest-pinned base images; keyless cosign signatures via OIDC | docker workflow |
| Release integrity | tag ↔ version ↔ changelog preflight; safety-case evidence bundle attached to releases | release workflow |
deny.tomlscripts/pin-actions.sh.github/workflows/docker.yml.github/workflows/release.yml
Coordinated disclosure: see SECURITY.md — 48-hour first response.
READ THE SOURCE
Attack the design, not the marketing.
The invariants, the threat analysis, and the audit chain implementation are all public — we prefer adversarial readers.