Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
Dash — uptime observatory
A self-hosted uptime monitoring dashboard in a single binary. A Go check engine with SQLite persistence streams live status over SSE into a hand-built React frontend: three bespoke SVG circular visualizations with spring physics, full RTL (Farsi) support, and a shareable public status mode.
What it does
- Five check types — HTTP (status + latency), TCP port, TLS certificate expiry (days remaining), ICMP ping, and Docker (container running + healthcheck status via the daemon socket). Each target runs on its own interval.
- Flap detection — N consecutive failures before a target is marked down, M consecutive successes before it recovers. No alert noise from a single dropped packet.
- Incident grouping — one incident spans the entire down period, from the first failed check of the streak until recovery.
- Live stream —
/api/streampushes every probe and every confirmed state transition over Server-Sent Events. The UI never polls. - Every result persisted — timestamp, latency, status, error string in
SQLite (pure Go driver, no CGO), pruned after
history_days.
The frontend
Three circular instruments, all hand-built SVG + Framer Motion (no charting libraries):
- Status rings — one per target. The outer arc fills to the uptime percentage over the selected window (24H/7D) and springs to every new value; the inner brass gauge encodes latency on a log scale (1ms–2.5s); each completed probe lands as a visible pulse; confirmed state changes ripple outward. Click a ring and it travels into a detail view (shared layout transition) with a latency sparkline, a per-target day dial, and recent incidents.
- Orbital overview — every target orbits the system core at Kepler-flavored speeds (inner orbits run faster). Healthy bodies drift calmly with motion trails, degraded ones breathe, a down body breaks its orbit outward and flares red over the scar of its old path. The core carries fleet-wide uptime and a brass scanner hand.
- Day dial — a 24-hour clock face. Each of the last 24 hours is one segment at its true wall-clock position, colored by status. Hours without data stay hairline-thin. A brass hand marks now. One glance, one full day.
Plus: a boot sequence where the instruments draw themselves in, staggered;
prefers-reduced-motion collapses all motion to fully-formed static
rendering; فارسی / RTL is a first-class layout mode (mirrored layout,
Vazirmatn type, Persian digits and the Persian solar calendar via fa-IR);
dark and light themes (persisted, both tuned variants of the same brass
/ ink design language); and a Status mode — the same instruments
stripped to a clean, shareable public status page.
Continuous motion (the orbital field, the scanner hand) runs outside React entirely — one animation-frame loop mutates SVG attributes directly, so the page stays at full frame rate regardless of target count. No SVG filters, no blend modes on hot layers.
Build
Requires Go ≥ 1.22 and Node ≥ 20. The frontend must be built first because
the binary embeds it via go:embed.
# 1. build the frontend into web/dist
cd web && npm install && npm run build && cd ..
# 2. compile the single binary (web/dist is embedded)
go build -o dash .
The result is one self-contained file, dash. Copy it and a targets.yaml
to any Linux/macOS/Windows box — nothing else to install.
Run
./dash # uses ./targets.yaml, ./dash.db, :8080
./dash -config /etc/dash/targets.yaml -db /var/lib/dash.db -listen :9090
Open http://localhost:8080.
ICMP checks need raw-socket privileges. Run as root, or grant the binary
CAP_NET_RAW(setcap cap_net_raw+ep ./dash), or allow unprivileged ping (sysctl net.ipv4.ping_group_range="0 2147483647"). Other check types work without any privileges.
Configuration — targets.yaml
settings:
listen: ":8080" # bind address
db: "dash.db" # sqlite file
history_days: 30 # raw check retention
targets:
- id: example-web # unique slug (required)
name: "Example Website"
type: http # http | tcp | tls | icmp
url: https://example.com # http → url, tcp/tls → address, icmp → host
interval: 15s # time between checks (default 30s)
timeout: 5s # per-check timeout (default 5s)
fail_threshold: 3 # consecutive fails → down (default 3)
recover_threshold: 2 # consecutive oks → up (default 2)
expect_status: 200 # http only: require exact status
degraded_latency_ms: 1500 # mark degraded above this latency
- id: github-tls
name: "GitHub TLS"
type: tls
address: github.com:443
interval: 5m
warn_days: 21 # tls only: degraded when cert expires sooner
- id: my-container
name: "My Container"
type: docker
container: my-container # docker container name
interval: 30s
# socket: /var/run/docker.sock (default)
Docker checks read the daemon socket. Run the binary as a user in the
dockergroup (orSupplementaryGroups=dockerin the systemd unit). A container is up when running, degraded while its healthcheck reportsstarting, and failing when stopped orunhealthy.
API
| Endpoint | Description |
|---|---|
GET /api/targets |
Current state of all targets: flap-filtered status, last check, 24h/7d uptime, latency percentiles, open incident |
GET /api/targets/{id}/history?range=24h |
Time series: downsampled points plus 24 aggregate buckets (range: 30m, 24h, 7d, …) |
GET /api/incidents?range=7d |
Grouped incidents, ongoing first |
GET /api/stream |
SSE: snapshot on connect, then check per probe and state per confirmed transition |
Development
go run . & # backend on :8080
cd web && npm run dev # vite on :5173, proxies /api → :8080
Layout
main.go, webfs.go entrypoint + embedded frontend
internal/config targets.yaml parsing, defaults, validation
internal/checker the four probe implementations
internal/engine scheduler, flap detection, incidents, SSE hub
internal/store sqlite persistence (modernc.org/sqlite, no CGO)
internal/api REST + SSE handlers
web/ React + TypeScript + Vite + Tailwind v4 + Motion
