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
| Seam | Implementation | Why |
|---|---|---|
| Store | memory (CNS_STORE_IMPL=memory) | Real, working Append/Query/SetTTL — unlike noop, which errors on every call — with zero persistence and zero external dependency. |
| Secrets | memory (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. |
| Realtime | noop (unset) | Not exercised by the fixture — see the caution on GET /v1/stream in Realtime & streaming. |
| Identity | sso-jwt via a fixed local HMAC secret | A 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
- Builds and launches
cns-server, backgrounded within the script (so the script's own subsequent steps can reach it over HTTP), thenwaits 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. - Polls
/healthzuntil the server is ready. - Mints an HS256 bearer token (
scripts/mint-fixture-token.go) for a fixed sample identity — subjectsub_dev_fixture, tenantkatyayani(both literal, hardcoded values in the script, not configurable via the usualCNS_DEFAULT_TENANT_IDoverride) — and writes it to the file named by$TOKEN_FILE. - Confirms the bootstrap
platform-operatorgrant resolved correctly viaGET /admin/v1/me. - Grants that same subject an explicit
tenant-adminrole viaPOST /admin/v1/rbac/roles— required becauseGET /admin/v1/activityandGET /admin/v1/auditare PII-gated:platform-operatoralone gets403on those two (see Authz planes). - 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
| Variable | Default |
|---|---|
CNS_SSO_JWT_HMAC_SECRET | a fixed local-only dev string |
FIXTURE_SUBJECT_ID | sub_dev_fixture |
FIXTURE_EMAIL | dev-fixture@cns.local |
CNS_HTTP_ADDR | 127.0.0.1:8098 |
TOKEN_FILE | a 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
- The dev-fixture config guard in
internal/config.Load(see Profiles & adapter selection) —CNS_DEV_FIXTURE=1makesLoadrefuse to boot unless the listen address is loopback,CNS_ENV=dev, store/secrets are bothmemory, and realtime isnoop. CNS_DEV_FIXED_SUBJECT_ID— normally, identity-mapping mints a fresh ULIDsubject_idthe 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 setCNS_ADMIN_BOOTSTRAP_SUBJECTto 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 samesubject_id.