# Heartbeat · Pulse > The network's breathing — an append-only event log, a pulse worker, and the components that make its state visible at a glance. --- .. toc:: ## The event log is the truth Everything the heartbeat knows is an **event**: a small JSON record with a UTC timestamp, a `kind` (`tick`, `task_start`, `task_end`, `api_call`, `error`, `note`), a `phase` (`idle`, `working`, `error`), and a task slug. Events append to `data/heartbeat/events.jsonl` (never rewritten, except by the nightly prune) and mirror best-effort into Postgres. Every dashboard number derives from this log — nothing is stored twice, so nothing can disagree. On the hub, an `api_call` is an outbound HTTP probe (a satellite check, the wallet-provision POST) and carries `target / status / ok / latency_ms` in its extras. ## The dial The `HeartbeatPulse` component is pure DOM + CSS — three nested rings whose animation cadence and color follow the phase: .. exec::docs.heartbeat.pulse_examples ## The rhythm bar One cell per minute; the fill height encodes total events, the colored stack encodes kinds (error on top — the loudest thing wins the eye). Tooltips are CSS pseudo-elements, not component tooltips: 120 React portals re-rendering every second destabilized the original page, and a `data-tooltip` attribute costs nothing. .. exec::docs.heartbeat.rhythm_examples ## Process ownership One environment flag decides who runs the pulse: - **Dev** (no flags): the web process runs the tick loop, the job dispatcher, everything — off shared JSONL. - **Prod**: the web sets `HEARTBEAT_PULSE_WORKER=1` and defers; the `2plotai-pulse` sidecar owns the pulse, the APScheduler, and the dispatcher. The web reads the worker's state through the Postgres mirror — separate containers share no filesystem. .. admonition::Truthful by construction :icon: tabler:scale :color: teal The control board's liveness badge derives from the last tick's age in the shared log — not from any process's in-memory flag — so it tells the truth no matter which container did the ticking. ## Live updates The dashboard streams over Server-Sent Events on the FastAPI backend (`/api/heartbeat/stream`, owner-gated) and falls back to interval polling everywhere else. WebSocket callbacks stay off app-wide. .. llms_copy::Heartbeat · Pulse --- *Source: /docs/heartbeat-pulse*