Skip to content

Live dashboard

Status: Accepted · Audience: Operator · Area: B — Running it


One screen that answers “what is my stack doing right now?”

Without it, the answer requires opening six web UIs: SABnzbd for Usenet progress, qBittorrent for torrents, Sonarr for the queue, Gluetun’s logs for VPN state, and a terminal for disk usage. Each holds one fragment; nobody holds the whole picture.

The dashboard’s value is precisely the fragments no single service can provide — that qBittorrent’s traffic is genuinely leaving via the tunnel, that imports are hardlinking rather than copying, that the disk will fill before the queue drains.

Running lemonfiber with configuration present and no subcommand opens the dashboard. It is the product’s front door for the operator.

Ordered by what the operator needs to see first, not by what’s easiest to render:

Section Shows
Health summary One line: everything fine, or n things need attention (G7)
VPN Tunnel state, exit IP and country, forwarded port, and whether the download client’s egress matches
Transfers Active downloads with name, protocol, progress, speed, ETA
Queue Per-*arr queue depth, plus anything stuck (C7)
Storage Free space, projected exhaustion, hardlink status
Services Per-service state, grouped by profile

The VPN row is placed second, above transfers, deliberately: it is the only item on the screen with consequences outside the machine.

The view updates about once a second. Input remains responsive during image pulls, log streaming and slow API calls. A dashboard that freezes while polling is worse than a static status command.

If live telemetry is unavailable — the Docker API unreachable while the CLI still works, or an *arr not responding — the affected panel says so explicitly rather than showing stale data as current or blank data as zero.

Stale, unknown, and zero are three different things and must never be rendered identically. “0 B/s” and “unknown” mean opposite things to someone deciding whether their download is stuck.

  • Egress comparison — the download client’s public IP against the VPN’s
  • Hardlink status — whether imports are linking or copying
  • Projected disk exhaustion — free space against queued content size
  • Cross-service stalls — downloaded but never imported

The operator can view any panel alone, for a narrow terminal or a focused question.

State Meaning
live Telemetry current, refreshing normally
degraded Some sources unavailable; affected panels marked
disconnected Docker API unreachable; control may still work via the CLI path
no-stack Configured but nothing running; offers to start a form
unconfigured No configuration; offers setup
Situation Behaviour
Docker API unavailable, CLI path working Enter disconnected, state that telemetry is off but control still works. Don’t exit.
A service’s API unreachable Mark that panel unavailable; leave the rest live. One failure must not blank the screen.
Terminal too narrow Degrade gracefully: drop columns by priority, never truncate mid-value or corrupt the layout.
Terminal resized Reflow without restarting or losing scroll position.
No downloads active Say “no active transfers”, not an empty box. Absence of data is information.
Very large queue Show the top n by relevance with a total count. Never attempt to render thousands of rows.
VPN not configured Omit the VPN panel entirely rather than showing a permanently red one.
Native-mode Jellyfin Show it, marked host-managed, with health if reachable.
Data unchanged between refreshes Don’t redraw unnecessarily; avoid flicker.
Very long service or release names Truncate with an unambiguous marker; full value available on focus.
Running over SSH on a slow link Reduce refresh rate rather than saturating the connection.
Clock skew between host and containers Compute durations from a single clock source; never render a negative ETA.
ID Requirement
B3-R1 Invoking lemonfiber with configuration present and no subcommand MUST open the dashboard.
B3-R2 The dashboard MUST show health summary, VPN state, transfers, queue depth, storage and service states.
B3-R3 VPN state MUST include exit IP, country, forwarded port, and egress-match confirmation.
B3-R4 Refresh MUST NOT block user input.
B3-R5 Stale, unknown and zero values MUST be visually distinct.
B3-R6 An unavailable data source MUST mark only its own panel, leaving others live.
B3-R7 Loss of the telemetry channel MUST NOT terminate the dashboard, and MUST be stated.
B3-R8 The dashboard MUST show hardlink status and projected disk exhaustion.
B3-R9 Narrow terminals MUST degrade by dropping lower-priority columns, never by corrupting layout.
B3-R10 Resizing MUST reflow without restart or loss of scroll position.
B3-R11 Absent data MUST be stated explicitly rather than rendered as an empty region.
B3-R12 Large collections MUST be truncated with a total count shown.
B3-R13 Any panel MUST be viewable in isolation.
B3-R14 Idle refresh MUST sustain 1 Hz below 2% CPU, and lemonfiber’s resident memory SHOULD stay under 50 MB.
B3-R15 Durations and ETAs MUST derive from one clock source and MUST never render negative.

This page lives in another repository Rendered from lemonfiber/spec at 1d10402, 2026-09-09. Read the source of this page