OGuardAI
Operations

Secrets and Key Ceremony

Self-hosted secret provisioning for OGuardAI, the full Tier 1 and Tier 2 secret set, generation, validation, and rotation

OGuardAI is self-hosted. You run the entire runtime in your own infrastructure. There is no hosted or managed OGuardAI service, and no on-premises-as-a-service offering, so no third party (including Oronts) ever holds your secrets. You generate the secrets below in your own environment, store them in your own secrets backend, and inject them into the runtime at start. Key custody is entirely the operator's responsibility.

This page is the site mirror of the canonical operator runbook in the repository at docs/key-ceremony.md. When the two differ, docs/key-ceremony.md wins.

Never commit real secret values. Never paste them into chat, a ticket, or a log. The runtime never logs secret material, only key ids and fingerprints.


The secret set

Tier 1: cryptographic root secrets

A leak of any Tier 1 secret breaks the trust boundary (sealed sessions, the audit chain, or corpus token identity). Generate each as 32 random bytes, base64-encoded (openssl rand -base64 32).

Env varConfig keyRoleIf unset
GUARDAI_SESSION_SECRETsession.secret / session.keyringMaster session-sealing secret. The AES session key, and (unless set separately) the audit-HMAC, corpus-identity, revocation, and prompt-preamble-marker keys derive from it.Required. An ephemeral random secret is used and sessions do not survive a restart.
GUARDAI_AUDIT_SECRETaudit.secretDedicated audit-chain HMAC root, independent of the session secret so the tamper-evident audit log verifies across a session-key rotation.Falls back to session-derived.
GUARDAI_CORPUS_SECRETrag.corpus_secretRAG deterministic corpus-token identity.Falls back to session-derived.
GUARDAI_REVOCATION_SECRET(revocation)Revocation digest HMAC.Falls back to session-derived.
GUARDAI_POLICY_SIGNING_KEY(policy)Policy integrity signing.Policy signing disabled.
per-tenant BYOKsession.tenants.<id>.session_keysA tenant seals under its own key domain. Raw inline material is accepted only in dev; production references a keyring.Tenant seals under the root-derived key.

Tier 2: access credentials

Env varRole
GUARDAI_API_KEY_<NAME>Server client keys. Each carries per-key entitlement companions: _SCOPES, _ALLOWED_POLICIES, _ALLOWED_OUTPUT_CHANNELS, _ALLOWED_DESTINATIONS.
GUARDAI_PROXY_API_KEYTransparent proxy client auth.
GUARDAI_DETECTOR_API_KEYAuth to the Python NER detector sidecar (must match the sidecar's configured key).
GUARDAI_BEARER_TOKEN / GUARDAI_BEARER_TOKEN_FILEBearer credential the MCP server presents to the OGuardAI API (alternative to GUARDAI_API_KEY; mutually exclusive).
GUARDAI_TLS_KEYTLS private key, if terminating TLS in-process rather than at a load balancer.
session.redis_urlRedis connection string; may embed a password when the shared store backends are Redis.

Ceremony

  1. Generate the set in a controlled environment (an operator workstation or a CI secrets step), not on a shared host:

    deploy/scripts/gen-secrets.sh --all

    --tier1 prints only the root secrets; --api-key NAME prints one extra client key line. The script writes nothing itself: it prints an .env template with fresh high-entropy values to stdout for you to pipe into your secrets backend.

  2. Store the values in your secrets backend. Options: AWS Secrets Manager, HashiCorp Vault, a Kubernetes Secret, or an encrypted .env your orchestrator injects. Do not write them to a file the repo tracks.

  3. Inject them into the runtime as environment variables (Helm: a referenced Secret; docker-compose: an env_file or secrets; bare process: your process manager). The container image never bakes a secret in.

  4. Validate before serving traffic:

    oguardai config validate --config /path/to/config.yaml

    A placeholder or example session secret, or a malformed value, aborts startup.

Keep every value above a placeholder in your notes and examples. GENERATE_WITH_openssl_rand_base64_32 is the intended shape, never a real-looking secret.


Rotation

Session-key rotation and audit-root rotation are separate ceremonies.

For session sealing, use session.keyring: add a new key id, deploy it everywhere while the old id remains current, switch current_kid, wait one full session TTL, then retire the old id. This avoids client-visible session expiry. If the audit root falls back to session.secret, changing that base secret also rotates the audit root and requires the audit procedure below before the change.

For an audit-root change, stop every writer to the live file, move that file to a private archive, and preserve its owner and mode 0400 or 0600. Put the previous root in audit.retired_secrets, configure the new audit.secret, then restart the runtime so it creates a new exact-0600 live file in the trusted parent. Verify the archive with the same configuration:

oguardai-server audit verify --config /etc/guardai/oguardai.yaml \
  --path /var/lib/guardai/audit/audit-previous.jsonl

The verifier selects the archived file's writing key from its header key id, or verifies configured candidates for a legacy header, and uses one opened descriptor throughout. See Verifying audit-log integrity for source custody and export rules.

Keep GUARDAI_CORPUS_SECRET stable, or already-ingested RAG corpora lose token identity.

Tier 2 access credentials rotate on a fixed cadence (90 days is a reasonable default) and immediately on suspected exposure. Client keys can be rotated one at a time without downtime.

The step-by-step keyring procedure, with its ordering constraints, is in the Key Rotation Runbook. The Deployment guide covers the same rotation from the config angle.


Deployment model

Self-hosted only. The operator runs the runtime and holds all secrets. A managed or hosted SaaS is not offered today, and there is no on-premises-as-a-service offering. Until that changes, managed key custody is not part of the product, and this ceremony is the operator's responsibility end to end.