Flags and variations
A flag has an immutable key, a name, a description, and an ordered list of JSON values. The index is the variation’s identity. Booleans, numbers, strings, arrays, and objects are all ordinary values.
Open source · self-hosted · HTTP API
flaggator is a feature-flag service you run yourself. One local process serves the HTTP API, and every behavior the service offers goes through that API: projects, environments, flags, targeting, and evaluation.
A project holds flags and environments. Variations belong to the flag. Targeting belongs to one environment, so a flag can be on in dev and off in prod.
A flag has an immutable key, a name, a description, and an ordered list of JSON values. The index is the variation’s identity. Booleans, numbers, strings, arrays, and objects are all ordinary values.
Each environment stores whether the flag is on, the off variation, individual targets, ordered rules, a fallthrough, prerequisites, and a config version. A new environment starts with every existing flag disabled.
The first matching rule wins, and every clause in that rule must match.
A rule can serve one variation or a percentage rollout. Rollouts hash
flag:salt:context key with SHA-256, so the same person stays
in the same bucket.
A flag can require another flag in the same environment to evaluate to a given variation before its own rules run. Cycles are rejected on write. If a prerequisite fails, the off variation is served.
There are no user accounts. A bearer token is the caller. Secrets are stored as hashes and shown in plaintext only once, when the key is created.
| Management token | SDK key |
|---|---|
| Creates and edits projects, environments, flags, and targeting. Can read the audit log. | Bound to one environment. Evaluates flags and reads that environment’s evaluation config. |
| May also evaluate, so an operator can debug a flag. Must name the project and environment. | Cannot mutate. A mismatched project or environment on the request is forbidden. |
Replacing targeting sends the version the client read. A stale version is a conflict, so two editors cannot silently overwrite each other. The client reads again and retries. Mutations are audited: actor, action, and the before and after documents. The secret itself is never written to the log.
GET /health is the only route without a token. It reports that
the process is up and does not check the database.
The same configuration and the same context always produce the same decision. Time and randomness are not inputs. Order is off, prerequisites, targets, rules, then fallthrough. This example has no prerequisites. The page runs it in the browser and does not call a server.
—
Controllers stay thin. A handler parses the request, calls one domain service, and writes the response. Business rules do not live in SQL or in the HTTP layer.
The connection layer, schema migrations, in-memory cache, GET /health, management tokens, projects, environments, and flags are in place. python -m main bootstrap creates a management token. The process does not listen for requests yet. SDK keys, targeting writes, the audit log, and the evaluate routes are specified and not served yet. The license is not chosen yet.
github.com/frncscsrcc/flaggator