How it works

60 seconds, end to end.

Create flag → scope to tenant → 50% rollout → approval → audit → revert. Six steps. Zero Slack threads.

v1 · video · v1.5 · interactive walkthrough (navattic / arcade)

Step by step

Each step writes a row. Each row is queryable by tenant.

01 · Create flag
Define the flag and its default.
argus flag create new_billing_v2 --env prod --default off

The flag enters the system with a key, an environment, and a default value. No tenant has it yet. The default is what every tenant resolves to until you change it for one.

02 · Scope to tenant
One tenant gets a per-tenant override document.
argus tenant 98chimp set new_billing_v2 on

Override documents are first-class — not segment clauses. They have an audit stream, an owner, and a resolution lane. Touching tenant 98chimp does not touch tenant loomi.

03 · 50% rollout
Deterministic hash by tenant_id and flag_key.
argus rollout new_billing_v2 --tenant 98chimp --pct 50

The same user inside tenant 98chimp gets the same outcome on every evaluation. No surprise drift between sessions, regions, or SDK reloads.

04 · Two-engineer approval
Production-impacting changes wait for a second reviewer.
approval requested · awaiting 1 of 2 · reviewers marco, roberto

Required, not configurable. Solo deploys to prod are not a feature; the system enforces them out of existence.

05 · Audit row written
Append-only. Tenant-scoped. Source metadata travels.
actor=shahin env=prod tenant=98chimp flag=new_billing_v2 before=off after=50% ts=2026-05-04T22:08:14Z

The row carries the source of the change (rule · override · rollout) so a compliance question two quarters later does not need archaeology.

06 · Revert · one tenant, one click
Roll back for 98chimp without touching loomi or dk_derby.
argus rollback new_billing_v2 --tenant 98chimp · blast radius = 0

The override flips. A new audit row appends. Cross-tenant blast radius is zero by construction — because there is no shared rule to edit.

Why this works

Tenant is required at the storage layer.

Every flag, every rule, every audit row is tenant-scoped at the storage layer. Bypass is impossible — not because we ask the SDK nicely, but because the row can't be written without one.

MVP runs on Firebase: Firestore as data plane and change log via onSnapshot, Cloud Functions enforcing approvals and audit writes. A custom data plane is a post-PMF decision.

See the data model
Layer 04SDK
evaluate() subscribe() audit()
Layer 03Rules
flag override rollout
Layer 02Tenant
tenant_id env scope
Layer 01Storage
firestore cloud functions
What this replaces

Four artifacts that should not exist anymore.

  • The "is feature X on for client Y?" Slack thread that pings three engineers.
  • The custom user segment named tenant_b_only_pilot_v3 with a TODO from last quarter.
  • The spreadsheet titled Brand Feature Matrix - DO NOT EDIT that everybody edits.
  • The post-incident grep for "what changed for the regulated customer last quarter".
Pilot · 30 days · written-first

Three design partners. We respond in writing within two business days.