Feature flags
What it does
The packages/flags cartridge38 evaluates typed flags.
A flag declares its name, schema25, default, owner, purpose, removal date, and evidence ladder7.ΒΆ
Each rung names its authority and exit measurements. A read scope7 is the partition for a flag value and its evidence.
A child schema defines it; the default is global.ΒΆ
Handlers request the Flags service26 from core.
The Config adapter27 reads deployment configuration.
The memory adapter supplies rules and writable values for tests.ΒΆ
The KV adapter owns its namespace through Alchemy.
Under the default policy, missing KV values use Config values or declared defaults.
The refuse policy returns a typed failure on a missing or failed replica read.ΒΆ
listFlagslists names, owners, and removal dates.ΒΆgetFlagevaluates one flag for an optional subject.ΒΆ- Allow-lists select a value for named subjects.ΒΆ
- Percentage rules use a stable hash of the flag name and subject.ΒΆ
- The first matching rule wins. Missing subjects use the default.ΒΆ
EVENTS_ENABLED defaults to false and stays deploy-time Config.
Live KV writes cannot change this flag.ΒΆ
EVENTS_IDENTITY_MODE defaults to daily.
The Worker reads its live value for each captured request.
Config keeps the existing environment keys and boolean spellings.
An empty KV namespace preserves Config behavior.ΒΆ
Invalid Config stops initialization.
A failed KV read follows the catalogβs failurePolicy.
The default policy returns the declared default. The refuse policy preserves the typed failure.ΒΆ
Previews use Config and declare no flags namespace. The production Worker and content backend read KV.ΒΆ
Alchemy still selects analytics resources during deployment. See Analytics18 for capture and identity behavior.ΒΆ
The standard
- Flag values pass their declared schemas.ΒΆ
- Every declaration names an owner and a
removeBydate.ΒΆ - Lint rejects declarations without a literal removal date.ΒΆ
- The flags build rejects expired registered events flags with a typed repair message.ΒΆ
- Each subject keeps its rollout bucket when the rollout percentage changes.ΒΆ
- The lifecycle permits
declaredβrollingOutβonβremoved.ΒΆ - Declared Effect29 actors record lifecycle transitions. Removed flags stay removed.ΒΆ
- Each capability27 shares its contract27 across surfaces. JSON encoding is required for
getFlag.ΒΆ
Three operator capabilities replace the raw setter:ΒΆ
flip_feature_flagclimbs one rung or flipsoff.ΒΆrecord_flag_evidencerecords a measurement at the current rung and exact scope34.ΒΆread_flag_ledgerreads a bounded attempt page and the authoritative position.ΒΆ
Climbs use the capability approval service. Above agent, they also need an approvalRef quote.
The roles are agent, arbiter, and owner. Token checks belong in the childβs composition.ΒΆ
off bypasses approval and still records its caller in the attempt ledger.ΒΆ
The CLI accepts --yes or asks for interactive approval on a climb.
Its default composition has no live ledger and returns a named error.ΒΆ
The CLI records local-cli as a channel label.
Its caller has no authenticated person identity.ΒΆ
The public Worker, RPC readers, and read-only sandbox expose no flag writes. A child mounts its operator surface25 and supplies its approval policy and ledger adapter.ΒΆ
Each available-store attempt records its caller, flag, observed old value, requested value, time, and outcome. The journal stores an immutable Pending key before the approval gate. A separate completion key records success, denial, failure, or interruption. Both keys share an attempt id.ΒΆ
Missing completion means Pending with an unknown result. A failed completion write returns a typed error; the flag may have changed. Audit storage failures block only writes. Config and default journals use composition-local memory.ΒΆ
A projection is a copy derived from a committed value.ΒΆ
The default operator adapter uses one Durable Object per flag and scope. It serializes transitions and commits the value, receipt, and dirty projection marker together. An alarm projects that committed value to KV. Failed projection keeps the marker for redrive14.ΒΆ
Readers use KV and never call the Durable Object.ΒΆ
KV changes may take 60 seconds or more to reach another location. The observed old value in an attempt may be stale. Climb validation uses the DOβs authoritative position. Use deployment settings or a stronger control path for kill switches and safety stops.ΒΆ
The explicit KV ledger fallback keeps immutable attempt and outcome keys.
It refuses authoritative flips and evidence writes with FlagAtomicityRequired by default.
A composition can opt in with authoritativeWrites: "accept-non-atomic-unserialized".ΒΆ
That option accepts the absence of atomic commit and cross-isolate serialization. Writes to the same KV key can hit its one-write-per-second limit.ΒΆ
The crash-recovery proof uses a persisted service double. Native workerd transaction and alarm behavior still needs provider qualification. The operator ledger is not wired into the deployed Stack.ΒΆ
The lifecycle journal is scoped memory, with no durable state between processes.ΒΆ
How to check
- Run
pnpm --filter @rat-stack/flags testfor rollout properties, generated write histories, audit failures, and lifecycle histories.ΒΆ - Run
pnpm --filter @rat-stack/core exec vitest run test/patterns-rule.test.tsfor the removal-date fence45.ΒΆ - Run
pnpm turbo run check test build --concurrency=1for the full gate.ΒΆ - Inspect the deployment plan. Readers declare one KV namespace. The operator layer18 adds the ledger DO and its KV write binding23.ΒΆ
- Follow Add a feature flag for scoped evidence, authority, and crash-redrive checks.ΒΆ
- Replace
apps/site/src/flags.tswith core imports.ΒΆ - Export
flagsLayer = Flags.defaults(eventFlags)andliveFlagsLayer = flagsLayer.ΒΆ - Remove the package dependency and its directory.ΒΆ
Callers keep their typed defaults. The CLI retains its named read-only failure.ΒΆ
Sources
- Flags cartridge. rat-stack. Evaluation, adapters, audit journal, lifecycle, and behavior tests. Accessed 2026-10-11.
- How KV works. Cloudflare. Eventual consistency, cached missing keys, and no atomic transactions. Accessed 2026-10-11.
- Workers KV limits. Cloudflare. One write per second to each key. Accessed 2026-10-11.
Last changed 2026-10-24 in 777b9ba. Source on GitHub. Change log.
Linked from
- add-a-feature-flag. Declare a typed feature flag, its scoped evidence ladder, approval roles, removal date, and durable ledger adapter.
Read Flags climb on evidence and Feature flags first.ΒΆ
- Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
Feature flags β Typed feature flags with owners, removal dates, and stable subject rollouts.
- Change log. What changed in the files served here, newest first.
Feature flags β edited: fix(flags): refuse non-atomic KV authority by default
- Durability. Seven durable Effect patterns, each with its seam, its fence, and the incident that taught it.
Seam: the flags cartridge.
- Flags climb on evidence. A flag ladder connects each rollout step to scoped measurements, approval, and a durable receipt.
A feature flag declares a ladder of values.
- Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
- Learn the stack | rat-stack. Hands-on guides to Effect actions, XState lifecycles, and the seams between stack pieces.
add-a-feature-flag β Declare a typed feature flag, its scoped evidence ladder, approval roles, removal date, and durable ledger adapter.
- Rat Stack systems | rat-stack. The systems rat-stack ships, the standard each keeps, and how to check it.
Feature flags β Typed feature flags with owners, removal dates, and stable subject rollouts.
- Source change history (log.md). The generated change log as Markdown.
Feature flags β edited: fix(flags): refuse non-atomic KV authority by default