Architecture
At a glance
Overall design
The feature-flag system is a single workspace library — @cdz/feature-flags (libs/feature-flags/) — backed by a single DynamoDB table named codazen-feature-flags. The table is provisioned once in production AWS (us-west-2) by SST and is shared across all stages. Stage isolation happens per-row, not per-table: every flag row carries an enabledIn array (e.g. ["staging"]), and consumers compare it against their own runtime stage.
The library exposes one fast read path — isEnabled(name, { scope }) — used by every consuming app on every flag check, plus admin functions listFlags, getFlag, setEnabledIn, and listFlagHistory used by this dashboard.
Data model
Two row shapes share the same partition; setEnabledIn writes both atomically in a single TransactWriteCommand so audit history can never diverge from current state.
Flag row:
pk = FLAG#<scope>#<name>,sk = META— current state, description, last updater, last updated.History row: same
pk,sk = HISTORY#<timestamp>#<uuid>— previousenabledIn, newenabledIn, who, when, the change note.
Scopes are implicit: there is no separate SCOPE#<name> registry row today. A scope "exists" once at least one flag has been written under it; the dashboard derives the scope list by scanning for sk = META rows.
How Harmonica consumes flags
Harmonica wires the library at API boot — apps/harmonica-api/src/main.ts calls configureFeatureFlags({ tableName, defaultScope: 'harmonica', stage }) once, where stage is resolved from the STAGE env var (set by SST in deployed envs, defaults to local for dev).
At request time, handlers call isEnabled('flag-name') directly. The Harmonica frontend fetches a small allowlisted bundle of flag states through the/api/feature-flags endpoint and exposes them to React via the useFeatureFlags() hook — apps/harmonica/src/app/(protected)/drops/page.tsx is the canonical example, gating the Drops surface on useFeatureFlags().isEnabled('cadence').
This dashboard
The admin dashboard (apps/codazen-feature-flags-admin/) is a Next.js 16 + React 19 app using the App Router, MUI v7 + Emotion for UI, and NextAuth 4 + Google OAuth for auth. Sign-in is restricted to @codazen.com emails via the shared @cdz/auth lib.
Reads happen in server components, which call ensureFeatureFlagsConfigured() (a one-time bootstrap that registers a DynamoDB client honoring DYNAMODB_ENDPOINT for local dev) and then call listFlags / getFlag / listFlagHistory directly — no extra HTTP hop. Mutations go through Next.js API routes (POST /api/feature-flags/[scope], PATCH /api/feature-flags/[scope]/[name]) which gate every request with requireCodazenSession() before invoking setEnabledIn. updatedBy always comes from the verified server session, never from the client.
Deployment runs on Vercel at ff.codazen.com. Vercel's GitHub integration auto-deploys every push to main as a Production deploy and every other branch as a Preview deploy. AWS credentials and OAuth secrets live in the Vercel project's environment variables; the SDK uses real DDB in production (no DYNAMODB_ENDPOINT set) and DDB Local in development (endpoint pointed at http://localhost:8000).