A stale venv after a code update (editable install + git pull) left
pyserial out of the environment, and the hard module-level import in
wireview.py took the entire web server down — even on machines without
a WireView device.
Guard the import: without pyserial the serial transport is disabled
(one-time warning, connect fails, reads return None) while the rest of
the server keeps running. The hwmon transport is unaffected.
Also simplify the except clauses to OSError (SerialException is an
OSError subclass) so they no longer reference the possibly-None module.
Standalone support for the ThermalGrizzly WireView Pro II without
depending on the external wireview_reporter exporter:
- wireview.py: serial protocol (STX/ETX + 16-bit CRC16-CCITT) with
vendor-data product identification, config version, UID, build
string, screen layout, and temperature/power/current sensor reads;
hwmon fallback with per-channel index resolution; udev-based
detection (vendor 0x2560 / product 0x0101) with fallback port
probing; serial read timeout and watchdog reconnect
- server.py: startup detection (root only), 1 Hz poller gated on
subscribed clients, WS 'wireview' channel, GET /api/wireview,
rejected-product memoization, stale-read race guard
- frontend: WireView tab (visible when a device is detected) with
live temperature/power/current cards, sparkline history, and
device info; nullable temp channels
- tests: 76 tests covering parser, CRC, fault classification,
hwmon resolution, JSON safety, and the serial transport against
a pty-based fake device
Verified live: tab appears with the device connected, disappears
when unplugged, reconnects on re-plug, no serial traffic when idle.
Adds an experimental power-cap mode using the undocumented RM ioctl
interface (based on panchovix's LACT PR #1205) to set power limits
below the VBIOS minimum (down to 30 W).
- hal/rm_power.py: RM ioctl power-cap read/write/reset + runtime probe
- limits.py: power_cap_mode (nvml/ioctl) with support detection
- config.py: persist power_cap_mode per GPU
- profiles: record/apply power_cap_mode
- server.py: POST /api/limits validates ioctl support (409 on failure)
- cli.py: profile save falls back to persisted mode
- client.py: power_cap_mode in Limits
- frontend: toggle + warning with panchovix attribution (LACT #1205)
- tests: test_rm_power.py (unit) + integration coverage
- Makefile: add test_rm_power.py to make test
Also includes automated linter reformatting (prettier, ruff, shellcheck,
isort, markdownlint) that the linter would apply anyway.
Security review findings, fixed and verified:
Critical
- Fix unauthenticated arbitrary file read: the SPA catch-all route
joined the raw URL path onto the dist dir without containment, so
encoded '..' segments (/%2e%2e/etc/passwd) leaked any file readable
by the root server. Resolve with realpath and reject paths outside
the dist dir (fail-closed 404).
High
- Daemon socket: serve_start no longer accepts caller-chosen
host/port. The socket is world-connectable (unprivileged CLI users),
so callers could previously rebind the root web server to 0.0.0.0.
The daemon now always binds the operator-configured address and
reports it in the response; the CLI warns on mismatch.
Medium
- Remove the per-request max_delta_khz override from the API: the
server-enforced safety cap is now authoritative. CLI direct paths
(write, profile apply, verify) honor the configured cap; --max-delta
still overrides for explicit root use.
- Snapshot restore: confine filepath to the snapshot directory
(realpath containment; blocks symlink escapes).
- Login lockout: honor X-Forwarded-For only for peers listed in the
new trusted_proxies config (rightmost untrusted hop), so the
per-IP lockout works behind a reverse proxy. Spoofed headers from
untrusted peers are ignored.
- /api/shutdown: new allow_api_shutdown config (default true);
shared systems can disable the API shutdown path.
TLS (opt-in, like auth)
- New ssl_certfile/ssl_keyfile config + CLI flags (serve start,
service install/configure, --no-ssl to disable). When active:
HTTPS for UI/API, wss:// for WebSockets, Secure session cookie,
CLI auto-switches to https://. Cert/key paths are validated up
front with a clear error instead of a silent uvicorn crash.
Tests & docs
- tests/test_security.py: standalone regression tests (no new deps)
covering SPA containment, snapshot containment, cap removal,
client-IP derivation, proxy normalization, TLS scheme detection,
and daemon host/port hardening.
- README + Usage-Guide: TLS section, new config keys, updated
security notes.