The control plane is one Go binary, controlplane, configured by a small set of
flags on its serve subcommand and a handful of environment variables — not a
sprawling config file. The defaults are chosen to fail closed: loopback binds, TLS
on, no shipped credentials. Everything below is taken from the binary’s own command
definitions and composition root; where a setting can’t be confirmed in source, it
isn’t listed here.
Secrets that wire real sources and custody real keys stay in operator-held files or mounted secrets referenced by environment variable — never in the store. For the runnable end-to-end path, see the self-host guide; for the full flag listing, see the CLI reference.
The serve subcommand
controlplane serve runs the REST/web HTTP server and the gRPC server in one
process, with the web console served from the same origin as the API. These are the
common configuration inputs.
| Flag | Default | Purpose |
|---|---|---|
--listen | 127.0.0.1:8443 | HTTP listen address (REST API + embedded web console). |
--grpc-listen | 127.0.0.1:8444 | gRPC listen address (control-plane / collector ingest). |
--data-dir | $OLIVARES_DATA_DIR or ./olivares-data | Audit signing key, TLS material, and — for SQLite — the store file. |
--engine | sqlite | Store engine: sqlite or postgres. |
--dsn | empty (SQLite file in the data dir) | Store connection string. |
--checkpoint-interval | 1h | How often a signed audit checkpoint is written over every tenant chain. 0 disables. |
--insecure | off | Serve plaintext HTTP/gRPC. Localhost development only. |
--seed-demo | off | Load a synthetic sample estate. Refuses to start on a non-loopback bind. |
TLS is on by default. With no --tls-cert/--tls-key supplied, the engine ensures
a self-signed certificate in the data directory once, up front, before any
listener accepts a connection — so both the HTTP and gRPC servers use the same
certificate and neither falls back to plaintext. When it generates that certificate
it logs the SHA-256 fingerprint so clients can trust or pin it.
--insecure is the only way to serve plaintext, and the gRPC path fails closed:
outside --insecure the server refuses to construct a plaintext listener rather than
degrade silently. Use it only against 127.0.0.1 during local development.
--seed-demo provisions a demo administrator with a public, source-tree password
and fabricated estate data — for demos and E2E only. The engine refuses to start it
if either listener is non-loopback. Use a throwaway data directory.
A second tier of flags governs distributed and mutual-TLS topologies —
--admin-dsn and --allow-privileged-db-role (Postgres), --grpc-client-ca
(collector mutual TLS), and --region/--known-regions (data residency). These are
covered below and listed in full in the CLI reference.
Environment variables
The engine reads a small number of environment variables at boot. The ones below are confirmed in the composition root and wiring.
Data directory and sources
| Variable | Effect |
|---|---|
OLIVARES_DATA_DIR | Default data directory when --data-dir is not given (falls back to ./olivares-data). Holds the audit signing key, TLS material, and the SQLite store file. Persist it across restarts. |
OLIVARES_SOURCES_CONFIG | Path to a JSON file that wires real observation sources, identity roster providers, and knowledge document sources before the engine starts. |
OLIVARES_SOURCES_CONFIG is the single input through which non-demo signal sources
and roster providers are resolved. It’s the operator’s secret-bearing configuration
and is deliberately kept out of the store. The engine reads it at boot and registers
every source before the runtime starts.
The handling is honest rather than fail-fast. A missing variable, an unreadable or invalid-JSON file, or a configured-but-empty source list all warn and yield an empty configuration — the engine never aborts the boot. An unconfigured source surfaces a warning instead of crashing the plane or pretending to work: with nothing wired, the access map simply stays empty. To populate it, configure at least one source — see connect a source and, for the cooperative Claude Code path, connect Claude Code.
Authorization decision point
The native attribute-based and role-based access control always govern. An external policy decision point (PDP), when selected, is an additional restrict-only layer that can only narrow the decision the built-in RBAC already made — never widen it.
| Variable | Effect |
|---|---|
OLIVARES_PDP_ENGINE | Selects the external PDP: cedar, opa, or none (empty/none = native ABAC only). |
OLIVARES_PDP_CEDAR_FILE | Cedar engine: path to the operator’s policy file. |
OLIVARES_PDP_OPA_URL / _OPA_PATH / _OPA_TOKEN | OPA engine: base URL, decision path, and bearer token for the Open Policy Agent endpoint. |
Two adapters sit behind one seam — an embedded Cedar evaluator (the pure-Go path) and
an OPA-over-HTTP adapter. If OLIVARES_PDP_ENGINE selects an engine but its config is
invalid (an unreadable Cedar file, a malformed OPA target), the engine disables only
the external PDP, keeps the native ABAC engine and RBAC enforcing, and logs loudly.
A broken policy file never leaves requests un-governed and never crashes the plane.
For the deny-by-default model, see governance.
Audit signing key
The audit ledger is append-only, hash-chained, and anchored by Ed25519-signed checkpoints. The per-event signing key is resolved at boot, fail-closed for every custodied source.
| Variable | Effect |
|---|---|
OLIVARES_AUDIT_SIGNING_KEY | Customer-provisioned signing key, base64, inline. |
OLIVARES_AUDIT_SIGNING_KEY_FILE | Path to a mounted secret holding the key (preferred — the value never enters the process environment). |
OLIVARES_KEY_CUSTODY | Declared custody posture (byok or cmek). A boot whose actual key custody doesn’t match the declared one is refused. |
With none of these set, the key is minted on first boot in the data directory —
the honest single-node / development fallback. Sharing one key across replicas (via
the env value or a mounted secret) is required for high availability: otherwise each
node mints its own and the ledger forks at failover. Per-event signing always stays
on-box. KMS-wrapped (customer-managed-key) custody is an additional posture configured
through OLIVARES_KEY_WRAP; see the CLI reference.
Store selection
The engine selects its store from --engine.
| Engine | When to use | Notes |
|---|---|---|
sqlite (default) | Single binary, single node, air-gapped installs. | Pure-Go embedded store, zero external dependencies. With no --dsn, the store file lives in the data directory. |
postgres | Multi-tenant and scale-out deployments. | Adds row-level-security tenant isolation. Requires a least-privilege application role. |
SQLite is the default and needs no external service — it’s the air-gap-ready, zero-dependency store for the single-node topology, and the one the one-command Docker Compose deployment runs. Move to Postgres when you need multi-tenant isolation or horizontal scale, not before.
Choosing postgres opts into the row-level-security backstop that isolates tenants.
The engine refuses to start against a Postgres superuser or BYPASSRLS role —
which would disable that backstop — unless --allow-privileged-db-role explicitly
overrides the guard (single-tenant / throwaway only). For full cross-tenant System
reads (org listing, multi-tenant checkpoint coverage) provide a dedicated
NOSUPERUSER BYPASSRLS admin role via --admin-dsn; without it those reads run
RLS-limited and may return empty. OLIVARES_DB_MAX_CONNS bounds the per-node
application pool.
Tenancy and residency
A single instance is multi-tenant by construction on Postgres, with row-level security
isolating each tenant’s data. Data residency is layered on top by --region.
- Single-region (default, no
--region): no residency enforcement. - Region-scoped (
--region eu,--region us, …): the instance serves only tenants pinned to its home region and denies cross-region access fail-closed.--known-regionslists the region codes valid across the whole deployment; a tenant’s pin must be one of them, and a malformed region config fails the boot before the store opens.
Audit checkpoints
--checkpoint-interval controls how often a signed checkpoint is written over every
tenant chain (default 1h; 0 disables). A final checkpoint is written at clean
shutdown before the store closes, so the chain is anchored on shutdown as well as on
the interval. See verify a release for how the signed
chain is verified downstream.
Secure defaults
These postures are in effect with no configuration beyond serve. They are the
product’s default stance, not optional hardening.
| Area | Default | What it means |
|---|---|---|
| Credentials | None shipped | No default username or password. On first boot with no users, the engine mints a single-use setup token and prints it to standard output only — never to the logs. |
| Transport | TLS on | HTTP and gRPC serve over TLS; a self-signed certificate is generated in the data directory if none is supplied, and its fingerprint is logged. |
| Bind address | Loopback | --listen and --grpc-listen default to 127.0.0.1. Off-host reachability is a deliberate operator decision. |
| Plaintext mode | Off | --insecure is the only way to serve plaintext, and the gRPC path fails closed. Localhost development only. |
| Demo seeding | Off | --seed-demo is off and refuses any non-loopback bind, because it mints a public-password demo administrator. |
| Telemetry home | Off | The engine does not phone home. Outbound connections exist only to the sources you configure — which is what makes an air-gapped control plane possible with zero egress. |
The loopback binds mean the engine isn’t reachable off-host until you change them.
When you publish it — say by mapping a host port in Docker Compose — TLS is already on
to protect it; don’t pair a published bind with --insecure. On a fresh install the
engine prints a FIRST-BOOT SETUP block to standard output with the one-time setup
token (read from the container logs under Compose); the administrator uses it to create
the first user, then authenticates.
For what the product observes, where it governs, and where coverage is tiered, read honesty and limits.