Settings reference — the per-key dictionary
Included from Basic tier
This page lists every key the Go server reads, per authority, with its
type, units, default, permitted values, precedence and whether a change is
live or needs a restart. The configuration model (documents, versions,
convergence) is opschain docs settings; the config.yaml key catalog is
opschain docs config and is not restated here.
Liveness vocabulary used throughout:
- live (≤1 min) — read through the authentication stack's TTL snapshot of the stored global settings; an edit bites within a minute, no restart.
- live (per read) — read from the database on each request, run creation or sweep; an edit bites on the next read.
- restart — config.yaml, keys.env and the process environment are read
once by
opschain server start(oropschain runner); changing them requires restarting that process.
Stored global settings (the SystemConfiguration document)
Edited with opschain admin settings show | prune and PATCH /api/settings/{id}; every key not listed here (and not a kept
git_remote.periodic_fetch_interval key) is legacy and pruneable
(opschain docs cli/admin-settings-prune).
Identity and tokens — live (≤1 min)
| Key | Type / units | Default | Permitted values | Read for |
|---|---|---|---|---|
enable_basic_auth | bool | true | true/false | Whether HTTP basic credentials are accepted (token auth is unaffected). Overridden by config.yaml enable_basic_auth, which OPSCHAIN_ENABLE_BASIC_AUTH beats. |
token.secret_key | string, secret | — | non-empty | Still resolved and required at boot — OPSCHAIN_TOKEN_SECRET_KEY (environment, then keys.env) wins over the stored value, a disagreement is announced at boot — but the v1 JWT lane it signed is retired: no token is minted or verified against it any more. Native session tokens sign on a dedicated stored key (auth_signing_keys). |
token.access_expiry | int, minutes | 15 (the native session lane's default; the pre-cutover 4 h default retired with the v1 token routes) | > 0 (0/absent = default) | Access-token lifetime on the v2 session lane; a stored value overrides the 15-minute default, clamped to the session's absolute expiry. |
token.refresh_expiry | int, minutes | 10080 (7 d) | > 0 (0/absent = default) | How far each refresh rotation extends the session's refresh token, clamped to the session's absolute expiry. |
token.session_absolute_expiry | int, minutes | 43200 (30 d) | > 0 (0/absent = default) | The absolute session bound: refresh rotation never extends a session — or an access token minted on it — past login time plus this. |
token.enable_cookies | bool | true | true/false | Whether the v2 AuthService (and the federated sign-in callback) may hand a browser its refresh token in the HttpOnly opschain_refresh cookie rather than the response body. |
Scalars tolerate both JSON types and env-seeded strings (240, "240",
false, "false").
The product_name setting is retired: the product name is fixed
(OpsChain). New session tokens stamp the fixed iss claim, and token
verification never reads iss (aud is the fixed audience vocabulary —
api, admin, … — not derived from the product name), so tokens minted
under a previously customised value keep validating. A stored key is
ignored and pruneable (opschain admin settings prune).
The retired ldap.* subtree
The ldap.* settings (the stored subtree, the config.yaml ldap: block
and the OPSCHAIN_LDAP_* environment overlay) were removed completely: a directory is a kind: ldap identity
provider connection — per-instance configuration, an encrypted bind
credential, a test verb and per-connection health (opschain docs identity). On the first boot after the upgrade, any remaining ldap.*
values are resolved one last time (stored < file < env, bind password
ladder included) and seeded into that connection, and the stored block is
stripped; the retired auth_strategy/auth.providers chain selectors are
read only by that seed (to carry over whether the directory lane was
enabled) and are pruneable afterwards (opschain admin settings prune
strips them). They no longer order the sign-in surface either: the login
page renders enabled connections by the per-connection display_order
(ascending, key ascending login-surface amendment), edited
through the IdentityProviderService admin verbs beside display_name,
and the identity settings section no longer surfaces the selectors.
Instance facts — live (per read)
| Key | Type / units | Default | Permitted values | Read for |
|---|---|---|---|---|
maintenance_mode | string enum | disabled (off) | disabled | enabled | superuser_override; a legacy boolean is tolerated; anything but absent/null/disabled reads as on | Display only: the /api/info maintenance_mode? attribute and the v2 GetInfo boot read. The Go server enforces nothing from it (opschain docs maintenance-windows). Not pruneable: pruning would silently clear the displayed flag. |
banners | JSON array | [] | served verbatim | /api/info banners / v2 GetInfo banners_json — the GUI's banner strip. |
instance_info | JSON object | {} | served verbatim | /api/info instance_info / v2 GetInfo instance_info_json — free-form instance labelling. |
licence | string | — | the uploaded legacy licence file | /api/info licence_info. Deliberately never pruneable. |
chains_max_window_days | int, days | 90 | > 0 (≤ 0/absent = default) | The chain explorer's maximum index window; a wider since/filter window is refused with a named error. |
repo_folder | string | .opschain | a folder name inside the template repository | Roots the git-committed properties/workflows walk and the runner's repo layer. Node-level settings documents override it leaf-most; change-level overrides are a recorded gap. |
git_remote.periodic_fetch_interval | int, seconds | 900 | > 0 | The periodic-fetch interval a project git remote inherits when it does not set its own (meta.default_periodic_fetch_interval); a project settings document overrides the global value. |
Notification retention — live (per hourly sweep; typed write)
| Key | Type / units | Default | Permitted values | Read for |
|---|---|---|---|---|
notifications.feed_retention_days | int, days | 90 | 1–3650, clamped on read; tunable on the Enterprise tier only | Age at which a notification is archived out of every feed (it survives as delivery evidence). |
notifications.evidence_retention_days | int, days | 400 | 1–3650, and never below the feed value (clamped up); Enterprise only | Age at which the row is deleted outright. |
The tier split: Basic is fixed at 30/90 — the stored keys are ignored.
The typed write path (the notification governance admin surface) refuses
evidence < feed and out-of-range values up front; the sweeper re-clamps
defensively against hand-edited documents.
Agent governance registry — live (per read; typed write)
Two policy keys are registry-held settings: they live in the
agent-release registry, not in config.yaml and not in any settings
document, and are written only through their typed admin verbs
(AgentReleaseService.SetAgentReleaseStages / RenameAgentReleaseStage,
EnvironmentService.UpdateEnvironment) so they are validated by name on
every change and audited. Neither key is accepted in config.yaml or a
PATCHed settings document. Both are read into the compiled trust snapshot,
so a decision never reads the database.
| Key | Type / units | Default | Permitted values | Read for |
|---|---|---|---|---|
agents.release_stages | ordered list of {name, governed} | [{dev,false},{test,false},{prod,true}] | ≥ 2 rungs; names lowercase alphanumeric with -/_, unique, never retired; a change that removes or reorders an occupied rung, or strands a floor or grant condition, is refused by name | The estate's release stage ladder: promotion order, which rungs are funnel-held, and the ranks every floor and grant condition compare by (opschain admin agent-release stages, rename-stage; opschain docs agent-releases). |
agents.release_stage_floor | string, per environment | — (no floor) | a rung of agents.release_stages; "" clears | The release stage an agent's enrolled release must hold to act under that environment; an agent without a release is refused while a floor is set (opschain nodes update <env> --release-stage-floor; opschain docs resources/environment). |
Node-level settings (any node's document, converged)
Resolved down the target path at a frozen moment (enqueue or run
creation) — live per new run; running work keeps the values it was frozen
with. Full semantics in opschain docs settings.
| Key | Type / units | Default | Permitted values | Resolution |
|---|---|---|---|---|
runner.group | string | — (unconstrained) | a registered runner-group name | Change metadata wins wholesale per field over node settings, leaf-most node wins; the global document is not consulted (the SystemConfiguration node is not an ancestor of any project). Frozen at enqueue. |
runner.selectors | map: string → string or list | — | registered attribute keys/values; refused at change create and settings PATCH otherwise | Same ladder; selector keys merge individually across layers. |
runner.execution_scope | string | — (derived: see opschain docs settings) | run | step; anything else refused at change create and settings PATCH; an invalid stored value is ignored with a notice | Same ladder. |
runner.max_parallel_steps | int | 1 (serial) | ≥ 1; a negative value is refused at change create, and a negative stored value is ignored with a notice at enqueue (0 = unset) | Same ladder; applies to run-scoped runs only, clamped by the runner's own declared capacity. |
orchestration.retries.attempts | int, total attempts | — (no automatic retries) | 1–100; an out-of-range layer is skipped with a notice | global → project → environment → asset, nearest layer winning per field, frozen at run creation; a step's own retries: is most specific. |
orchestration.retries.backoff | duration string (30s, 2m) | 0 | ≥ 0; a malformed layer is skipped with a notice | Same ladder. |
orchestration.fan_out_limit | int | 50 (engine default) | ≥ 1; out-of-range/malformed layer skipped with a notice | global → project → environment → asset, frozen at run creation; a for_each: expansion past it fails the step loudly, naming the supplying level. |
orchestration.change_depth_limit | int, chain hops | 5 (engine default) | ≥ 1; out-of-range/malformed layer skipped with a notice | global → project → environment → asset against the parent run's target path, read when the change: step is reached. |
repo_folder | string | the global value, else .opschain | a folder name | Leaf-most non-empty node value wins over the global. |
requires_approval_from | approval entries | — | retired and ignored | Never enforced by this server; stripped from every served layer. Convert stored entries to approval policies with opschain approval-policies convert-settings-approvers (see opschain docs settings). |
Note the asymmetry the ladders above spell out: the orchestration.*
subtree does read the global document as its base layer; the runner
block does not.
Operator environment variables
Server process (opschain server start) — restart to change
| Variable | Type / units | Effect |
|---|---|---|
OPSCHAIN_HOME | path | The OpsChain home (config, keys, blobs, AGE store). |
OPSCHAIN_TOKEN_SECRET_KEY | string, secret | The token secret (see token.secret_key — still required, though the v1 JWT lane it signed is retired). Environment wins over the same-named keys.env entry, which wins over the stored token.secret_key. |
OPSCHAIN_PRIMARY_KEY | string, secret | ActiveRecord-encryption primary key (keys.env twin) — decrypts encrypted stored documents. |
OPSCHAIN_KEY_DERIVATION_SALT | string, secret | ActiveRecord-encryption key-derivation salt (keys.env twin). |
OPSCHAIN_DETERMINISTIC_KEY | string, secret | Provisioned into keys.env for upstream parity; feeds only the deterministic encryption scheme, which no adopted column uses. |
OPSCHAIN_ENABLE_BASIC_AUTH | bool | Top of the basic-auth ladder: beats config.yaml enable_basic_auth, which beats the stored setting. An unparseable value is ignored. |
OPSCHAIN_LDAP_* (incl. _PASSWORD, _BASE_DN) | retired | Seed input only: read once by the upgrade's boot seed into the kind: ldap connection, then ignored — remove them. |
OPSCHAIN_BOOTSTRAP_ADMIN_PASSWORD | string, secret | First-boot admin password for unattended installs. Environment only — never a config.yaml key. |
OPSCHAIN_SERVER_ADMIN_TOKEN | string, secret | Static admin bearer guarding the admin service on local-lane compositions only (the --admin-token flag wins over it; default off). A full-auth server refuses it with the typed ERROR_REASON_ADMIN_TOKEN_RETIRED answer and prints a boot notice. |
OPSCHAIN_CLUSTER_NAME | string | The cluster name the server reports (v2 GetInfo, fleet surfaces). |
OPSCHAIN_MASTER_KEY | age identity, secret | The AGE secret-store master key — wins over the key file (which must be 0600) and the OS keychain. |
OPSCHAIN_TRANSPORTABLE_KEY_PATH | path | File holding the transportable encryption key, below config.yaml vault.transportable_key/transportable_key_path and above ~/.limepoint/localKey (ENVIRONMINT_KEY_PATH is its undocumented MintPress-interop fallback, only applicable to existing MintPress customers and those using the MintPress SDK or the packs:mintpress feature). |
OPSCHAIN_SKIP_MIGRATION_VERIFY | bool | Emergency escape hatch: 1/true reports boot-time migration-ledger findings loudly on stderr instead of refusing to boot. |
OPSCHAIN_VAULT_* | retired | Seed input only: read once by the upgrade's boot seed — each config.yaml vault: provider key overriding its env twin — into the SecretsProvider connection, then ignored with a boot notice; remove them. The runtime vault configuration (and the OPSCHAIN_VAULT_* set injected into action-step runners) comes from the enabled provider (opschain admin secrets providers). |
PGHOST / PGPORT / PGUSER / PGPASSWORD / PGDATABASE / PGSSLMODE | libpq standard | Fill in parameters absent from the store DSN, so database credentials stay out of config.yaml. |
keys.env ($OPSCHAIN_HOME/server/keys.env, 0600, written by opschain server configure) holds the four OPSCHAIN_* key-material entries; a
same-named process environment variable always wins over its keys.env
entry. Local mode (opschain … --local) reads neither: its SQLite
store encrypts sensitive step outputs and the property/settings layers
with its own generated keyset, $OPSCHAIN_HOME/local/keys.env
(OPSCHAIN_PRIMARY_KEY + OPSCHAIN_KEY_DERIVATION_SALT, created on
first use, 0600) — see opschain docs storage, "Local mode".
Runner daemon (opschain runner) — restart to change
| Variable | Type / units | Default | Effect |
|---|---|---|---|
OPSCHAIN_RUNNER_MAX_PARALLEL_STEPS | int | 0 (undeclared — follow the server's cap) | The runner's declared step capacity; the --max-parallel-steps flag wins; unset, non-numeric or < 1 resolves to undeclared. |
OPSCHAIN_RUNNER_POLL_TIMEOUT | duration string | 70s | Long-poll timeout for job acquisition; a malformed value falls back. |
OPSCHAIN_RUNNER_RENEW_INTERVAL | duration string | 60s | Lease renew cadence; a malformed value falls back. |
OPSCHAIN_RUNNER_ENV_PASSTHROUGH | comma-separated names | — | Host environment variables passed through to steps; the --env-passthrough flag wins. |
OPSCHAIN_MINTPRESS_SDK | path | discovery | Explicit path to the mintpress-sdk binary, overriding discovery (clean absence = no MintModel actions). |
CLI client
| Variable | Effect |
|---|---|
OPSCHAIN_SERVER | The server target when no --server flag or active context names one. |
OPSCHAIN_ADMIN_TOKEN | Admin bearer for opschain admin … surfaces (below the --auth-token flag and your stored login, above the token stored by opschain admin login). On a full-auth server it must carry a real credential — typically a service-account API key; the server's static admin token is refused there. |
OPSCHAIN_TTY | always/never forces or suppresses terminal rendering (otherwise real TTY detection; TERM=dumb forces plain). |
OPSCHAIN_HOME | As above — also the CLI's contexts/credentials home. |
Injected, not operator-set
These appear in step/runner environments because the server put them there;
setting them by hand does nothing useful and may break a step:
OPSCHAIN_API_URL, OPSCHAIN_STEP_TOKEN, OPSCHAIN_TRACE_ID,
OPSCHAIN_PROPERTIES, OPSCHAIN_EVENTS, OPSCHAIN_OUTPUT,
OPSCHAIN_OUTPUT_SENSITIVE, OPSCHAIN_DRY_RUN, OPSCHAIN_PACKS_DIR,
OPSCHAIN_GIT_ASKPASS_USER/_SECRET, OPSCHAIN_CONTEXT_SCHEMA_VERSION,
OPSCHAIN_WORKER_STEP_ID, the OPSCHAIN_MINTMODEL_* render-lane
variables, and the projected OPSCHAIN_VAULT_* set.
config.yaml
Deploy-authoritative, strict-parsed, read once at server start —
restart to change anything in it. The full key catalog with types and
defaults is opschain docs config; it also spells out exactly which keys
have flag twins (listen, store, tls_cert, tls_key) and which have
environment overrides (the ladders above).
Precedence ladders at a glance
| Concern | Ladder (lowest → highest) |
|---|---|
| Token secret | stored token.secret_key → keys.env OPSCHAIN_TOKEN_SECRET_KEY → environment OPSCHAIN_TOKEN_SECRET_KEY |
| Basic auth | stored enable_basic_auth → config.yaml enable_basic_auth → OPSCHAIN_ENABLE_BASIC_AUTH |
| Vault | the enabled SecretsProvider connection (the retired OPSCHAIN_VAULT_* → vault: ladder is seed input only) |
listen / store / TLS | config.yaml → flag |
runner placement | node settings (leaf-most; no global layer) → change metadata (per field) |
orchestration.* | engine default → global → project → environment → asset (→ the step's own declaration, for retries) |
repo_folder | compiled .opschain → global → node documents (leaf-most) |
| Agent release stage | the ladder agents.release_stages (registry) → the enrolment floor agents.release_stage (config.yaml) → the environment floor agents.release_stage_floor (registry, per environment) → the grant's release_stage condition — each a stricter gate, none overriding another |
An overlay never blanks a lower value — it wins only where it is set.
Known gaps (flagged for follow-up, deliberately not fixed here)
- The GUI settings JSON schemas are Rails-era:
they still offer pruned legacy keys (
pod_per_change_step,image_reuse,pod_templates, …) and know nothing of the current Go keys (orchestration.*,runner.execution_scope,runner.max_parallel_steps,notifications.*). The settings editor's suggestions therefore do not match this page. repo_folderoverride comments contradict the code andopschain docs settings:PGSource.GlobalRepoFolder's comment calls node/change-level overrides a recorded gap, but node-level overrides are applied (serve/entrygate.goresolveRepoFolder, leaf-most wins) — only the change-level override remains a real gap.
Related pages
opschain docs settings— the configuration model: documents, versions, convergence and inheritance.opschain docs config— the deploy-authoritative config.yaml catalog.opschain docs identity— the authentication stack these ladders configure.opschain docs cli/admin-settings-prune— stripping legacy stored keys.opschain docs fleet-queueandopschain docs dispatch— how therunnerblock places and pins work.