Feature flags
Flags
One admin page turns features on and off in apps that share no code or database, and an app that can't reach the service runs on the defaults in its own code.
- 2endpoints an app calls
- 30 sbetween polls; a change reaches the app on the next one
- 100flags per app
- 0lines of code shared with the apps
How an app reads its flags
-
Declare flags in code
Each flag has a key, a default and optional scopes, such as phemten's
liveandsandboxchannels. A new feature's default is off. -
Poll with the declarations
Every 30 seconds the app sends its declarations in one
PUTand gets back what has been set. The same call restores flags the service has lost. -
Evaluate in memory
A check reads the scope's override, else the flag's value, else the code default. No request waits on the service.
Failure and access rules
-
An app keeps running when the service is down
A failed or unreadable poll keeps the last snapshot that worked. Before the first one arrives, the app uses its code defaults.
-
Each app sees only its own flags
An app's Access service token is bound to one namespace. It reads and declares there, and can't change a value anywhere.
-
Only a signed-in admin changes a value
A change needs a person's Access login and an
X-Flags-Adminheader, which a browser does not add to a request from another site. -
Changes are logged with who made them
The page lists the last 20 changes, each with who made it and the old and new value.
Under the hood
- Endpoints
PUT /v1/<namespace>/declarations,GET /v1/<namespace>/flags- Snapshot
valueandscopesper flag;nullmeans unset- Limits
- 100 flags per app; names are lowercase words joined by hyphens, up to 48 characters
- Token check
- Cloudflare Access JWT, RS256; signature, issuer, audience and expiry required
- Admin page
- One script, allowed by its hash;
frame-ancestors 'none', so no other site can frame it - Stack
- Python, FastAPI and SQLite; the rules are in one module that does no IO