The "Use this template" starting point for flagpost - git-based feature flag control. Fork this repo, manage flags as YAML, read them at runtime via @flagpost/sdk-js.
- 🌐 flagpost.ianwelerson.com - full documentation site
- 📖 Docs source on GitHub - canonical source
flagpost ships llms.txt-style bundles for LLM-friendly consumption:
- 📇
/llms.txt- index of the bundles below (~1 KB) - 📦
/llms-small.txt- abridged docs, non-essential content stripped (~87 KB) - 📚
/llms-full.txt- full documentation in a single markdown file (~95 KB)
Point your agent at one of these for instant project context. This repo's CLAUDE.md also briefs Claude on how to work in a forked flag repo.
- 🗂️ Flags as YAML - one file per flag in
flags/, version-controlled, easy to diff - 🔀 PR-driven changes - review, audit log, and rollback come for free via
git - 🤖 Two GitHub workflows - schema validation on PRs,
flags.jsoncompilation on merge - 📊 Auto-updated flag table -
FLAGS.mdis regenerated on every push tomain - 🔒 Private-repo-first - your flag names never leak; no public dashboard
- ⚡ Zero infrastructure - no servers, no databases, no accounts
Inspired by upptime - uptime monitoring as a GitHub repo. flagpost applies the same idea to feature flags.
Flags are no longer just enabled: true | false. The schema supports:
- 🎯 Percentage rollout - integer
0-100; the SDK buckets users deterministically byuserId. Docs - 👥 User / group targeting -
targeting.enableandtargeting.disablelists;disablewins. Docs - 🌳 Per-environment overrides -
environments.{name}blocks overrideenabled,rollout, ortargetingper env. Docs
See flags/example-advanced.yml for a flag that exercises all three.
See FLAGS.md for the current flag state, auto-updated by CI on every merge to main.
Click Use this template at the top of this repo and make your fork private.
# flags/new-checkout.yml
name: new-checkout
enabled: true
description: Roll out the redesigned checkout
owner: "@you"The filename (minus .yml) must match the name: field. Rename flags/example.yml to your first real flag, or delete it.
The Validate flags workflow checks the schema on every PR.
The Build flags workflow recompiles flags.json, refreshes the flag table in FLAGS.md, and commits the result.
npm install @flagpost/sdk-jsimport { Flagpost } from "@flagpost/sdk-js";
const flagpost = new Flagpost({
repo: "you/your-flags-repo",
token: process.env.FLAGPOST_TOKEN, // PAT for private repos
});
await flagpost.load();
if (flagpost.isEnabled("new-checkout")) {
showNewCheckout();
}That's it. No backend. No dashboard. No account.
Every flag is a YAML file under flags/.
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string |
✅ | Lowercase alphanumeric + hyphens, ≤ 64 chars. Must match the filename. |
enabled |
boolean |
✅ | Base on/off state. |
description |
string |
≤ 280 chars. Shown in the auto-updated FLAGS.md table. |
|
owner |
string |
≤ 64 chars. Typically a GitHub handle. | |
rollout |
integer |
Percentage 0-100. Needs userId in the SDK eval context. |
|
targeting |
object |
enable / disable lists of users and groups. |
|
environments |
object |
Map of env name to a partial flag config (enabled, rollout, targeting). |
Unknown fields are rejected - a typo like enabld: true will fail validation. Full reference: Flag schema.
For private flag repos, the SDK needs a token with read access to this repo. Use a fine-grained PAT scoped to the single flag repo:
- Contents: Read-only on this repo - nothing else.
- Set an expiration (90 days is a good default) and rotate on schedule.
- Never commit it. Pass it via
process.env.FLAGPOST_TOKENor your platform's secret manager.
Full walkthrough: GitHub token setup.
@flagpost/sdk-js can read flags from more than just GitHub. Pick the source that fits each environment:
github- the default; polls your private/public flag repo on a TTL.file- load a bundledflags.jsonfrom disk (offline tools, build-time bundling).memory- hand-rolled artifact for tests and SSR.
See Sources overview for the full comparison and per-source docs.
For local dev and tests, force flags on/off without touching the repo:
new Flagpost({
repo: "you/your-flags-repo",
overrides: {
"new-checkout": true,
"dark-mode": false,
},
});💡 Static overrides work without calling
load()- useful for tests where you don't want any network calls.
Function-style overrides (dynamic conditions, env-aware) are documented in Local overrides.
your-flags-repo/
├── flags/
│ ├── example.yml # minimal starter flag - edit or replace
│ └── example-advanced.yml # showcases rollout + targeting + environments
├── .github/workflows/
│ ├── validate.yml # runs @flagpost/action in `validate` mode on PRs
│ └── build.yml # runs @flagpost/action in `build` mode on push to main
├── flags.json # compiled artifact - don't edit by hand
├── FLAGS.md # auto-updated flag table - don't edit between the markers
├── README.md # you are here
└── LICENSE
- 🏗️ flagpost - the monorepo (core schema, SDK, action, docs)
- 📦 @flagpost/sdk-js - runtime SDK
- 🤖 @flagpost/action - the GitHub Action used here
- 💡 upptime - the project that inspired this approach