GET/PUT /config/settings (1.5.0 full-config coverage): the API-settable single-value config
overlay (root section) and, on a PUT, the apply metadata. settings is the CURRENT effective
root override (the merge of prior overlay + this request). It is overlay-persisted so it survives
a restart. 1.5.3: a MUTABLE config always has a writable config.overlay backend (the boot
invariant), so a successful PUT is ALWAYS durable; a LOCKED config (config.locked: true) refuses
the PUT (400) instead of applying it in memory only; the silent-loss outcome is gone.
reload_to_apply names the fields whose new value is DURABLY STORED but not yet LIVE: the
process-level binds (listen/admin_listen socket, tls/admin_tls bind, and the
admin_require_mtls boot-guard) are read once at process start, and the durable store backend
is reused across a hot reload; none can hot-swap, so they take effect on the next RESTART (or a
supervisor restart), NEVER on a
POST /config/reload: a reload re-reads disk and rebuilds the App but does not rebind sockets,
rebuild the TLS acceptor, or re-open the store. It is always EMPTY when nothing was durably stored
(no overlay); note names the affected fields instead. Everything else
(rate_card/per_request_fee/security/health/routing) is LIVE on the swap;
limits is live EXCEPT four boot-scoped fields (see reload_to_apply_fields):
upstream_request_timeout_secs/pool_max_idle_per_host/pool_idle_timeout_secs, which the
reused UpstreamClients only reads once at boot, and max_inbound_concurrent, which is baked
once into the data router's GlobalConcurrencyLimitLayer at process start (a config apply swaps
only Arc<App>, never the router): two independent freezing mechanisms. There is NO
observability section here, and no metrics one either: 1.5.3 DELETED both from the config
grammar, and RootSettings (what this endpoint projects) carries neither field: a PUT naming
observability is a loud 400 (deny_unknown_fields), never a silent no-op. All telemetry
egress is now export:, a NAMED MAP of exporter instances that this endpoint does not reach at
all: it is edited in config.yaml and made live by a plugin reload, not by PUT /config/settings.
Each export: entry is keyed by an operator-chosen instance name and carries a module: naming
the exporter plus a settings: bag that module validates, and MAY carry a streams:
subscription list. The built-in modules are prometheus (carries the metrics stream), otlp
(traces), and request-log-webhook + request-log-file (logs); subscribing an instance to a
stream its module does not carry is rejected rather than silently delivering nothing. An entry
MAY also carry a fields: projection, but do NOT plan on it in 1.5.3: it is parsed and enforced
yet unreachable with every built-in module, because each stream they carry has a pinned field
that has no producer yet, so any fields: on them is rejected. Omit it and receive the stream's
produced default set.
advanced is live EXCEPT response_headers: response_headers.server_timing is
baked into router middleware state at boot (same "config apply swaps Arc<App>, never the
router" freezing as max_inbound_concurrent) and response_headers.route_policy seeds a
process-global OnceLock; neither is rebuilt by an apply.