Skip to main content

Profiles & adapter selection

Recap: one deployment is one Profile — an implementation choice plus settings, per seam. internal/config.Load builds it from environment variables; there's no file-based config in Phase 1.

How cmd/cns-server picks an implementation

For each seam, main.go switches on cfg.Profile.<Seam>.Impl and constructs the matching adapter — falling back to that seam's noop implementation for any value it doesn't recognize (including the empty/default case), so the server always boots, regardless of how incomplete its configuration is. A freshly cloned repo with zero environment variables set boots cleanly and logs store=noop bus=direct realtime=noop identity=noop — every route that needs a real adapter then fails per-request (typically 501/403, adapter-dependent), not at startup.

This matters operationally: you can stand up cns-server incrementally — wire identity first and confirm auth works, then store, then realtime — without the service being unavailable at any point in between.

Which environment variable selects which implementation

internal/config.Load inspects presence, not an explicit _IMPL switch, for most seams — the first matching case wins:

SeamSelects a real implementation when...Otherwise
StoreCNS_CLICKHOUSE_ENDPOINT is set → clickhouse; else CNS_STORE_IMPL=memory → in-process memorynoop
Bus(always direct, no variable)
RealtimeCNS_CENTRIFUGO_ADDR is set → centrifugonoop
IdentityCNS_SSO_JWT_HMAC_SECRET set → sso-jwt (HMAC); else CNS_SSO_JWT_JWKS_URL set → sso-jwt (JWKS)noop
SecretsCNS_SECRETS_IMPL (default ssm; memory or noop also accepted)(always has a value — defaults to ssm)
Channels(always [{Impl: "inapp"}], no variable)

Full variable-by-variable reference, including every identity/store/secrets setting: Environment variables.

The dev-fixture guard rail

Config.DevFixture (CNS_DEV_FIXTURE=1) is not just a convenience flag — Load actively refuses to boot unless the rest of the configuration is provably harmless: a loopback listen address, CNS_ENV=dev, store and secrets both set to the in-process memory implementation, and realtime left as noop. This exists specifically so a dev-fixture script can never accidentally be pointed at production settings by a copy-paste mistake — see The dev fixture.