Feature flags
Runtime toggles + percentage rollouts, audited.
/admin/feature-flags lists every flag with its current state. Each row exposes a master on/off Switch and a numeric rollout-pct input.
Routes
GET /api/v1/platform/feature-flags
PATCH /api/v1/platform/feature-flags/{key}PATCH body is partial — any field you omit keeps its current value. Supported fields:
{
enabled?: boolean;
rolloutPct?: number | null; // 0..100, clamped server-side
description?: string | null;
enabledOrgIds?: string[]; // user-level allow-list (override)
enabledUserIds?: string[]; // org-level allow-list (override)
}Audit: platform.feature_flag.set with { before: { enabled, rolloutPct }, after: { enabled, rolloutPct } }.
Resolution order
EvaluateFlagUseCase resolves in this order (first match wins):
- User allow-list —
enabledUserIds.includes(userId)→ on. - Org allow-list —
enabledOrgIds.includes(organizationId)→ on. - Master switch —
enabled === false→ off. - Rollout — bucket
(key, principal)to[0, 100)viasha256and compare torolloutPct. Same input → same bucket across processes, no shared cache.
The bucket is keyed by userId if present, otherwise organizationId (so anonymous traffic deterministically lands in the same bucket per session).
Creating a flag
Flags are created via the tenant-scoped PUT /api/v1/feature-flags (also requirePlatformAdmin-gated, separate router). The admin console only exposes the cross-tenant modify path because the typical admin task is "flip what's already there", not "create new". Use the upsert endpoint or seed flags from your migration script.
curl -X PUT http://localhost:3005/api/v1/feature-flags \
-H 'Content-Type: application/json' \
-b "$(cookie-jar)" \
-d '{"key":"new-dashboard","description":"New dashboard layout","enabled":false,"rolloutPct":null}'Rollout strategy
Common patterns:
- Internal dogfood —
enabledUserIds: [...staffIds],enabled: false,rolloutPct: null. - Gradual rollout —
enabled: true,rolloutPct: 5 → 10 → 25 → 50 → 100. - Allow-list specific tenants —
enabledOrgIds: [...]plusenabled: false. - Kill switch —
enabled: falseoverrides every other setting except the explicit user/org allow-lists.