Skip to main content

The dev fixture

scripts/dev-fixture.sh boots a live cns-server with no production credentials — no AWS, no ClickHouse, no Centrifugo — so you (or a CI browser test) can exercise a real, running instance. Quickstart uses it as the fastest path to a first event.

What it configures

SeamImplementationWhy
Storememory (CNS_STORE_IMPL=memory)Real, working Append/Query/SetTTL — unlike noop, which errors on every call — with zero persistence and zero external dependency.
Secretsmemory (CNS_SECRETS_IMPL=memory)Required, not optional. Unsetting AWS_* alone is not enough — the ssm adapter's AWS SDK still resolves ambient credentials (~/.aws, IMDS) even with no explicit env vars. The in-process keystore makes this path safe to exercise, including app-register/key-rotation flows.
Realtimenoop (unset)Not exercised by the fixture — see the caution on GET /v1/stream in Realtime & streaming.
Identitysso-jwt via a fixed local HMAC secretA real, working identity adapter, but never a real IdP.

It also explicitly unsets any AWS_*/ClickHouse/Centrifugo variables sitting in your shell before boot, so the fixture can never accidentally pick up real credentials.

What it does, step by step

  1. Builds and launches cns-server, backgrounded within the script (so the script's own subsequent steps can reach it over HTTP), then waits on it — so the script occupies your terminal's foreground for as long as the server should run. Run the script itself in the background (not the built binary directly) if you want the server to persist independently.
  2. Polls /healthz until the server is ready.
  3. Mints an HS256 bearer token (scripts/mint-fixture-token.go) for a fixed sample identity — subject sub_dev_fixture, tenant katyayani (both literal, hardcoded values in the script, not configurable via the usual CNS_DEFAULT_TENANT_ID override) — and writes it to the file named by $TOKEN_FILE.
  4. Confirms the bootstrap platform-operator grant resolved correctly via GET /admin/v1/me.
  5. Grants that same subject an explicit tenant-admin role via POST /admin/v1/rbac/roles — required because GET /admin/v1/activity and GET /admin/v1/audit are PII-gated: platform-operator alone gets 403 on those two (see Authz planes).
  6. Seeds three synthetic events (CNS_DEV_SEED_SUBJECT) so the activity timeline isn't empty on a fresh boot.

A trap stops cns-server on the script's exit/INT/TERM.

Overridable variables

VariableDefault
CNS_SSO_JWT_HMAC_SECRETa fixed local-only dev string
FIXTURE_SUBJECT_IDsub_dev_fixture
FIXTURE_EMAILdev-fixture@cns.local
CNS_HTTP_ADDR127.0.0.1:8098
TOKEN_FILEa path under the repo's local scratch area — the script prints the exact path it used

:::note The sample tenant id is not configurable via these overrides The script's mint step hardcodes -tenant katyayani as a literal argument (not interpolated from CNS_DEFAULT_TENANT_ID), and separately exports CNS_DEFAULT_TENANT_ID=katyayani for the server's own tenant allow-list. If you want a fixture running under a different sample tenant id, edit the script directly — there's no supported override today. :::

Two safety mechanisms working together

  1. The dev-fixture config guard in internal/config.Load (see Profiles & adapter selection) — CNS_DEV_FIXTURE=1 makes Load refuse to boot unless the listen address is loopback, CNS_ENV=dev, store/secrets are both memory, and realtime is noop.
  2. CNS_DEV_FIXED_SUBJECT_ID — normally, identity-mapping mints a fresh ULID subject_id the first time it sees a new email/phone, which a script can't predict ahead of time. Setting this pins resolution to one known literal string, which is what lets the script set CNS_ADMIN_BOOTSTRAP_SUBJECT to a value guaranteed to match the token it's about to mint — solving a real chicken-and-egg problem (the bootstrap subject must be known before boot; the minted subject_id is normally only known after). Never set this outside a fixture — every distinct real identity would collapse onto the same subject_id.