Skip to content
Talk to our solutions team

Config Overview

config.svc is two subsystems in one binary, sharing auth, transport, and audit but otherwise isolated:

  1. Configuration control plane — named, typed, CUE-validated configuration trees (“hives”) merged from external sources and pushed to subscribers over SSE.
  2. Service discovery — a TTL-leased service registry (register / heartbeat / deregister), with its own SSE membership stream.

The resolver model lives in a shared library imported by both the server and every service’s config client, so a value resolves identically on both sides.

Default listener: :7123. Bootstrap config: /etc/infinity-config/config.yaml.

A hive is a named configuration namespace with a fixed level structure. Two ship embedded (they are not operator-editable):

HiveLevelsNotes
clusterdc / cluster / service / instanceservice is predefined from the service catalog
tenantcustomer / product / environment / tenant / serviceenvironment is predefined: dev, uat, prod. Marked isolated — no single source may own the whole hive

Within a hive, values are addressed by path (colon-separated, e.g. acme:forge:prod:tenant-a:iam).

Sources of truth are external; the server merges them. Each source is declared per hive with an ownership glob and a priority:

BackendWritableWatch mechanism
fsnofilesystem notify
localdiryesfilesystem notify (dev/local writes)
gitno (server holds no push credentials)ref polling + webhook nudge
etcdyesnative watch
postgresyes (append-only history + revert)LISTEN/NOTIFY with poll fallback

Guard rails are fail-closed at boot: empty owns is rejected, writable: true on fs/git is rejected, and owns: ["**"] is rejected on the isolated tenant hive.

resolve(hive, path, instance-labels) walks the tree root → leaf, deep-merging every contributing node, on two axes:

  • Vertical — node inheritance: values set higher in the tree apply below; deeper wins.
  • Horizontal — at each node: source priority (low → high), then match specificity, then declaration order.

Merge rules: maps merge recursively; scalars and arrays replace wholesale; an explicit null deletes the key. Entries can carry a match: label set (e.g. ring: canary) and apply only to instances whose labels are a superset.

  • Reads/snapshots — a leader-elected reconciler merges sources → validates against the hive’s CUE schema → persists snapshots (bbolt single-node, etcd in HA). Subscribers receive snapshots over SSE (/stream/config) with Last-Event-Id resume.
  • Writes — any instance (no leader hop): authenticate → authorize (per-hive expression policy) → CUE-validate → synchronous audit → write to the highest-priority writable source. The reconciler notices the source change and re-snapshots. Audit-sink downtime fails writes closed (503).

Every chassis service selects its config mode with a nested config: block in its boot YAML:

config:
url: https://config.internal:7123 # remote mode: SSE-subscribed
apikey: ${CONFIG_APIKEY}
cache: /var/lib/mysvc/config.bbolt # optional last-known-good warm cache
  • config.url → remote mode (requires --datacenter/--cluster identity).
  • config.path → local mode: a folder of the same flat-path YAML documents.
  • Neither → bootstrap-as-config: the boot file’s own inline hives: block is the source.
  • url wins over path. The old flat config_url / config_apikey keys are not read.

The client blocks only on validation, loads in the background, retries forever with backoff, and serves the warm cache during outages. Reads are snapshot-consistent and lock-free; watches are coalesced, latest-wins callbacks. Tenant nodes are served for the keys listed under the service’s own tenants: value, and fetched lazily for anything else.

Continue with: