The five UI items in TODO.md become an executable pipeline. Planning files only — no application code changed. - 01_severity_language: one Python threshold table + `severity` on the values the pages colour, plus the .hero style. Today the thresholds are magic numbers at six JS call sites (75/90 here, 80/92 there), which is exactly how "same colour, four meanings" happens. - 02_overview_fleet_and_alerts: /api/summary gains a derived `alerts` list; the page drops to fleet + warnings. Unconfigured sources must not alert — that is invariant 3, tested. - 03_system_per_host_panels: per-host hero cards, no per-core, shared multi-host charts and the detail picker deleted. The six card metrics already exist in the payload, so the first task pins them with tests instead of inventing fields. - 04_llm_chart_readability: charts.js already exports sparkline() and no page calls it — that is why the page reads flat. Port llm_monitor's drawSpark behaviour (fixed max, right growth, p95 foots) and keep the provenance tags. - 05_ups_hero_battery_glyph: charge/load/runtime hero + an SVG battery whose fill is the charge, verified against both real UPSes and the low-charge/on-battery states only the simulator can produce. PLAN.md records the ratified anchors and the deviation worth knowing: severity vocabulary is ok|warn|fail, matching dash.css and nut.py, not the "danger" wording in the proposal — one synonym for one colour. TODO.md cleared to a bare heading; the items are now phases. .gitignore stops ignoring all of .agents/ (the roadmap is project history) while keeping phase-sessions/ and pipeline.log out. deploy/nut-sudoers rewritten: its header still blamed a port mystery that is solved; the remaining need is reading upsd.users so adding a scoped dashboards user does not delete the accounts upsmon authenticates as. LLM-Generated: true
55 lines
2.9 KiB
Plaintext
55 lines
2.9 KiB
Plaintext
# driveripper-ops sudo rules for the homelab dashboards UPS work.
|
|
#
|
|
# Install (from the repo checkout, as a user with sudo):
|
|
#
|
|
# sudo visudo -cf deploy/nut-sudoers # syntax check FIRST
|
|
# sudo install -m 0440 -o root -g root deploy/nut-sudoers /etc/sudoers.d/dashboards-nut
|
|
# sudo visudo -cf /etc/sudoers.d/dashboards-nut # and check the installed copy
|
|
#
|
|
# Scope: absolute paths only (`secure_path` is in effect), wildcards only where the command
|
|
# is read-only or idempotent, and nothing here grants a shell, `sh -c`, an editor, or a
|
|
# client that can send instant commands.
|
|
#
|
|
# WHY THESE ARE NEEDED
|
|
# Reads already work with zero privileges — upsd on driveripper serves `LIST UPS` /
|
|
# `LIST VAR` unauthenticated (on port 3051, not 3493). What is missing is a upsd.users
|
|
# entry with `instcmds`, which is the only thing standing between the dashboard and its
|
|
# battery-test buttons. `tee /etc/ups/*` is already granted, but writing upsd.users blind
|
|
# would drop the accounts upsmon authenticates as — upsmon must run as a user with
|
|
# `shutdown`, so an overwritten file means no orderly power-down. So: read first.
|
|
|
|
# ---- already present on driveripper (used during setup) ------------------------------ #
|
|
# (root) NOPASSWD: /usr/bin/tee /etc/ups/* # write a config file
|
|
# (root) NOPASSWD: /usr/bin/cp /etc/ups/* /home/ops/* # copy one out (root-owned)
|
|
# (root) NOPASSWD: /usr/bin/systemctl start|restart|reload|enable *
|
|
# (root) NOPASSWD: /usr/bin/udevadm * # USB device discovery
|
|
# (root) NOPASSWD: /usr/bin/firewall-cmd * # only if upsd must serve the LAN
|
|
# (root) NOPASSWD: /usr/bin/dnf install * # nut, nut-client
|
|
|
|
# ---- read: what is actually configured, before anything is rewritten ----------------- #
|
|
Cmnd_Alias NUT_READ = \
|
|
/usr/bin/cat /etc/ups/*, \
|
|
/usr/bin/journalctl -u nut*, \
|
|
/usr/bin/systemctl status nut*, \
|
|
/usr/bin/upsdrvctl status, \
|
|
/usr/bin/upsc *, \
|
|
/usr/sbin/ss -ltnp, \
|
|
/usr/bin/lsusb, \
|
|
/usr/sbin/getenforce, \
|
|
/usr/bin/ausearch -m avc *, /usr/sbin/sealert -l *
|
|
|
|
# ---- write: add/replace a scoped user, then reload ----------------------------------- #
|
|
Cmnd_Alias NUT_WRITE = \
|
|
/usr/bin/tee /etc/ups/upsd.users, \
|
|
/usr/bin/tee /etc/ups/upsd.conf, \
|
|
/usr/bin/systemctl restart nut-server, \
|
|
/usr/bin/systemctl reload nut-server
|
|
|
|
# ---- deliberately NOT granted -------------------------------------------------------- #
|
|
# /usr/bin/upscmd, /usr/bin/upsrw — the dashboard's gated POST /api/ups/command is the
|
|
# ONLY command path in this project, by design
|
|
# systemctl stop * — stopping nut-server blinds the UPS view
|
|
# tee /etc/ups/ups.conf, upsmon.conf — driver instances and shutdown policy are the
|
|
# operator's, not the dashboard's
|
|
# sh, bash, vim, tee to anything else — no escape hatches
|