Implement feature flag system#1823
Open
paustint wants to merge 1 commit into
Open
Conversation
…side verification
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a lightweight, code-defined feature flag registry with database-backed per-user/per-team overrides, plus server-side signing and browser-side signature verification so the UI can consume “trusted” resolved flags.
Changes:
- Add
FeatureFlagOverridePrisma model + migration to persist user/team-scoped flag overrides. - Introduce shared
@jetstream/typesflag registry + deterministic serialization for signing/verification. - Resolve + sign flags in the API
/api/meprofile path, verify once on the client, and consume flags via new Jotai state/hook (with an example UI gate inAppHome).
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| prisma/schema.prisma | Adds FeatureFlagOverride model and relations on User/Team. |
| prisma/migrations/20260623031854_add_feature_flag_override/migration.sql | Creates the feature_flag_override table and indexes/FKs. |
| libs/types/src/lib/types.ts | Extends UserProfileUi with featureFlags + featureFlagsSignature. |
| libs/types/src/lib/feature-flags.ts | Adds shared feature flag registry + stable signing serialization. |
| libs/types/src/index.ts | Re-exports feature flag utilities/types. |
| libs/shared/ui-utils/src/lib/shared-feature-flag-utils.ts | Adds browser-side signature verification and extraction of trusted flags. |
| libs/shared/ui-utils/src/index.ts | Exports the new UI feature flag helper. |
| libs/shared/ui-core/src/app/AppHome/AppHome.tsx | Adds feature-flag gating for home cards. |
| libs/shared/ui-app-state/src/lib/ui-app-state.ts | Verifies flags once during profile fetch; adds featureFlagsState + useFeatureFlag. |
| libs/api-config/src/lib/env-config.ts | Adds JETSTREAM_FEATURE_FLAG_PRIVATE_KEY env support. |
| apps/api/src/app/services/feature-flag-signing.service.ts | Implements ECDSA P-256 signing (ieee-p1363) for resolved flags. |
| apps/api/src/app/services/tests/feature-flag-signing.service.spec.ts | Tests signature validity and tamper detection. |
| apps/api/src/app/db/user.db.ts | Resolves + signs flags when building the user-facing profile. |
| apps/api/src/app/db/feature-flags.db.ts | Adds DB-backed flag resolution + optional authoritative server-side gate helper. |
| apps/api/src/app/db/tests/feature-flags.db.spec.ts | Tests resolution behavior, scoping, and gating. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+15
to
+23
| function base64ToBytes(value: string): Uint8Array<ArrayBuffer> { | ||
| const normalized = value.replace(/-/g, '+').replace(/_/g, '/'); | ||
| const binary = atob(normalized); | ||
| const bytes = new Uint8Array(binary.length); | ||
| for (let i = 0; i < binary.length; i++) { | ||
| bytes[i] = binary.charCodeAt(i); | ||
| } | ||
| return bytes; | ||
| } |
| logger.warn('[FEATURE FLAGS] Signature verification failed — using code defaults'); | ||
| return { ...DEFAULT_FEATURE_FLAGS }; | ||
| } | ||
| return { ...DEFAULT_FEATURE_FLAGS, ...received }; |
Comment on lines
+29
to
+51
| /** | ||
| * Authoritative server-side check for a single flag. Use this to enforce a gate on a dedicated API | ||
| * endpoint (mirrors `checkUserEntitlement`). Most of the UI hits generic endpoints, so this is only | ||
| * useful for the rare feature with its own server route. | ||
| */ | ||
| /** | ||
| * Canonical "active team" used for flag scoping: an ACTIVE membership in an ACTIVE team. The /api/me | ||
| * profile path (`findIdByUserIdUserFacing`) applies the same rule in-memory off its already-loaded | ||
| * membership, so the flags shown in the UI and the server-side gate always agree on the team. | ||
| */ | ||
| export async function resolveActiveTeamIdForUser(userId: string): Promise<string | null> { | ||
| const membership = await prisma.teamMember.findFirst({ | ||
| select: { teamId: true }, | ||
| where: { userId, status: 'ACTIVE', team: { status: 'ACTIVE' } }, | ||
| }); | ||
| return membership?.teamId ?? null; | ||
| } | ||
|
|
||
| export async function checkFeatureFlag({ userId, key }: { userId: string; key: FeatureFlagKey }): Promise<boolean> { | ||
| const teamId = await resolveActiveTeamIdForUser(userId); | ||
| const flags = await resolveFeatureFlagsForUser({ userId, teamId }); | ||
| return flags[key]; | ||
| } |
Comment on lines
+2
to
+14
| CREATE TABLE "feature_flag_override" ( | ||
| "id" UUID NOT NULL DEFAULT uuid_generate_v4(), | ||
| "key" VARCHAR(255) NOT NULL, | ||
| "enabled" BOOLEAN NOT NULL DEFAULT true, | ||
| "userId" UUID, | ||
| "teamId" UUID, | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "updatedAt" TIMESTAMP(3) NOT NULL, | ||
|
|
||
| CONSTRAINT "feature_flag_override_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateIndexes |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduce a feature flag system that allows for client-side verification and database support for user and team-specific overrides. This system enhances feature management by enabling dynamic control over feature availability based on user entitlements.