Skip to content

Repository files navigation

Nermal Dashboard

A single-page dashboard for one person's infrastructure. Home Assistant lights and climate, VPN tunnels, Railway deployments, OpenRouter credit, Navidrome, Docker containers and host metrics — grouped into visual regions on one uniform grid, served over Tailscale.

Tiles with history draw a faded 24-hour sparkline behind their numbers, and the header dropdown hides whole categories. No client-side data fetching, no build step. One request fans out to every service concurrently, and the page reloads itself every 60 seconds.

python demo.py --render --open     # see it immediately, with synthetic data

Windows users can double-click demo.bat instead — it creates its own virtualenv on first run.

Try the demo first

demo.py renders the real template against entirely invented data. No network calls, no credentials, no /proc, nothing personal — it runs anywhere, including Windows, and is the fastest way to see the layout and to record a screencast.

demo.bat / python demo.py --render --open writes demo.html and opens it
demo-serve.bat / python demo.py --serve --open serves on 127.0.0.1:9121 with the 60s auto-refresh live

The demo shows eight categories and deliberately includes unhealthy states — a failed deployment, a degraded tunnel, a nearly-exhausted API key, a low battery — so the colour language is visible rather than uniformly green. It also exercises the sparklines, including the two-series Aranet tile (temperature with CO₂ overlaid) and the VPN latency charts.

Running it for real

python3 -m venv venv
./venv/bin/pip install -r requirements.txt
./venv/bin/python dashboard.py          # http://127.0.0.1:9120

The host fetchers read /proc, df, uptime and ps, so the live dashboard is Linux-only. Bind it to your tailnet address and run it under systemd — see Deploying as a service.

Connecting your services

AGENTS.md is the setup guide: per-service playbooks with the exact credential, where it goes, how to verify it, and which tiles appear. It is written for an AI agent doing the wiring, which also makes it the most direct reference for doing it by hand.

Every integration is optional and independent. A service whose credential or path is missing contributes no tiles and its category disappears — so you can start with one and add the rest later.

Service Needs Shows
Home Assistant long-lived access token lights with brightness, CO₂/climate, weather, batteries, entity counts, media players
Railway account token projects, services, latest deployment status
OpenRouter provisioning key credit balance and spend, per-key limits and usage
Navidrome readable SQLite path artist/album/song counts, last scan, container state
Docker + Caddy docker group container inventory with status and ports
Hermes agent HERMES_HOME memory counts, gateway health, MCP errors
Host metrics nothing memory, load, disk, uptime
VPN servers not yet implemented — tile type ready, fetcher isn't (worked example)

Secrets are read from ~/.hermes, outside the repo, so they cannot be swept up by git add. See .env.example for the format.

Security

The dashboard has no authentication and displays account balances and infrastructure state. Bind it to a Tailscale address, not a public interface. If it must be reachable off-tailnet, put a real authenticating proxy in front of it.

How the layout works

Every tile is a direct sibling of one uniform CSS grid. Categories are not wrapper elements — they are SVG regions drawn over the cells, because wrapping is what creates ragged empty space. A packer assigns CSS order so each category occupies a contiguous, connected run of cells, widening a tile to absorb any row remainder so there are no holes. Column-count changes animate with FLIP.

templates/dashboard.html documents the reasoning inline, including the off-by-one in the connectivity bound (n > cols, not n >= cols) that would otherwise split a category into two islands. window.__regions().split must always be empty.

Sparklines sit outside that flow deliberately: each chart is absolutely positioned behind its tile's text, so a charted tile is exactly as tall as an uncharted one and mixed rows have no dead space. Hiding a category repacks the grid the same way a resize does. Both are covered in AGENTS.md.

License

GPL-3.0. You may use, modify and redistribute this, including commercially, but distributed modifications must also be GPL-3.0 and ship their source. The copyright line in each source header reads "Nermal Dashboard contributors" — replace it with your own name or handle if you prefer.

Layout of the repo

dashboard.py              fetchers, tile building, the one route
demo.py                   synthetic data; renders or serves the demo
templates/dashboard.html  the whole frontend — styles, tile types, layout engine
static/backdrop.jpg       faint background texture
nermal-dashboard.service  systemd unit template
AGENTS.md                 service-by-service setup guide
demo.bat, demo-serve.bat  Windows launchers (self-bootstrapping)
LICENSE                   GPL-3.0

About

Single-page dashboard for Home Assistant, VPN tunnels, Railway, OpenRouter, Navidrome, Docker and host metrics, served over Tailscale.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages