Skip to content

Overview

Configuration Reference

Format: HOCON (Human-Optimized Config Object Notation)

Settings are resolved in this order (highest to lowest):

  • CLI flags (-r, --readonly, --config, etc.)
  • System properties (-Dkey=value or -DKEY=VALUE)
  • SCRIBE_* env vars (SCRIBE_LDAP_URL, SCRIBE_DATABASE_PASSWORD, etc.)
  • Standard env vars (OTEL_*, POSTGRES_*, etc. where supported)
  • This config file (values defined below)
  • Built-in defaults

SCRIBE_* variables follow the config path with dots → underscores. Some frequently-tuned settings have shorter aliases.

Config pathEnvironment variable
log.levelSCRIBE_LOG_LEVEL
concurrencySCRIBE_CONCURRENCY
ldap.urlSCRIBE_LDAP_URL
ldap.max-size-limitSCRIBE_LDAP_MAX_SIZE_LIMIT
monitoring.hints.enabledSCRIBE_HINTS_ENABLED (alias)
database.urlSCRIBE_DATABASE_URL
database.passwordSCRIBE_DATABASE_PASSWORD
database.max-pool-sizeSCRIBE_DATABASE_MAX_POOL_SIZE
monitoring.prometheus.enabledSCRIBE_PROMETHEUS_ENABLED (alias)
monitoring.traces.enabledSCRIBE_TRACES_ENABLED
monitoring.traces.endpointOTEL_EXPORTER_OTLP_*, MONITORING_TRACING_OTLP_URL, SCRIBE_TRACES_ENDPOINT
monitoring.traces.headersOTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS, MONITORING_TRACING_HEADERS, SCRIBE_TRACES_HEADERS

The ${?VAR} syntax substitutes VAR if defined, otherwise keeps the previous value. When multiple assignments exist, the last one wins.

TypeExamples
Booleanstrue, false, yes, no, on, off
Durations500ms, 5s, 30m, 1h, 2d
Sizes1k, 2m, 3g (bytes)
Numbers42, 3.14 (no quotes)
Strings"quoted" or unquoted-simple

When ${?VAR} is used WITHOUT a preceding default, the key is undefined if VAR is not set. This allows runtime overrides to fill in values.

When a concrete default precedes ${?VAR}, that default “pins” the value:

key = "default" # Key is always defined
key = ${?SCRIBE_KEY} # Only overrides if SCRIBE_KEY is set

With -Dconfig.override_with_env_vars=true, any config key can be overridden using CONFIG_FORCE_* environment variables:

  • CONFIG_FORCE_a_b__c___d → config key a.b-c_d
  • Name mangling: _., __-, ____

The application searches for the config file in order:

  • CLI: --config /path/to/identity-scribe.conf
  • System property: -Dconfig.file=...
  • Env var: SCRIBE_CONFIG_FILE (preferred)
  • Env var: CONFIG_FILE (fallback)
  • CWD: identity-scribe.conf
  • CWD: config/identity-scribe.conf
  • Next to binary: ./identity-scribe.conf
  • Next to binary: ./config/identity-scribe.conf

Some sections support automatic inheritance:

PathInherits from
http.sockets.<name>http
ldap.ssl, http.sslssl
retry.servicesretry
auth.bearer.cacheauth.cache
auth.ropc.cacheauth.cache
auth.ldap.cacheauth.cache
channels.rest.uiopenapi.ui
monitoring.observe.uiopenapi.ui

For inheritable sections, just override what differs:

http.sockets.internal {
port = 9001
concurrency-limit.max-limit = 50
}
auth.cache.ttl = 10m # All auth caches inherit this
auth.ropc.cache.ttl = 5m # Override just ROPC
openapi.ui.theme = "moon" # All UIs inherit this

HOCON lets you customize defaults without copying them. Use these patterns to keep configs short and maintainable.

Add items after existing defaults:

# Single item
monitoring.log.rules += { action = include, name = "Audit.*" }
# Multiple items (use ${path} [...] pattern, not += [...])
monitoring.log.rules = ${monitoring.log.rules} [
{ action = include, name = "Debug.*" }
{ action = exclude, name = "Noisy.*", where = "duration.seconds<=10ms" }
]
# Works for any array
http.cors.allow-origins += "http://localhost:3000"
ssl.cipher-suites += "TLS_AES_256_GCM_SHA384"

Place items before defaults. For log rules, earlier items take priority:

# Your rule first, then defaults
monitoring.log.rules = [
{ action = include, name = "Critical.*" }
] ${monitoring.log.rules}
# Multiple prepended items
monitoring.log.rules = [
{ action = include, name = "Security.*" }
{ action = include, name = "Audit.*" }
] ${monitoring.log.rules}

Wrap defaults with items on both sides:

monitoring.log.rules = [
# High priority (evaluated first)
{ action = include, name = "Critical.*" }
{ action = include, name = "Security.*" }
] ${monitoring.log.rules} [
# Low priority (evaluated after defaults)
{ action = exclude, name = "Verbose.*", where = "duration.seconds<=5s" }
]

Result: your prepended rules, then defaults, then your appended rules.

Objects merge automatically. Specify only what changes:

# Change just the port; inherit host, ssl, cors, etc.
http.sockets.admin { port = 9001 }
# Override nested values without touching siblings
monitoring.log {
format = "json"
# sample-rate, rules, childs keep their defaults
}

Reference another config section and override specific values:

# Start with global SSL, change just the cert
channels.rest.ssl = ${ssl} { cert = "/path/to/rest.crt" }
# Inherit parent cache settings, extend TTL
auth.bearer.cache = ${auth.cache} { ttl = 10m }

The ? in ${?path} makes substitution optional—if the path doesn’t exist, the block is skipped rather than failing.

These settings cascade automatically. Override at any level:

Setting pathInherits from
http.sockets.<name>.sslhttp.sslssl
http.sockets.<name>.corshttp.cors
auth.bearer.cacheauth.cache
auth.ropc.cacheauth.cache
auth.ldapldap
channels.*.uiopenapi.ui
transcribes.<name>.ldapldap

Don’t copy entire arrays just to add items:

# BAD: duplicates 11 default rules, breaks on upgrades
monitoring.log.rules = [
{ action = exclude, name = "LDAP.*", ... }
{ action = exclude, where = "scribe.result=ok ..." }
# ... 9 more copied rules ...
{ action = include, name = "MyCustom.*" } # Only this is new
]
# GOOD: append what's new
monitoring.log.rules += { action = include, name = "MyCustom.*" }

Copied arrays miss new defaults on upgrade and bloat config files.