Skip to content

Live dashboard

The server serves a live, server-rendered dashboard at DASHBOARD_PATH (default /deploy/dashboard). It uses htmx with Server-Sent Events; the htmx assets are vendored, so it works offline. Cards refresh every DASHBOARD_REFRESH_MS (default 2000 ms).

The top navigation has five screens plus a light/dark theme toggle:

  • Live — the operational overview (cards described below).
  • Agents — a per-agent table: name, ip, connection uptime, heap, agent-reported version, and connected/disconnected status.
  • Config — the effective value of every configuration variable at runtime, with secrets masked.
  • Aliases — the list of alias files and, for a selected alias, its commands (agents / name / arguments) plus the raw YAML.
  • Log — a tail of the server log file (SERVER_LOG_FILE); shows an explicit reason when the file is unconfigured or empty.

The Live screen streams these cards over {DASHBOARD_PATH}/events:

  • Service — process uptime, heap usage, connected-agent count.
  • Agents — the connected agent ids.
  • Current task — the most recently executed deploy task and its state.
  • Issues queue — issue ids waiting to be processed (with an inline form to enqueue one).
  • Delivery — the durable Redmine/Telegram queues: pending, dead, sent, dead-lettered counts and send-latency percentiles (p50/p95/p99).
  • Agent logs — a live tail of recent command output from the agents.

The Issues queue card has a form that POSTs to {DASHBOARD_PATH}/issue with an issue_id field, enqueuing the issue exactly as the webhook would. The endpoint is method-agnostic so a reverse proxy may rewrite the POST to a GET without breaking it.

The live cards, the Log tail and the Agent logs tail are Server-Sent Events streams ({DASHBOARD_PATH}/events, /log/events, /agentlog/events). Each stream flushes its response headers immediately with a : connected comment, sends a : keepalive comment after 15 s of silence, and sets X-Accel-Buffering: no, so nginx defaults (proxy_read_timeout 60s, proxy_buffering on) work without tuning. A minimal location:

location /deploy {
proxy_pass http://127.0.0.1:9090;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off; # optional: X-Accel-Buffering already disables it per response
}

If the deploy server sits behind a second proxy, that proxy must also pass the stream through unbuffered (honour X-Accel-Buffering or set proxy_buffering off), or events arrive in bursts.