Local GPU + fleet dashboard for NVIDIA DGX Spark (Grace Blackwell, unified memory) — and any Linux box with an NVIDIA GPU. Pulse, fleet cards, a read-only services board. Nothing phones home.
Default UI: http://127.0.0.1:8085/
This public tree is the portable core: collectors, the UI, and a read-only service board. It runs standalone. The hero screenshot is a live dual-Spark run on the author’s box (your clone shows your hosts and units, not that fleet).
Published default is main. Ignore leftover master if GitHub still lists it — that branch is not this dashboard.
| What it is | Spark Console — a local GPU/system dashboard for NVIDIA DGX Spark: live graphs, model inventory, endpoint health, and a read-only service board. |
| What it’s for | See GPU, memory, model and service health in one page on localhost — without phoning home, and without a cloud account. |
| How to use it | ./setup.sh → open http://127.0.0.1:8085/. Stop with ./stop.sh. |
git clone https://github.com/Coinupbtc/spark-console.git
cd spark-console && ./setup.sh
# open http://127.0.0.1:8085/git clone https://github.com/Coinupbtc/spark-console.git && cd spark-console
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
./start.sh./setup.shRuntime data stays in local data/ (gitignored). Nothing phones home.
| Path | What |
|---|---|
console.html |
The console UI |
server.py |
Read-only HTTP API (see the route list in its docstring) |
collector.py |
Performance snapshots → data/ (CSV + latest JSON) |
services_lite.py |
Service board driven by your services.json |
remote_node.py |
Optional second node over SSH |
cluster_metrics.py |
Two-node metrics + degraded-node alerting |
baseline_summary.py |
Percentiles, availability, longest-run report |
todos_lite.py |
Local scratch list for the console panel |
setup.sh / start.sh / stop.sh |
One-command lifecycle |
Copy the example and edit it — the board is empty until you do:
cp services.example.json services.jsonEach entry needs an id plus at least one of unit, port, or probe_url:
{"services": [
{"id": "ollama", "label": "Ollama", "group": "inference",
"unit": "ollama.service", "port": 11434, "critical": true}
]}critical: true raises an alert when that service is not running. The board is read-only — the console reports state and never starts or stops anything.
Point it at another machine over SSH and its metrics join the graphs:
export [email protected] # or NODE2_SSH_ALIAS=mynode from ~/.ssh/config
export NODE2_SSH_KEY=~/.ssh/id_ed25519 # pin an identity (needed under systemd)
./start.shUnset, the console is simply a single-node dashboard — no errors, no alerts.
| Var | Default | Meaning |
|---|---|---|
PORT / HOST |
8085 / 127.0.0.1 |
Bind address |
CONSOLE_ALLOWED_HOSTS |
— | Extra Host values to accept (see Security) |
NODE2_HOST / NODE2_SSH_ALIAS |
— | Enable the second node |
NODE2_SSH_KEY |
— | SSH identity for the second node |
NODE2_ENDPOINTS |
8000,8080,11434 |
Remote ports probed for /v1/models |
SPARK_CONSOLE_SERVICES |
./services.json |
Service board config path |
SPARK_CONSOLE_SYSTEMD_SCOPE |
--user |
Use --system for system units |
NOTIFY_HOOK |
— | Executable taking --plain <msg>, paged when node2 degrades |
SPARK_FABRIC_IP |
— | If set, also probe :8800 on that address (no baked-in LAN IP) |
DGX_DATA_DIR |
./data |
Where snapshots and the CSV live |
The console binds 127.0.0.1 only, which is necessary but not sufficient — a
localhost HTTP server is still reachable from any page your browser loads. Two
defences are built in:
Hostallowlist — blocks DNS rebinding, where a page onevil.comre-points its own hostname at127.0.0.1to become same-origin with the console and read every endpoint. ForeignHostheaders get a400.Origincheck on writes — a cross-originPOSTwith no custom headers is a "simple request", so no CORS preflight would stop it. ForeignOriginvalues get a403. Requests with noOrigin(curl, scripts) still work.
To reach the console from another machine, prefer an SSH tunnel — the Host
stays localhost, so nothing needs configuring:
ssh -L 8085:127.0.0.1:8085 user@your-sparkIf you must serve a real hostname, add it explicitly:
CONSOLE_ALLOWED_HOSTS=spark.lan ./start.shThis tree has no start/stop, restart, model-switch, kill, or launch HTTP
routes. Pulse, fleet cards, and the service board are read-only. POST /api/todos
is a local scratch list; POST /api/refresh re-runs the collector.
./.venv/bin/python -m pytest tests/ -qMIT
