Web UI
The flow web subcommand — workflows overview, jobs list, run history, DAG flowchart, and restart-from-failed-step.
Run flow web from any host that can reach your RunLog backend.
The CLI launches a FastAPI server on 127.0.0.1:8080 by default
with no bearer-token requirement on loopback. For off-host access,
pass --token <value>: every /api/* route (except /api/health)
is then gated on Authorization: Bearer <token>. Binding to a
non-loopback address without a token logs a warning.
pip install "ematix-flow[web]"
flow web --port 8080 --module pipelines
# open http://127.0.0.1:8080/
The --module flag (repeatable) pre-imports a pipelines module so
the UI can render schedules, next-run times, and the DAG view
immediately — without waiting for a scheduler tick to populate
rich-history.
The SPA defaults to a dark, focused operator theme — quiet neutral surfaces, a single teal accent for links and active state, and monospace numerics for IDs / timestamps / durations. A theme toggle in the top-right corner of the nav swaps to a light variant; the choice persists across sessions. Four top-level views — Workflows, Jobs, Runs, and DAG — plus a per-run detail page.
Workflows — named groupings with inline DAG

The default landing page is per-workflow. Each card shows the workflow name, a trigger panel (cron / after-expression / on-message), member-job count, edge count, status summary, and an inline SVG flowchart laying out the member jobs left-to-right with arrows on every DAG edge. Reading the example:
data_quality_gatedemonstrates a compositeafter_exprtrigger — the panel renders the expression tree literally:After: ALL ( ● ingest_orders AND ANY ( ● feature_views_ready OR ● backfill_complete ) ). Each leaf carries its own state dot (green = ready, amber = pending, red = failed); composite nodes (ALL/ANY) carry the rolled-up aggregate state. Two-level nesting reads at a glance: the workflow fires afteringest_ordershas finished and either of the two readiness signals is met.feature_store_syncdemonstrates the simple cron case — trigger panel reads “Schedule: */15 * * * * (UTC)”.live_ordersis a streaming workflow — the● Streamingpill (pulsing dot, info-blue) replaces the trigger panel entirely. Streaming workflows don’t fire on a tick; they run continuously.warehouse_etl(below the fold) is a four-job diamond:ingest_ordersandload_dim_customerfan intobuild_sales_mart, which fans out topublish_metrics. The inline flowchart lays it out left-to-right.- Each card carries a kind pill (
Declared/Single/● Streaming), and the header right edge surfaces the job count plus edge count. Status summary pills (N succeeded/N running/N failed) appear inline when relevant. - Click any node in the inline flowchart → focused DAG view
(
#/dag/<job>). Click the workflow title → same.
This view exists to answer “what runs together?” at a glance.
Jobs — every individual job, filtered and sorted

The Jobs tab is the flat list of every individual job in the project — useful when you want to scan one specific job, regardless of which workflow it belongs to. One card per job:
- Name + kind of the job. Names link into the focused DAG view.
- Last-10 execution strip — one colored bar per execution, oldest left → newest right. Green = succeeded, red = failed, teal-pulse = running, amber = paused, dashed = no run yet. Bar height encodes duration with a square-root curve so a single outlier doesn’t flatten the rest of the strip.
- Next:
<UTC>for batch jobs (rendered in the job’s configuredtimezone=when set), or the● Streamingpill for streaming consumers. - For batch: median duration + a link into Runs filtered to that job. For streaming: a live Throughput X rps in (1m) / Y rps in (5m) · Batch cycle: A ms avg (1m) footer.
- Filter inputs (name substring / kind / latest status) and sort buttons (name / kind / status / next / duration) sit in the panel above the cards.
Clicking any square in the last-10 strip drills into that run’s detail page.
Runs — every execution record in a flat table

When you need to scan recent failures across every job at once, the
Runs tab is a flat table of every run record. Each row carries
a rounded status pill (● succeeded / ● running / ● failed /
● paused / ● requested / ● cancelled with semantic colors and
a leading dot), the pipeline name, started timestamp, duration,
attempt number, and the run ID. Column headers are clickable to
sort. Clicking a row opens that run’s detail page.
The default sort is Started ▼ (most recent first), which puts
live activity at the top — a ● running row for the streaming job
or a ● requested row for a pending manual rerun stays visible
without scrolling.
DAG — full cross-job flowchart with arrows

The DAG view (#/dag) renders every dependency edge in the
project as an SVG flowchart with cubic-Bézier arrows pointing from
upstream to downstream. The rank-as-column layout from earlier
versions is gone — topological order is now expressed by arrow
direction, not implied by column position.
Each node shows:
- Leaf name of the job in monospace bold (the qualifying
workflow.prefix is moved off the primary line so long names fit cleanly in the box). - Status · latest-run duration on the next line.
- Workflow prefix as a muted subtitle when the job is qualified
(
warehouse_etl,nightly_reporting, …); jobs that aren’t workflow-scoped fall back to their cron schedule here, and if neither is present the line is omitted. - Duration bar along the bottom edge, sized relative to the slowest node currently visible.
Hover any node to see the fully-qualified name, full schedule, status, and duration in a tooltip.
#/dag/<job-name> focuses the subgraph on a single job’s ancestors
- descendants, so you can answer “what does this job depend on, and what depends on it?” without scanning the full graph.
Failed nodes get a red border with a soft-red background tint. Running nodes get a teal border with a slow pulse animation. Pending nodes get a dashed border in the neutral surface color.
This view is useful for:
- Spotting orphan jobs (no upstream, no downstream — easy to forget when the project grows).
- Catching unintended cycles before they fail at registration time.
- A one-screen answer to “what runs after
ingest_orders?” without grepping decorator kwargs.
Failed run — Restart from failed step highlighted

A run that hit an error mid-DAG. The detail page exposes the exact
step that failed (transform_partition_1), the error
(transform_partition_1: ConnectionResetError during shuffle), the
started + finished timestamps, the attempt number, and a Task DAG
panel below with per-step status (succeeded steps in green, the
failed step in red, downstream steps still pending and dashed).
The Restart from step “transform_partition_1” button is one of
two restart actions surfaced on a failed batch run. Clicking it
POSTs to /api/runs/<run_id>/restart with
{ "from_step": "transform_partition_1" }. The scheduler picks up
the new “requested” row on its next tick (default 10s) and the
worker resumes the DAG from transform_partition_1 — earlier
steps’ outputs are reused, so the re-run is the failed step plus
everything downstream, not the whole job.
The Rerun from beginning button next to it (rendered in danger red as the more destructive option) kicks off a fresh run from scratch — useful when the source data has changed or the failure was non-deterministic.
For streaming runs, the same button reads Resume from watermark instead and resumes from the last committed watermark rather than a discrete step. Rerun from beginning is available on any terminated run.