Core Concepts

The mental model.

Three ideas explain how Flux works. Once you have them, every CLI command becomes obvious.

System shape
Client Request
HTTP / webhook / event
Gateway
captures request + metadata
trace_requests
Runtime
executes function in V8 isolate
runtime spans
Data Engine
intercepts DB writes
state_mutations
Your PostgreSQL
source of truth
Data recorded at each layer
trace_requests One row per request: id, tenant, function, status, duration, spans JSON
state_mutations One row per DB write: table, row id, old value, new value, request_id
runtime spans Nested span tree: function calls, tool calls, async steps, latencies
All data is keyed by request_id
Every row in every table carries the request_id that caused it. That single ID unlocks the entire debugging workflow.
1 Requests are executions

Every HTTP request is a recorded execution

When a request hits Flux, the gateway captures it and the runtime executes your function inside a V8 isolate. That entire round-trip — inputs, outputs, spans, tool calls, DB queries — is stored as a single execution record.

The execution record is identified by a request_id. Every CLI command that debugs a request takes this ID.

CLI commands that use request_id
flux why <id> — root cause
flux trace <id> — full span tree
flux trace debug <id> — step through spans
flux trace diff <a> <b> — compare two
execution record
$ flux why 550e8400

  Execution  550e8400
  POST /signup  →  function: create_user
  2026-03-10 14:22:31 UTC  ·  44ms

  Spans
  create_user          44ms
    db.insert(users)    3ms
    stripe.create       timeout

  Status  500 — Stripe API timeout
2 Data changes are mutations

Every database write is a logged mutation

When your function writes to PostgreSQL, Flux's Data Engine intercepts the write and records it in the mutation log. Each entry carries the row that changed, the old value, the new value, and — critically — the request_id that caused it.

This makes your database auditable by default. You never need to write audit_log tables manually.

CLI commands that query mutations
flux state history <table> — row history
flux state blame <table> — who changed it
mutation log
$ flux state history users --id 42

  Row  users / id = 42
  ────────────────────────────────────────

  2026-03-10 14:20:11  req:4f9a3b2c
  + email    → alice@example.com
  + plan     → free

  2026-03-10 14:22:31  req:550e8400
   plan     → null (Stripe timeout rolled back)

  2026-03-10 14:23:18  req:7b1d3f9a
   plan     → pro
3 Everything is traceable

Every layer emits spans — automatically

The gateway, runtime, data engine, tool calls, async jobs — every layer in Flux emits spans without any instrumentation from you. Those spans are assembled into a trace graph keyed by request_id.

The trace graph is not just for reading. You can replay it, diff it against a different request, bisect your commit history with it, or step through it interactively.

CLI commands that use the trace graph
flux incident replay — safe re-execution
flux bug bisect — find breaking commit
flux explain — AI analysis
trace graph
$ flux trace 91a3f

  Trace Graph  91a3f
  POST /create_order · 200 OK · 194ms

  gateway                         2ms
  └─ create_order                 8ms
     ├─ db.insert(orders)          4ms
     ├─ stripe.charge             180ms
     └─ slack.notify                 429

  ── Spans: 5  ·  Errors: 1  ·  DB writes: 1
4 Deterministic replay

Production can be replayed safely

Because every execution is recorded — inputs, state, secrets, timing — Flux can re-run a production time window against your current code with side-effects disabled. Emails won't send. Payments won't process. Slack won't fire.

This is the Replay Engine. It is how you test a fix against exactly the production traffic that caused an incident, without touching production.

How replay safety works
Each replayed request carries x-flux-replay: true
Your functions can check this header to disable side-effects
The runtime also suppresses outbound webhooks automatically
replay engine
$ flux incident replay --window 14:20-14:25

  Replay Window  2026-03-10 14:20 → 14:25
  3 requests captured

   Replaying 4f9a3b2c200  82ms
   Replaying 550e8400500  44ms
   Replaying 7b1d3f9a201  91ms

  Side-effects disabled: email, Slack, Stripe
  x-flux-replay: true sent on each request
   1 failure reproduced. Run flux why 550e8400 to debug.

Ready to see it in action?

The quickstart puts all four concepts into practice in about 5 minutes.