@moshevents/flags
v0.8.0
Published
Mosh — single source of truth for the feature-flag / remote-config registry. Typed `Flags` for web/admin; iOS consumes the SwiftPM target.
Readme
mosh-flags
Single source of truth for the feature-flag / remote-config registry across web, admin, and iOS — the third SSOT alongside mosh-tokens and mosh-copy. Same pattern: one hand-edited source → a dependency-free generator → typed per-platform outputs.
This repo owns the schema (which flags exist, their types, defaults, owners). The values are resolved at runtime by the backend.
What's generated
flags.json → npm run build →
Sources/MoshFlags/Flags.swift— iOS:Flags.<key>as a typedFlagKey<T>with the default baked in.dist/flags.ts— web/admin: typedFlagsregistry,FlagKeyunion,FLAG_DEFS(for backend seeding).dist/flags.md— human/ops view: a table of every flag.
FlagKey.swift is hand-written (the typed key + FlagResolving protocol); Flags.swift is generated.
Add a flag
- Add an entry under
flagsinflags.json:"newCheckoutEnabled": { "type": "bool", "default": false, "description": "...", "owner": "platform" }type∈bool | string | int | doubledefaultMUST be safe to ship if the backend is down. For kill-switches, defaulttrue(feature stays ON until explicitly killed).- keys are
lowerCameland stable — renaming a key is a new flag.
npm run build(regenerates all three outputs).- Backend seeds/validates against the registry; clients reference
Flags.newCheckoutEnabled.
Flags are temporary — once a rollout is 100% and durable, fold the behavior in and delete the flag.
How the full system fits (v1: operational gating + % rollout)
flags.json (this repo, the registry/contract)
│ build.mjs
├── Flags.swift ─────────────► iOS app: RemoteConfig.shared.value(for: Flags.x)
├── flags.ts ─────────────► web/admin: typed keys + defaults
└── flags.md ─────────────► ops view
Backend (dreadnought, Postgres + GraphQL + Redis)
• feature_flags table (key, type, enabled, default, rollout_pct, targeting) — seeded from FLAG_DEFS
• query featureFlags(context): resolves each flag for the requesting user
enabled? → targeting match? → stable %-rollout (hash(userId+key) < pct) → value/variant
• Redis-cached; admin mutations to toggle / set rollout
│
▼
Clients fetch the resolved map on launch (+ refresh), cache locally, and
fall back to the SSOT `default` when offline or for unknown keys.Admin console gets a flags screen (list, toggle, rollout %). iOS is the priority consumer (App-Store cadence → server-driven gating). See ~/Desktop/development/SSOT_REFACTOR.md for cross-repo status.
Consume
- iOS: add the SwiftPM package,
import MoshFlags, thenRemoteConfig.shared.value(for: Flags.maintenanceMode)(the app providesRemoteConfig, conforming toFlagResolving). - web/admin:
npm i @moshevents/flags,import { Flags, FLAG_DEFS } from "@moshevents/flags".
