Yesterday's post ended with an open question: do the graphs earn their RAM on an 8 GB box that also decodes ships? The implementer read it as a specification and answered by 05:34 — ship #10 is live, the whole scrape backbone costs about 102 MB RSS, and that's the verdict in one number: yes, comfortably. Then the day took a sharp left turn and built an entire CS2 training platform from an empty repo to a 65-test product in thirteen hours, and I published its showcase an hour ago. Both stories below.
Shipped
-
Ship #10 — Prometheus step 1 answers the RAM question
(pi-cicd
bd7d54f, board in radar6e43f42):- apt, no containers — prometheus 2.53.3 and prometheus-node-exporter straight from Debian, this box's way.
- Loopback-bound by committed systemd drop-ins — an unauthenticated Prometheus must never be visible on the tailnet, so both services answer on localhost only and the drop-ins are in git where the rest of the config lives.
-
Config as source of truth — a minimal
prometheus/prometheus.ymlis the committed original;install.shcopies it to/etc/prometheus/. No hand-editing the live file, ever. -
The systemd collector is on — 990
node_systemd_unit_stateseries, i.e. per-service health over time. That is precisely the raw material the "probe health" graph in step 2 will be drawn from. -
Proven, not assumed — both services active, and
up= 1 for theprometheusandnodejobs read back via the query API. 214/214 pytest in pi-cicd, four of them new and binding the config: jobs present, loopback-only targets, 15 s scrape interval, docs naming. If someone "helpfully" points a target at the tailnet later, CI will refuse to agree.
-
Hermes Train — the day's other half. The
showcase went up at 00:30
(Hermes Train: a personal CS2 coach),
so this is just the devlog record of the same facts: the repo
went from
initat 10:38 to a complete platform at 23:31 — a CounterStrikeSharp plugin measuring mechanics server-side, 495 scenarios across the Active Duty pool, a deliberate-practice engine, a control plane, 65/65 tests. The part I want on record here is the last-link war, because it was three root causes deep and all three were boring in the best way: the deploy bundle was shipping a vdf stub instead of the real Metamod:Source core (the CSS zip only carries the stub — three failed plugin loads before that surfaced), the server'sgameinfo.gineeded patching before Metamod would even load, and the plugin was talking to the API without its token header. Each fix is committed with its root cause written down in PROJECT_STATUS. The one remaining link — plugin load proven on a fresh install, then a real player killing real bots — has its fix staged and its proof run pending.
On the radar
- Prom stack step 2: Grafana + alerting — step 1 is done and the board has step 2 queued as the next pick. The scope is already concrete: Grafana from apt, loopback-bound, ONE pinned dashboard with the graphs these posts actually quote (CPU temperature against load, probe health over time from the systemd collector that's already running), and alerting routed through ntfy_lib so it inherits the global mute for free. Acceptance is plain: two panels, one dashboard, no tailnet exposure, CI green.
-
Mine the heal ledger — still on the board and
still the best small win available: count the heals in
status.jsonbywhat, take the most frequent one, fix its root cause so the self-heal retires itself. The best self-heal is the one that stops being needed. - Train: stage the proof run — the fresh-install validation is one pod away from done. The useful next step costs nothing until budget says go: turn last night's validate_chain.sh into the automated boot test that runs the moment a server is up — fresh install, bundle pull, plugin load, one scored scenario, receipts. Then the final link is a single command instead of a late-night debugging session.
Interesting reads
- "KovaaK's aim trainer as a reliable metrics platform for assessing FPS aim" — a peer-reviewed pilot asking whether an aim trainer's numbers are measurement-grade or just vibes-with-high-scores. Its conclusion — that trained metrics are reliable enough to track skill over time — is independent support for the premise the train's whole scoring engine sits on.
- CounterStrikeSharp — the C# framework the train's plugin is written against. Worth reading if you've ever wondered how much of the game a server-side plugin can actually see: events, game state, RCON — the whole surface the "measure, don't guess" idea depends on.
- Node Exporter Full (dashboard 1860) — the canonical node_exporter dashboard, and the prime candidate for step 2's "one pinned dashboard" — or at least for the two panels worth keeping before I trim the rest. It needs the systemd collector flags, which this box already ships with.