Skip to content
Talk to our solutions team

Quickstart

Audience: application developers building a product on top of the config service. No source-code access required — five minutes from “downloaded the binary” to “subscribing to live config changes.”

For the comprehensive endpoint reference, see the HTTP API. This page gets you to your first authenticated API call as fast as possible; the reference is your daily-driver doc after that.

  • The config.svc binary on your $PATH (Linux or macOS).
  • curl and jq for poking around (or your language’s stdlib HTTP client).
  • A working directory you can write to (/tmp/config-dev below).

The binary ships templates. Generate a dev bootstrap and a matching API-key file:

Terminal window
mkdir -p /tmp/config-dev/{cluster,tenant}
cd /tmp/config-dev
config.svc generate bootstrap-dev > config.yaml
config.svc generate apikeys > apikeys.yaml
# Note the printed API key — you'll need it for every authenticated call.

The bootstrap that lands looks like this (annotated; for TLS, OpenTelemetry export, etc. see the comments in the generated file or the Operations guide):

service: config # operational identity (don't change for dev)
version: 0.0.0-dev
sid: dev-local # this instance's ID
host: "" # bind all interfaces
port: "7123"
zerotrust: false # plain HTTP — flip to true with TLS material in prod
log:
level: info
storage:
mode: bbolt # single-node; etcd for HA — see the deployment guides
bbolt:
path: /tmp/config-dev
hives: # one config tree per "hive"; defaults shipped
cluster:
sources:
# localdir = a WRITABLE, credential-free local store. Same on-disk format
# as fs and git: a tree of flat-`path` YAML (hives: <hive>: - path: ...)
# where folders and filenames carry no meaning. fs (read-only) and git
# (read-only via the API) are the other backends — a localdir folder can
# be pushed to git and served by either, unchanged.
- { name: local, backend: localdir, path: /tmp/config-dev/store,
owns: ["**"], priority: 0, writable: true }
tenant:
sources:
- { name: local, backend: localdir, path: /tmp/config-dev/store,
owns: ["**"], priority: 0, writable: true }
auth:
apikey_file: /tmp/config-dev/apikeys.yaml
Terminal window
config.svc -f config.yaml server

You should see log lines ending with:

INF config subsystems wired; reconcilers running
config service started at :7123

In another shell, sanity-check:

Terminal window
curl -s http://localhost:7123/health # → {"status":"ok"}
curl -s http://localhost:7123/version # → JSON build info
Terminal window
export KEY="...paste the key from the apikeys.yaml generation step..."
export BASE="http://localhost:7123"
curl -s -H "X-Api-Key: $KEY" "$BASE/hives" | jq
# → {"hives":["cluster","tenant"]}

Three ways to write a value into the tenant hive. Each ends up producing the same snapshot.

(a) REST API — your most common path from application code:

Terminal window
curl -s -X PUT \
-H "X-Api-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"log_level":"debug","features":{"fast_path":true}}' \
"$BASE/hives/tenant/acme/forge/prod/acme-prod/forge-meta" | jq

(b) Direct filesystem edit — the localdir store is a tree of flat-path YAML: every entry is addressed by its path: key under hives:, and the file it lives in is your choice (folders and filenames carry no meaning). Author it on disk in any *.yaml/*.yml file under the root:

Terminal window
mkdir -p /tmp/config-dev/store
cat > /tmp/config-dev/store/config.yaml <<'YAML'
hives:
tenant:
- path: acme:forge:prod:acme-prod:forge-meta
log_level: debug
YAML

Split entries across as many files and folders as you like — they all union into one source. Two entries with the same (path, match) anywhere in the root is a hard error (the source fails closed), so each entry lives in exactly one file.

This is the same format the read-only fs and git backends read, so a store you develop against locally can be committed and served from git unchanged.

localdir reflects its own API writes immediately; to pick up a manual file edit, restart the server (or re-PUT through the API). For live reload on external edits, add a read-only fs source (fsnotify-watched) alongside the writable store.

(c) kis config CLI — if you have the kis CLI installed, it wraps the REST API with login context:

Terminal window
kis config write tenant/acme/forge/prod/acme-prod/forge-meta --file new.yaml
Terminal window
curl -s -H "X-Api-Key: $KEY" \
"$BASE/hives/tenant/acme/forge/prod/acme-prod/forge-meta" | jq

The response includes value, version, provenance (which source contributed each key), and resolved_at. For the full response shape, see the HTTP API.

In your application, you typically subscribe once and react to changes as they arrive. Test the stream with curl:

Terminal window
curl -N -H "X-Api-Key: $KEY" "$BASE/stream/config?hives=tenant"

You’ll see:

:connected
event: snapshot
id: 1
data: {"hive":"tenant","path":[...],"value":{...},"version":1,...}
event: heartbeat
data: {"ts":"2026-05-21T..."}

Edit a value (any of the three write methods above) and watch the new event: snapshot arrive within a second.

To resume after a disconnect, replay the id: of the last event you saw via the Last-Event-Id header:

Terminal window
curl -N -H "X-Api-Key: $KEY" \
-H "Last-Event-Id: 42" \
"$BASE/stream/config?hives=tenant"

The server sends every snapshot with version > 42, then resumes live streaming.

Terminal window
# Stop the server (Ctrl-C in the server shell), then:
rm -rf /tmp/config-dev

The bbolt files under /tmp/config-dev/ hold all state; source content lives under cluster/ and tenant/. Deleting the directory wipes everything.

SymptomCause / fix
curl: (7) Failed to connect to localhost:7123Server didn’t start. Re-check the server’s stderr — startup errors log a clear reason.
401 UnauthorizedHeader is X-Api-Key: (not Authorization:); make sure you exported the key from the apikeys.yaml generation.
403 ForbiddenAuthenticated, but the API key’s role doesn’t permit this action on this hive.
409 Conflict on PUTOptimistic concurrency: send If-Match: "<version>" using the version from a prior write response to that path (the source version, e.g. "3") — not the GET snapshot ETag, which is the per-hive snapshot version (a different counter).
422 Unprocessable EntityBody failed CUE schema validation. The response body lists field-level errors.
SSE shows snapshots only on writes through PUT, not on direct file editsAdd a read-only fs source alongside the writable localdir store for fsnotify-watched live reload.
  • HTTP API — every endpoint, payload, and header.
  • Configuration files — bootstrap, source format, CUE schemas.
  • Your application code — integrate the REST + SSE calls into your product. Treat config snapshots as the authoritative source for whatever business config the platform owner is publishing for your tenant.