feature-flag-engine: A Terminal-First Control Plane for Feature Flags and Remote Config
A clean-architecture Python stack that combines deterministic rollouts, live WebSocket updates, and a Textual dashboard to manage app behavior without SaaS lock-in.
- This repo turns feature management into a compact infrastructure system by pairing deterministic evaluation, real-time propagation, and a terminal-first operator workflow.
- Its rollout logic is stateless but still sticky, because the same user and flag key always hash into the same bucket.
- WebSockets make every mutation visible immediately, so the control plane feels live instead of eventually consistent.
- Clean Architecture and audit logs keep the flag engine testable, traceable, and easier to swap into a larger stack.
The Control Room Lives in the Terminal
The most opinionated choice in feature-flag-engine is not the rollout math. It is the interface. Instead of a browser dashboard, the project puts operators in a Textual terminal, where keyboard shortcuts and async actions replace mouse-heavy admin work.
That matters because feature flags are not a one-time setup. They are an operating surface. If the tool is built for fast toggles, quick edits, and immediate feedback, the terminal becomes a serious advantage rather than a nostalgic flourish.
# dashboard/app.py style interaction
# Typical bindings in the TUI
bindings = {
"space": "toggle_flag",
"e": "edit_flag",
"r": "refresh_state",
}
# Actions map directly to async API calls
# while WebSocket updates keep the view current.
The architectural signal is clear. This is not a demo wrapped around an admin panel. It is a control room built for people who want to move fast from the keyboard.
The Real Trick: Rollouts Without State
The engine’s core evaluation flow is simple and surprisingly disciplined. First it checks whether the flag is globally enabled. Then it checks segment membership. Only after those gates does it fall through to percentage rollout, where a deterministic hash turns a user and flag key into a stable bucket.
That is the elegant part of the design. The backend does not need to remember which users were assigned where. The hash function does the remembering for it, which keeps rollout behavior predictable across repeated requests and across instances.
import hashlib
def bucket(flag_key: str, user_id: str) -> int:
digest = hashlib.md5(f"{flag_key}:{user_id}".encode()).hexdigest()
return int(digest, 16) % 100
def evaluate(flag, user):
if not flag.enabled:
return False
if flag.target_segments and user.segment not in flag.target_segments:
return False
return bucket(flag.key, user.id) < flag.rollout_percentage
Why the Broadcast Layer Matters
Deterministic evaluation is only half the story. The rest is distribution. The backend keeps active WebSocket connections in a manager, and when a flag changes, the mutation is broadcast immediately to connected clients.
That turns the engine from a static API into a live control plane. A toggle in one place becomes visible everywhere else without waiting for a refresh cycle or a redeploy. Dead sockets get cleaned up during failed sends, which keeps the broadcast list from rotting over time.
Clean Architecture Keeps the Flag Logic Pure
The repository is organized like a system that expects to grow. Domain entities stay separate from application use cases, repositories handle persistence, and delivery concerns live at the edge in FastAPI, WebSockets, and the TUI.
That split is not academic. It makes the feature logic testable without the database, keeps the persistence layer swappable, and prevents the rollout engine from becoming tangled with transport code.
| Layer | Responsibility | Why it helps |
|---|---|---|
| Domain | FeatureFlag, audit records, and repository interfaces | Keeps business rules pure and easy to test |
| Application | Evaluation and mutation use cases | Centralizes rollout logic and operator workflows |
| Infrastructure | SQLAlchemy repositories and JSON storage | Handles persistence without leaking ORM details |
| Delivery | FastAPI, WebSockets, Textual dashboard | Lets different clients share the same engine |
The storage choice is pragmatic too. Segment lists can live as JSON, which keeps the model simple without forcing a join table for every small targeting list.
Audit Logs Turn Every Toggle Into a Record
Audit logging is the feature that moves this from hobby project to operations tool. Every mutation writes old and new values, so the system keeps a paper trail of what changed and when.
| Capability | Simple flag tool | feature-flag-engine |
|---|---|---|
| Mutation history | Often omitted or implicit | Explicit audit log with old and new values |
| Operator trust | Harder to reconstruct after the fact | Each change is traceable |
| Production readiness | Depends on external tooling | Built into the control plane |
| Rollback confidence | Manual and uncertain | Faster to inspect and reason about |
That matters because flags tend to become blame magnets. When an incident happens, a good audit trail turns a guessing game into a reviewable sequence of events.
What It Is Competing Against
The right comparison is not a checklist of features. It is a philosophy of operation. Unleash, Flagsmith, Flipt, and GrowthBook are broader platforms with browser-first workflows and mature ecosystems. This repo is narrower, more terminal-centric, and more explicit about deterministic evaluation plus live propagation.
| Project | Admin experience | Rollout model | Real-time sync | Self-hosting | Distinctive angle |
|---|---|---|---|---|---|
| feature-flag-engine | Terminal-first Textual dashboard | Deterministic hashing plus segments | WebSocket fan-out | Yes | Compact control plane with clean boundaries |
| Unleash | Browser-first | Advanced targeting and rollout rules | Available through integrations | Yes | Broad enterprise feature management |
| Flagsmith | Browser-first | Targeting and environments | Supported | Yes | Product-focused flag management |
| Flipt | Web UI and API | Simple flag evaluation | Supported | Yes | Lean self-hosted flag service |
| GrowthBook | Browser-first | Flags plus experimentation | Supported | Yes | Experimentation-led feature management |
That positioning is the point. This repo is not trying to out-platform the incumbents. It is trying to make the control plane smaller, clearer, and easier to reason about.