Configuration
All Admina settings are configured via admina.yaml or environment variables.
A few settings accept both forms โ the PII engine, for instance, is the top-level
pii_engine: key in admina.yaml (a sibling of
forensic_store: and auth_provider:, not nested under
domains) or the ADMINA_PII_ENGINE environment variable โ while
others, such as the gateway variables and ADMINA_GUARD_FAIL_MODE, are
environment-variable-only.
Secrets are auto-generated on first launch and stored in an encrypted vault
(.admina/secrets.json). The defaults are tuned so that the full Docker Compose
stack lands in the OISG adequate band out of the
box, and a bare pip install lands at 75, in the Good coverage
band; turning any capability off immediately lowers the score. There are four supported
bootstrap paths:
# 1. CLI (recommended) โ local mode, no Docker pip install admina-framework admina init my-project cd my-project && admina dev # 2. Generate admina.yaml interactively (or non-interactively for CI) admina configure # interactive wizard admina configure --non-interactive # scaffolds defaults to ./admina.yaml # 3. Docker Compose without the CLI ./scripts/bootstrap-secrets.sh # writes random creds to .env docker compose up --build # 4. Manual cp .env.example .env # then fill in values
Authentication
Admina ships with dashboard authentication enabled by default. Two credentials drive the platform:
ADMINA_API_KEYโ protects every governance endpoint. The only exempt paths are/health,/docs,/openapi.json,/redoc,/metricsand the bundled dashboard shell (/,/heimdall.png,/vendor/*) โ the dashboard's own/api/*calls still authenticate, via a signed session cookie. Note that/metricsserves Prometheus counters unauthenticated: keep it off the public internet or in front of your own reverse proxy.ADMINA_DASHBOARD_PASSWORDโ shared login for the bundled Alpine.js dashboard, Grafana, and ClickHouse.
Both can be managed via the CLI:
admina password show # display current credentials admina password reset # regenerate all credentials admina password set # set a custom password
Include the API key in every request to a protected endpoint:
# Via header curl http://localhost:8080/api/stats \ -H "X-API-Key: $ADMINA_API_KEY" # Via Bearer token curl http://localhost:8080/api/stats \ -H "Authorization: Bearer $ADMINA_API_KEY"
For local development only, set ALLOW_UNAUTHENTICATED=true to
disable auth. A warning is logged at startup. The docker-compose default is
false.
ADMINA_API_KEY and no auth providers now rejects
protected requests (with a loud startup warning) instead of authenticating every
caller as admin. To run without a key you must explicitly set
ALLOW_UNAUTHENTICATED=true.
ADMINA_GUARD_FAIL_MODE controls what happens when a
pluggable governance guard raises an exception. The default open skips the
guard and records it as an ERROR entry in the decision's checks;
setting closed turns the exception into a BLOCK (also recorded as
an ERROR check). It is enforced on the request side of three
governed surfaces โ the SDK (GovernedModel.ask() and .stream()),
the MCP proxy (POST /mcp) and the OpenAI-compatible gateway
(POST /v1/chat/completions). Response-side inspection fail-closed is
MCP-proxy-only, where a fail-closed block additionally writes an explicit forensic
ERROR record. Two caveats worth stating plainly: only
ValueError, RuntimeError, OSError and
TypeError raised inside a guard are caught โ any other exception propagates;
and a request-side fail-closed BLOCK is still downgraded to ALLOW under
ADMINA_GOVERNANCE_MODE=observe or dry-run, where it is recorded
as a shadow decision instead. An unrecognised value is rejected at startup (the proxy
fails fast), and the SDK reads the variable once, at GovernedModel
construction.
All variables
UPSTREAM_MCP_URL http://localhost:9000 Default upstream MCP server URL CORS_ORIGINS http://localhost:3000,http://localhost:8080 Comma-separated allowed CORS origins LOG_LEVEL INFO Logging verbosity: DEBUG, INFO, WARNING, ERROR ADMINA_ENGINE auto Governance-engine backend, uniform across proxy / SDK / integrations: auto | python | rust (since v0.10.0; unrecognized value raises). See the Rust engine guide. ADMINA_PII_ENGINE spacy-regex PII detection engine: spacy-regex (default) | presidio (Microsoft Presidio, behind the [presidio] extra, EN + IT). Also settable as the top-level pii_engine: key in admina.yaml. An unknown value raises โ there is no silent fallback. Selecting presidio bypasses Rust acceleration, which only ever applies to spacy-regex. See Data Sovereignty. ROUTING_CONFIG_PATH โ Path to multi-upstream routing config (OpenClaw mode) ADMINA_GATEWAY_UPSTREAM http://localhost:11434/v1 Upstream base URL for POST /v1/chat/completions and GET /v1/models. Must already include the API base path (typically /v1); the default points at a local Ollama. The gateway forwards no credentials, so the upstream must be keyless (Ollama, local vLLM, LM Studio). ADMINA_GATEWAY_BLOCK_MESSAGE This request was blocked by the Admina governance policy. Assistant message returned in the synthetic HTTP 200 completion โ or, on a streaming request, in the synthetic SSE chunk โ when the governance pipeline blocks a request (finish_reason: content_filter). ADMINA_GATEWAY_MODELS_ALLOWLIST โ Comma-separated model ids; empty means full passthrough. When non-empty, filters the data[] returned by GET /v1/models. It does not restrict which model POST /v1/chat/completions may forward.
These three are environment-variable-only โ there is no admina.yaml equivalent. See API Reference โ OpenAI-compatible gateway.
ADMINA_API_KEY โ API key for all endpoints. Auto-generated by the vault or bootstrap-secrets.sh ADMINA_DASHBOARD_PASSWORD โ Shared password for dashboard, Grafana, and ClickHouse UIs ALLOW_UNAUTHENTICATED false Set true only for local development โ bypasses the API key check REDIS_URL redis://localhost:6379/0 Redis connection URL โ session state, rate limiting, hash chain FORENSIC_BACKEND memory One of memory | filesystem | s3 FORENSIC_BASE_DIR โ Required when FORENSIC_BACKEND=filesystem (explicit opt-in, no default) FORENSIC_S3_ENDPOINT โ S3 endpoint URL when FORENSIC_BACKEND=s3 โ boto3-based, S3-compatible (leave empty for AWS S3). Standard AWS_* credentials are honoured if the FORENSIC_S3_* equivalents are empty. FORENSIC_S3_BUCKET forensic-blackbox Bucket that holds the forensic hash chain FORENSIC_S3_LOCK false Enable S3 Object Lock (WORM COMPLIANCE mode); retention set by FORENSIC_S3_LOCK_DAYS (default 7 years) ADMINA_FORENSIC_STATE_KEY โ Optional HMAC-SHA256 key that signs the forensic chain-state file _chain_state.json into a _chain_state.json.sig sidecar (a file next to the records for the filesystem backends, a separate object under that key in the same bucket for S3). The signature is the hex digest computed over the exact bytes of the state payload as written โ chain_head, record_count, updated_at. On restart a valid signature is trusted as a fast path; a missing or invalid signature is treated as untrusted โ a CRITICAL event is logged and the chain state is reconstructed from the stored records instead. Honoured by ForensicBlackBox (both its filesystem and s3 backends) and by the FilesystemForensicStore plugin. Recommended in production.
Leaving ADMINA_FORENSIC_STATE_KEY unset is not a vulnerability: the state file is simply unsigned, and the baseline truncation protection โ reconstructing the chain state from the persisted records โ is retained.
Programmatic form. Both classes take the key as the keyword argument
state_signing_key: str | None = None. The explicit argument wins; the
environment variable is only read when the argument is left at None
(or empty), so SDK users need no env var at all:
from admina.domains.compliance.forensic import ForensicBlackBox from admina.plugins.builtin.forensic.filesystem import FilesystemForensicStore # key material comes from your own secret manager โ never hardcode it box = ForensicBlackBox( filesystem_dir="/var/lib/admina/forensic", state_signing_key=signing_key, ) store = FilesystemForensicStore( base_dir="/var/lib/admina/forensic", state_signing_key=signing_key, )
Choose your forensic backend deliberately. The hash chain that makes
Admina's audit trail tamper-evident depends on persistent storage. The no-config
fallback is memory (records lost on restart); since v0.9.5 the
admina init templates and the dev docker-compose.yml scaffold
with filesystem instead:
memory default Local dev, tests, demos. Records LOST on restart (loud warning at startup). filesystem โ Single-host on-prem / air-gapped. Persistence depends on the host filesystem; not ideal for HA. Requires FORENSIC_BASE_DIR. s3 Apache 2.0 (boto3) Recommended for production. Works against any S3-compatible service โ AWS S3, Cloudflare R2, Backblaze B2, SeaweedFS (Apache 2.0), Garage (AGPLv3, as a backend), MinIO, Ceph RGW. Object Lock supported. s3 backend: point
FORENSIC_S3_ENDPOINT at your MinIO server (boto3 speaks the S3 API). One thing
to keep in mind that is not an Admina obligation but MinIO's own:
MinIO Server is AGPLv3 โ deploying it as part of a
network-accessible service can be read to require publishing the source code of
the combined application (the commercial license removes this obligation but is
paid). If you want a fully permissive stack, SeaweedFS
(Apache 2.0, lightweight, single-binary S3 gateway) is a FOSS-friendly drop-in.
FORENSIC_BACKEND=minio still works for now โ it routes to the
s3 backend with a migration warning.
CLICKHOUSE_HOST localhost ClickHouse host for analytics CLICKHOUSE_PORT 8123 ClickHouse HTTP port CLICKHOUSE_DB admina ClickHouse database name CLICKHOUSE_PASSWORD โ ClickHouse password. Change in production. OTEL_ENDPOINT http://localhost:4317 OTLP gRPC collector endpoint ADMINA_GOVERNANCE_MODE enforce How the firewall reacts to flagged traffic: enforce (default, blocks), observe (never blocks, logs "would have blocked"), dry-run (like observe + tags the response). Use observe for the first 1โ2 weeks of a new deployment. Honoured under this name since v0.11.1; on v0.11.0 it was silently ignored and the unprefixed GOVERNANCE_MODE was read instead. ADMINA_GUARD_FAIL_MODE open What happens when a pluggable governance guard raises: open (default) skips the guard and records an ERROR check; closed turns the exception into a BLOCK. Shared by the SDK, the MCP proxy and the OpenAI-compatible gateway (new in v0.11.0). An unrecognised value is rejected at startup.
There is no environment variable that selects the active domains โ each one is toggled
individually in admina.yaml under domains.<name>.enabled
(data_sovereignty, ai_infra, agent_security,
compliance). Three of them ship enabled; ai_infra is opt-in and
defaults to false.
RATE_LIMIT_MAX_REQUESTS 100 Max requests per session per window RATE_LIMIT_WINDOW_SECONDS 60 Rate limit window in seconds LOOP_WINDOW_SIZE 10 Number of past requests to compare for loop detection LOOP_SIMILARITY_THRESHOLD 0.85 Cosine similarity threshold (0.0โ1.0) to trigger loop detection GRAFANA_ADMIN_PASSWORD โ Grafana admin password โ docker-compose.yml passes it straight through to GF_SECURITY_ADMIN_PASSWORD and falls back to an empty value when unset. The admin username is fixed at admin. Set it in production. Where does admina.yaml come from? There is no environment
variable for its path, and no --config flag. The library loader looks for
admina.yaml in the current working directory, then in the installed package
root, and falls back to environment variables / .env when neither exists; the
CLI is stricter โ admina dev reads admina.yaml from the current
directory only and exits if it is missing. To write one somewhere else, use
admina configure -o /path/to/admina.yaml
(the --output flag defaults to ./admina.yaml).
Docker Compose environment
When using the included docker-compose.yml, run
./scripts/bootstrap-secrets.sh to generate all secrets at once.
The script writes random values to .env and is idempotent โ
rerun with --force to regenerate.
./scripts/bootstrap-secrets.sh docker compose up --build # An API key and one password shared by every UI โ the two values the # script prints. Both are written to .env: ADMINA_API_KEY=<random 32-byte hex> ADMINA_DASHBOARD_PASSWORD=<random 20-char password> CLICKHOUSE_PASSWORD=<same value> GRAFANA_ADMIN_PASSWORD=<same value> MINIO_SECRET_KEY=<same value> # unused residue of the removed MinIO backend (v0.9.5) # .env also gets a separate, unprinted WEBUI_SECRET_KEY (another random # 32-byte hex), used only by the Open WebUI service that `admina init` # adds to its compose file when the ai_infra domain is on.
Production checklist
- Run
bootstrap-secrets.sh(or let the CLI vault generate them) โ never commit.env - Leave
ALLOW_UNAUTHENTICATEDat itsfalsedefault - Use
FORENSIC_BACKEND=s3with TLS and Object Lock for a tamper-evident production audit trail - Set
ADMINA_FORENSIC_STATE_KEYso the forensic chain-state file is HMAC-signed โ an unsigned or tampered state is then detected and the chain rebuilt from the records - Decide your guard fail mode deliberately:
ADMINA_GUARD_FAIL_MODE=closedmakes a guard exception aBLOCKinstead of a skipped check - Configure
CORS_ORIGINSto your actual frontend domains (the proxy warns on wildcard) - Set
LOG_LEVEL=WARNINGto reduce log volume - Point
OTEL_ENDPOINTto your observability platform