Status: Accepted Date: 2026-04-28 UTC Refs: #146, console-feature-flags.md
The console needs a mechanism to persist feature flag values that: - Survives Heroku dyno restarts and deploys (i.e., is not just in-process state) - Is auditable (who changed it, when, from what to what) - Is env-scoped (prod and staging can diverge) - Is accessible to the console UI without requiring Heroku CLI access
Three persistence candidates were evaluated:
console_feature_flags tableOption 1: new console_feature_flags table in the console Postgres DB.
Schema: (flag_key TEXT, env TEXT, value INTEGER 0/1, updated_at, updated_by) with a UNIQUE (flag_key, env) constraint.
Resolution order: DB row wins over env var, which wins over YAML default.
Positive:
- Auditable within the existing console_audit_log infrastructure. No new logging system needed.
- Env-scoped trivially via the env column — the same flag key can have different values for prod and staging.
- Transactional: flips are atomic DB writes. No partial state.
- Rollback is a table drop: the existing env-var fallback re-activates automatically.
- The updated_by FK provides operator attribution without any new identity infrastructure.
- Consistent with the env-switcher pattern (ADR-0024), which also uses the console DB for session-resident state.
Negative: - One more table to migrate and maintain. - Cache invalidation across multiple dynos: a flip on dyno A's in-process cache does not immediately invalidate dyno B's cache. Mitigated by a short TTL (30s). If sub-30s propagation is required, a pub/sub mechanism would be needed (deferred to open question in the design doc). - If the console DB is unavailable, flag reads fall back to env vars / YAML (fail-safe, not fail-closed).
Infisical already holds sensitive config values. Storing feature flags there would co-locate all runtime config in one place.
Rejected for the following reasons: - Infisical is designed for secrets; feature flag values are not sensitive. Treating them as secrets conflates the two concerns and means flag reads require vault credentials everywhere a flag is checked. - Audit log in Infisical is external to the console's own audit trail. Flag flip events would appear in two different audit systems (Infisical + console_audit_log), creating reconciliation burden. - The Infisical client has latency and dependency overhead inappropriate for a hot path that is checked on nearly every request. - Env-scoping in Infisical is possible but requires Infisical environment separation to be correctly wired for every consumer — an operational dependency that does not exist today.
The console could call the Heroku Platform API to read/write config vars, replacing the CLI.
Rejected for the following reasons: - Requires storing a Heroku API token in the console, which is a rotatable secret but also a significant blast-radius credential. Not appropriate for a flag flip path. - Heroku config var changes restart the dyno — a flag flip would cause a brief outage on the console itself. Unacceptable for a UI-driven toggle. - Audit log would require custom implementation (Heroku API calls are not audit-logged in console_audit_log automatically). - Couples the console's operational state to Heroku's API availability.