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.
1. Prerequisites
Section titled “1. Prerequisites”- The
config.svcbinary on your$PATH(Linux or macOS). curlandjqfor poking around (or your language’s stdlib HTTP client).- A working directory you can write to (
/tmp/config-devbelow).
2. Generate a minimal bootstrap
Section titled “2. Generate a minimal bootstrap”The binary ships templates. Generate a dev bootstrap and a matching API-key file:
mkdir -p /tmp/config-dev/{cluster,tenant}cd /tmp/config-dev
config.svc generate bootstrap-dev > config.yamlconfig.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-devsid: dev-local # this instance's ID
host: "" # bind all interfacesport: "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.yaml3. Run
Section titled “3. Run”config.svc -f config.yaml serverYou should see log lines ending with:
INF config subsystems wired; reconcilers runningconfig service started at :7123In another shell, sanity-check:
curl -s http://localhost:7123/health # → {"status":"ok"}curl -s http://localhost:7123/version # → JSON build info4. Your first authenticated call
Section titled “4. Your first authenticated call”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"]}5. Write some config
Section titled “5. Write some config”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:
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:
mkdir -p /tmp/config-dev/storecat > /tmp/config-dev/store/config.yaml <<'YAML'hives: tenant: - path: acme:forge:prod:acme-prod:forge-meta log_level: debugYAMLSplit 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:
kis config write tenant/acme/forge/prod/acme-prod/forge-meta --file new.yaml6. Read the resolved snapshot
Section titled “6. Read the resolved snapshot”curl -s -H "X-Api-Key: $KEY" \ "$BASE/hives/tenant/acme/forge/prod/acme-prod/forge-meta" | jqThe response includes value, version, provenance (which source contributed each key), and resolved_at. For the full response shape, see the HTTP API.
7. Subscribe to changes (SSE)
Section titled “7. Subscribe to changes (SSE)”In your application, you typically subscribe once and react to changes as they arrive. Test the stream with curl:
curl -N -H "X-Api-Key: $KEY" "$BASE/stream/config?hives=tenant"You’ll see:
:connected
event: snapshotid: 1data: {"hive":"tenant","path":[...],"value":{...},"version":1,...}
event: heartbeatdata: {"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:
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.
8. Cleanup
Section titled “8. Cleanup”# Stop the server (Ctrl-C in the server shell), then:rm -rf /tmp/config-devThe bbolt files under /tmp/config-dev/ hold all state; source content lives under cluster/ and tenant/. Deleting the directory wipes everything.
9. Common gotchas
Section titled “9. Common gotchas”| Symptom | Cause / fix |
|---|---|
curl: (7) Failed to connect to localhost:7123 | Server didn’t start. Re-check the server’s stderr — startup errors log a clear reason. |
401 Unauthorized | Header is X-Api-Key: (not Authorization:); make sure you exported the key from the apikeys.yaml generation. |
403 Forbidden | Authenticated, but the API key’s role doesn’t permit this action on this hive. |
409 Conflict on PUT | Optimistic 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 Entity | Body failed CUE schema validation. The response body lists field-level errors. |
SSE shows snapshots only on writes through PUT, not on direct file edits | Add a read-only fs source alongside the writable localdir store for fsnotify-watched live reload. |
Where to go next
Section titled “Where to go next”- 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.