Skip to main content
Version: v5.6.1

config.yaml — the server configuration catalog

Included from Basic tier

$OPSCHAIN_HOME/server/config.yaml is the server's deploy-authoritative configuration file: the keys an operator (not the API) owns. It is written by opschain server configure, read once by opschain server start, and strict-parsed — an unknown or typo'd key refuses to start the server with an error naming the accepted keys, rather than being silently dropped. Overrides above the file are narrower than a blanket rule: exactly four keys have flag twins (--listen, --store, --tls-cert, --tls-key, each winning over its file key), and the environment wins over the file only where a named variable exists — OPSCHAIN_ENABLE_BASIC_AUTH over enable_basic_auth, and the keys.env secrets (which the same-named process environment beats). Everything else in this file has no flag or environment twin. See opschain docs settings-reference for the full per-key precedence ladders.

This page is the key dictionary. It is the documentation half of the same catalog that backs GET /api/admin/config (every key with its set/default state and effective value, secrets redacted), the GUI's Configuration screen, opschain server configure --template, and the config validator (opschain admin config validate / the API's validate endpoint, which reports exactly what server start would refuse, plus the resolved security-posture notice when the candidate is valid).

Authority: config.yaml vs stored settings​

OpsChain configuration has two authorities:

  • Deploy-authoritative — this file. Keys here are owned by whoever operates the host. They are never editable through the API; several (enable_basic_auth, security.posture) deliberately override or anchor what the database holds.
  • DB-authoritative — stored settings, edited via the API/GUI (see opschain docs settings). Where a key's default below reads (stored setting), the file key is an override layered over the stored value: stored < file < environment variable.

Secrets never belong in config.yaml where an environment alternative exists: the key-material set (OPSCHAIN_TOKEN_SECRET_KEY, OPSCHAIN_PRIMARY_KEY, OPSCHAIN_DETERMINISTIC_KEY, OPSCHAIN_KEY_DERIVATION_SALT) lives in $OPSCHAIN_HOME/server/keys.env (mode 0600), and database credentials belong there as PG* variables rather than inline in the store DSN. Keys marked secret below are redacted in the effective catalog view and in support bundles.

Top-level keys​

KeyTypeDefaultMeaning
listenstring127.0.0.1:8443API listen address (host:port; port 0 picks a free one).
peer_urlstring—Base URL peer server replicas reach THIS replica at (scheme://host:port — the pod/host address, never the load balancer; OPSCHAIN_PEER_URL wins). The replica-to-replica lane: step-log chunks a peer receives for an attempt this replica owns are forwarded here (one spool owner per step attempt, so a non-sticky load balancer never splits a step's log), cross-replica live log tails fetch in-flight step output from the owning replica, and cross-replica agent routing forwards a request for an agent connected here. Required on every replica of a multi-replica estate: unset, peers cannot forward this replica's attempts (uploads are refused retryably and warn:logs:ingest_forward_failed is recorded), peers' live tails of steps owned here converge at step seal, and peers answer 503 for agents connected here. Single-replica estates leave it unset.
server_namestring<hostname>This node's STABLE server name — the key of the per-node quiesce posture (opschain admin server node quiesce <name>), surviving restarts and binary rolls where the per-boot replica id cannot. OPSCHAIN_SERVER_NAME wins over the file. Set it explicitly wherever hostnames are not stable (Kubernetes Deployments).
sitestring—This node's site identity (e.g. dc1) — the same vocabulary as the reserved location runner-group attribute. Makes claiming site-preferring (a step demanding one location is claimed by that site's replicas first, with an any-site fallback so work never strands — see opschain docs dispatch), keys the site quiesce posture (opschain admin server site quiesce <site>) this node honours (held-at-boot included; the site-scoped offer gate refuses, rather than serves, when the posture cannot be read), and appears on opschain admin status. OPSCHAIN_SITE wins over the file. Single-site estates leave it unset; multi-site estates set it on every replica.
storestring<opschain home>/opschain.dbData store: a postgres:// DSN (parameters absent from the DSN fall back to the standard PGHOST/PGPORT/PGUSER/PGPASSWORD/PGDATABASE/PGSSLMODE environment variables, so credentials belong in keys.env, not here), or a SQLite database file path.
blob_rootstring<opschain home>/blobsBlobstore directory (the blobstore.kind: fs backend's root; unused with kind: s3).
tls_certstring—TLS certificate file — set with tls_key to serve HTTPS; unset means plain HTTP.
tls_keystring—TLS private key file.
enable_basic_authbooltrueWhether HTTP basic authentication is accepted (deploy-authoritative over the stored setting; OPSCHAIN_ENABLE_BASIC_AUTH wins).

blobstore: — the blob backend​

Which backend holds step-log segments, artifacts, pack bundles and retention archives — and what the /readyz blobstore check probes. Omit the block (or set kind: fs) for the local filesystem store under blob_root: the single-server default. Running more than one server replica requires kind: s3: a shared S3-compatible store every replica reaches. On cloud estates point every replica (every site) at ONE shared bucket and let the provider's durability and geo-redundancy do the replication; on-prem estates run a per-site S3-compatible store (MinIO is the reference) with the store's own site replication, and monitor its replication watermark. Credentials belong in keys.env (or the process environment) as OPSCHAIN_BLOBSTORE_ACCESS_KEY_ID / OPSCHAIN_BLOBSTORE_SECRET_ACCESS_KEY, which win over the file keys.

KeyTypeDefaultMeaning
blobstore.kindstringfsBlob backend: fs (local filesystem under blob_root) or s3 (shared S3-compatible store — required for more than one server replica).
blobstore.endpointstring—S3 endpoint URL (https://s3.<region>.amazonaws.com, or the MinIO/compatible server's URL). Required with kind: s3.
blobstore.bucketstring—Bucket holding the blobstore objects. Required with kind: s3.
blobstore.regionstringus-east-1SigV4 signing region.
blobstore.access_key_idstring—Access key id (OPSCHAIN_BLOBSTORE_ACCESS_KEY_ID wins over the file).
blobstore.secret_access_keystring secret—Secret access key (OPSCHAIN_BLOBSTORE_SECRET_ACCESS_KEY wins over the file; prefer that over writing it here).
blobstore.prefixstring—Key prefix namespacing this OpsChain instance inside a shared bucket.
blobstore.path_stylebooltruePath-style object addressing (<endpoint>/<bucket>/<key> — MinIO and most self-hosted stores). false addresses the bucket as a virtual host (AWS S3).

shutdown: — graceful-shutdown tuning​

What a rolling restart choreographs against: shutdown first flips /readyz to 503 (the drain signal), lets in-flight requests finish for the drain grace, unparks runner long-polls (runners re-poll and land on a peer), detaches run drives (runs stay durable and resume on the next boot), and abandons whatever still runs when the overall timeout expires. Size the platform's termination grace (for example the pod's terminationGracePeriodSeconds) above timeout_seconds plus any preStop sleep.

KeyTypeDefaultMeaning
shutdown.drain_grace_secondsint5How long shutdown lets in-flight requests finish before parked runner long-polls are unparked — the load balancer's drain-observation window.
shutdown.timeout_secondsint60Overall graceful-shutdown budget: work still running after this many seconds is abandoned (run drives stay durable and resume on the next boot).

ldap: — RETIRED​

The ldap: block and its OPSCHAIN_LDAP_* environment overlay were removed completely: a directory is a kind: ldap identity provider connection managed through the IdentityProviderService admin verbs — per-instance configuration, an encrypted bind credential, a test verb, per-connection health and a sweep verb (opschain docs identity). An existing block is still parsed for one purpose: on the first boot after the upgrade it is consumed, with the stored ldap.* settings and the environment variables, into the seeded connection — after which the boot notice tells you to delete it.

vault: — platform crypto only (the provider keys are retired)​

The vault: block's provider keys are removed completely: type, address, auth_method, token, username, password, secret_id, mount_path, password_length, cipher_stacking and client_options have no runtime consumer. The secrets backend is a SecretsProvider connection managed through the API/GUI (SecretsProviderService, opschain admin secrets providers): per-instance configuration with an encrypted write-only credential, a test verb, per-connection health, audited writes, and the single-enabled default (opschain docs cli/admin-secrets-providers).

On the first boot after the upgrade, an existing block (and any OPSCHAIN_VAULT_* in the server's process environment, each file key overriding its env twin) is consumed ONCE to seed that connection (code vault, credential encrypted at rest, kind recorded from the old vault.type — inference included, so it retires with the key). After the seed, a config.yaml still carrying provider keys refuses to start with an error naming them — delete them. Lingering OPSCHAIN_VAULT_* server variables only draw a boot notice. With no provider registered the local AGE dev store resolves references best-effort (the old no-block default).

What stays in the block is the platform crypto material — it keys the server's secret codec for {AES2}/T cipher-stacked values and was never a connection property:

KeyTypeDefaultMeaning
vault.transportable_keystring, secret—Transportable encryption key inline (server-side only; overrides transportable_key_path).
vault.transportable_key_pathstringOPSCHAIN_TRANSPORTABLE_KEY_PATH, then ~/.limepoint/localKeyFile holding the transportable key (server-side only).

Any extra set key still renders in the effective catalog as "set but not in the documented catalog" (secret-named ones redacted), so nothing stored is invisible. One long-retired key keeps its own refusal: vault.encrypt_secrets was renamed to vault.cipher_stacking (v5.0.0, no alias) and fails strict parsing with the migration hint.

metrics: — the Prometheus endpoint​

KeyTypeDefaultMeaning
metrics.enabledbooltrueMount GET /metrics.
metrics.require_tokenbooltrueRequire admin/bearer authorisation on scrapes.

gui: — the embedded GUI​

The binary embeds the GUI (a vendored snapshot of the sibling GUI repository, provenance reported on GET /api/admin/status and the overview) and serves it at the root origin: the app at /, the API under /api on the same port. The API, health (/healthz, /readyz), metrics and RPC paths always take precedence; any other unknown path serves the app so client-side routes deep-link. Default on in every posture; disable it only where the GUI is fronted separately — unknown root paths then 404 as before.

KeyTypeDefaultMeaning
gui.enabledbooltrueServe the embedded GUI at /.

api: — v2 API rate limits​

The api.rate_limit block tunes the built-in per-principal rate limits on the v2 surface (/api/v2): a per-principal request budget (token bucket; anonymous callers are keyed by client address) and a cap on concurrent server streams (FollowLogs, WatchEvents). An absent block means enforced with the generous defaults below. Every v2 response carries the standard RateLimit-Limit/RateLimit-Remaining/ RateLimit-Reset headers; a refusal answers the typed resource_exhausted (ERROR_REASON_RATE_LIMITED) with Retry-After. Budgets are per server process — under HA each replica grants each principal its own budget. A deployment whose API gateway owns rate limiting sets enabled: false (or individual keys to 0) — effectively-off, per the gateway contract.

KeyTypeDefaultMeaning
api.rate_limit.enabledbooltrueEnforce the built-in v2 rate limits. false turns both limits off (and the headers with them).
api.rate_limit.requests_per_minuteint600Per-principal steady-state v2 request budget. 0 turns the request budget off.
api.rate_limit.burstint600Request-bucket capacity: how many requests a quiet principal may issue at once before the steady rate governs.
api.rate_limit.max_concurrent_streamsint25Concurrent v2 server streams one principal may hold open. 0 turns the cap off.
api.maintenance_badge_horizon_minutesint60How far ahead node reads badge a scheduled maintenance window covering the node (the imminent maintenance badge). 0 badges active windows only.

logging: — file logging and rotation​

KeyTypeDefaultMeaning
logging.filestring—Log file path; setting it engages file mode with built-in rotation.
logging.max_size_mbint100Rotate when the file exceeds this many MB.
logging.max_age_daysint30Delete rotated backups older than this many days.
logging.max_backupsint10Keep at most this many rotated backups.
logging.compressboolfalsegzip rotated backups.

retention: — age-out policy​

The retention job itself is documented at opschain docs retention; every *_days class defaults to 0 — never age out — except chat_transcripts_days, whose default is 365, and evidence_ledger_days, whose default is 730.

KeyTypeDefaultMeaning
retention.enabledbooltrue when the block is presentRun the retention job.
retention.interval_minutesint60Minutes between sweeps on the leader.
retention.batch_sizeint500Rows per delete batch.
retention.archive_dirstring—Retired: export-before-delete archives are sealed into the blobstore as verified tar objects (one per batch, re-read and verified before anything is deleted), so every replica can reach them. The key is accepted and ignored.
retention.step_logs_daysint0 (never)Age out step log segments.
retention.chat_transcripts_daysint365Age out chat and adapter transcript segments; 0 means never, and the default only applies while retention as a whole is enabled (an absent retention: block still disables retention entirely). Exchanges cited as transcript evidence on a legal-held ledger entry are exempt until unflagged.
retention.events_daysint0 (never)Age out events.
retention.step_transitions_daysint0 (never)Age out step transition audit rows.
retention.scheduler_done_daysint0 (never)Age out scheduler completion bookkeeping (no export).
retention.artifacts_daysint0 (never)Tombstone artifacts and release their blob bytes.
retention.recommendations_open_daysint0 (never)Expire agent recommendations still open after this many days.
retention.evidence_ledger_daysint730The evidence ledger's hot window: rows older than this are sealed into verified blob-store archive segments and only then deleted — never a bare purge, and the hash chain verifies across the boundary (opschain docs retention). 0 means never, the default only applies while retention as a whole is enabled, and legal-held rows are never archived out from under their hold.
artifacts.max_artifact_mbint512Per-artifact step-artifact collection budget in MB (total matched file bytes); an over-budget collection is refused (opschain docs artifacts).
artifacts.max_run_mbint1024Per-run step-artifact collection budget in MB; an over-budget collection is refused.

agents: — agent-governance knobs​

KeyTypeDefaultMeaning
agents.approvers[]string—Fallback usernames who approve SUPERVISED agent-created work.
agents.release_stagestring—Minimum AgentRelease promotion stage required at enrolment: a rung of the estate's stage ladder (dev, test or prod on the default ladder; opschain docs agent-releases). Empty requires none — a declared release still verifies. A value the ladder lacks refuses every declared enrolment by name.
agents.assignment_floorstringadviseWhat an assets-under-management assignment confers with no matching grant: advise or deny.
agents.owner_property_keystringmetadata.asset_ownerProperties key holding an asset's designated owner.

security: — the identity-security posture​

security.posture selects the baseline (evaluation or hardened); the other keys override individual controls, except the ⚓-anchored ones, which the hardened posture refuses to relax. Deploy-authoritative throughout — never editable through the API. See opschain docs identity.

KeyTypeDefaultMeaning
security.posturestringevaluationIdentity-security posture: evaluation or hardened (deploy-authoritative, never editable through the API).
security.require_bootstrap_passwordboolhardened true, evaluation false⚓ anchored in hardened: first boot requires OPSCHAIN_BOOTSTRAP_ADMIN_PASSWORD and rotate-on-first-login.
security.local_user_modestringhardened break_glass_only, evaluation fullfull, or break_glass_only (refuse creating non-break-glass local users).
security.password_min_lengthinthardened 14, evaluation 0Minimum local password length.
security.bcrypt_costinthardened 12, evaluation 10bcrypt work factor for local passwords (4-18).
security.password_historyinthardened 5, evaluation 0Break-glass accounts refuse reusing their last N passwords.
security.password_max_age_daysint0 (off)Force rotation of local passwords older than N days.
security.break_glass_containmentboolhardened true, evaluation falseBreak-glass logins get 1h access tokens, no refresh token, no api_key minting.
security.max_break_glass_accountsinthardened 2, evaluation 0 (unlimited)Refuse creating/designating break-glass accounts beyond this count.
security.collision_defenceboolhardened true, evaluation falseRefuse local usernames shadowing directory accounts; warn on provider-collision logins.
security.directory_reconciliationboolhardened true, evaluation falsePeriodically disable directory identities whose entry the directory no longer returns (per ldap-kind connection, keyed by entry id), revoking their tokens and sessions.
security.directory_reconciliation_hoursint24Reconciliation cadence in hours.

auth: — network controls​

KeyTypeDefaultMeaning
auth.break_glass_networks[]string(any source)CIDR allowlist: break-glass logins from outside it are refused.

server: — reverse-proxy trust​

The client address OpsChain records about a request — in the request log's remote_ip, the per-source login throttle, the session row's source_address/last_client_address and the audit trail's actor object (opschain docs events) — is the TCP peer unless that peer is a trusted proxy. Behind a load balancer, an ingress or Cloudflare the peer is the proxy, so list the proxies' CIDRs here and the server resolves the real client from the headers only a listed peer is allowed to set: when the peer is listed, the client is CF-Connecting-IP when present, otherwise the right-most X-Forwarded-For hop that is not itself listed (the standard right-to-left walk — every hop left of the first untrusted one is the client's own claim and ignored). An unlisted peer's forwarding headers are ignored entirely, so a direct caller can never spoof its recorded address. An entry that is not a valid CIDR refuses to start the server by name (server.trusted_proxies: "x" is not a valid CIDR), and the validator reports the same.

KeyTypeDefaultMeaning
server.trusted_proxies[]string(none — the TCP peer is the client)CIDRs of the reverse proxies whose X-Forwarded-For / CF-Connecting-IP the server honours when resolving a request's client address.

How long the recorded origin is kept: events age out under retention.events_days (default never), sessions live until they are purged, and the request log under logging.max_age_days — see opschain docs security-audit, "What the audit trail holds about origin".

eula: — first-start acceptance​

KeyTypeDefaultMeaning
eula.acceptedboolfalseAccept the LimePoint EULA on a fresh installation's first start (the config twin of server start --confirm-eula; read it with opschain eula). Inert once the acceptance is recorded.

database: — scheduled database maintenance​

The database.pool block caps each server replica's PostgreSQL connections — three pools share one database, and the caps keep an N-replica estate inside the server's max_connections budget (the defaults total 25 per replica, so four replicas fit the PostgreSQL default of 100).

KeyTypeDefaultMeaning
database.pool.store_max_connsint10Connection cap for the primary store pool.
database.pool.api_max_connsint10Connection cap for the shared API/auth read pool.
database.pool.scheduler_max_connsint5Connection cap for the scheduler's pool.

The database.maintenance block tunes the dbmaint subsystem (opschain docs database-maintenance). Unlike retention:, an absent block means enabled with the defaults below; enabled: false is the explicit opt-out for estates where a DBA owns maintenance.

KeyTypeDefaultMeaning
database.maintenance.enabledbooltrueRun the scheduled database maintenance lanes (per-table ANALYZE daily and after retention sweeps; weekly VACUUM (ANALYZE) in the off-peak window; SQLite PRAGMA optimize). false hands maintenance to a DBA — status and on-demand runs stay available.
database.maintenance.analyze_interval_minutesint1440Minutes between scheduled ANALYZE passes on the leader.
database.maintenance.vacuum_windowstring02:00-04:00Server-local HH:MM-HH:MM window the weekly VACUUM (ANALYZE) may start in.
database.maintenance.vacuum_daystringsundayWeekday the VACUUM (ANALYZE) window applies on.
database.maintenance.tables[]string(the built-in churn set)Override the maintained table set (default: events, runs, steps, workflow steps, log tables, queue and bookkeeping tables).

Failure modes​

  • An unknown key refuses to start the server. The strict parser names the accepted keys at each level in its error, so a typo (metrics.enbled) fails loudly instead of leaving an ignored password behind an unfindable LDAP error 49.
  • A missing config.yaml is not an error — the server starts with every key at its default (zero config).
  • An invalid candidate never half-applies: the validator strict-parses and resolves the security posture without applying anything, so what it reports is exactly what server start would refuse with.
  • opschain docs settings — the DB-authoritative half: stored settings documents and their inheritance down the node tree.
  • opschain docs settings-reference — the per-key dictionary across all three authorities, with the full precedence ladders.
  • opschain docs retention, opschain docs database-maintenance, opschain docs identity, opschain docs storage — the subsystems these blocks tune.