Commit Graph
179 Commits
Author SHA1 Message Date
Mitchell-JohnsonandClaude Fable 5.1 a446a03b7d Bug bash 3: solid HUD dialogs, remote stacks deployed from the hub's shell, stopped-on-purpose is not down, the New stack dialog keeps what you typed
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-20 12:30:15 -07:00
Mitchell-JohnsonandClaude Fable 5.1 634e286796 Docker: delete volumes nothing uses, prune anonymous ones; images marked in use, remove unused
A named volume is deleted by typing its name, one at a time; the API wants it named
twice; nothing is forced. Prune touches anonymous volumes only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-20 08:01:57 -07:00
Mitchell-JohnsonandClaude Fable 5.1 c017b422d5 Docker: new stacks and containers, stack down/delete, container remove, image pull/remove/prune
A new stack is a folder in a home (where stacks are already kept: Dockhand's group here,
~/stacks elsewhere) with compose.yaml and an optional root-only .env, validated by compose
before it is kept and deployed the way its neighbours were — inside dockhand, or to a node
over tcp from the hub. New container is the same dialog on a form that writes the compose.
Homes are scanned, so undeployed stacks stay listed. Delete moves a stack's files to
.hamlab-trash; nothing is forced, and volumes and data are never touched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-20 07:50:43 -07:00
Mitchell-JohnsonandClaude Fable 5.1 df6c1dcebf Login: no title under the logo — the logo has the name (it is the image's alt text now)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 19:43:44 -07:00
Mitchell-JohnsonandClaude Fable 5.1 3a882e2d84 New app icon and login logo: the HamLab penguin
Every icon is rendered from web/scripts/logo-source.png by scripts/icons.mjs: trimmed to
the artwork's own edge, transparent corners for the browser icons, a dark ground from the
artwork's palette for iOS (which paints transparency black) and for Android's maskable
icon, where the whole artwork sits inside the 80% safe circle. The login screen no longer
crops the logo to a circle or repeats the wordmark. Service worker cache bumped so
installed apps fetch the new files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 19:41:47 -07:00
Mitchell-JohnsonandClaude Fable 5.1 1d48267d90 Stacks: remote stacks editable from the hub, .env editing, deploy diff, image update checks; a gone container says so
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 16:42:54 -07:00
Mitchell-JohnsonandClaude Fable 5.1 eddba4250b Bug bash 2: 11 bugs in Stacks, Alerts, Docker, skins and the installer, fixed and verified (BUGS.md)
Stacks: deploy with every compose file (labels from inspect, not comma-joined ps);
refuse a stale overwrite; empty list without Docker. Alerts: firing state survives a
restart without re-announcing; vanished alerts are recorded as cleared with the reason;
rule values validated; 200 rules. Docker: 404 for a removed container's logs. Skins: four
palettes lifted to readable contrast. Phone: bigger toggles and swatches. Installer:
deploy republishes; an older published build is never installed silently.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 15:59:51 -07:00
Mitchell-JohnsonandClaude Fable 5.1 9c4f9dfa9f Docker: a Stacks section — edit a compose file, validate, deploy; fix the sideways scroll on phones
Stacks are found from container labels; the file is resolved on the host or through an
orchestrator's mounts, and compose runs where the file makes sense (inside Dockhand here),
so a redeploy is identical to how the stack was deployed. Save validates with compose and
keeps a .hamlab-bak; Deploy / Update / Restart / Stop run as followed tasks.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 15:23:50 -07:00
Mitchell-JohnsonandClaude Fable 5.1 08fb9d6e38 container.<name>.down: should be up and isn't — not merely stopped; the default alert watches that
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 15:10:17 -07:00
Mitchell-JohnsonandClaude Fable 5.1 cb1617c2b8 Alerts tab, Docker tab, nine new skins including HUD, a colour for every machine
Alerts: rules over sensors, watched fleet-wide from the hub; ntfy and webhook channels;
firing count on the rail. Docker: containers by stack with live use, start/stop/restart,
following logs, inspect, images/volumes/networks. Skins: HUD, Nord, Gruvbox, Catppuccin
(+Latte), Dracula, Tokyo Night, Solarized (+Light). Accents: per-machine colour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 15:07:15 -07:00
Mitchell-JohnsonandClaude Fable 5.1 63fcba6077 Rail: an outline clock for Cron instead of the ⏱ emoji
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 12:06:15 -07:00
Mitchell-JohnsonandClaude Fable 5.1 4542fa6c99 installer messages fit 80 columns
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 11:14:16 -07:00
Mitchell-JohnsonandClaude Fable 5.1 d9470bbfe1 install.sh: every line fits 80 columns; the closing card is a tidy table
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 11:13:27 -07:00
Mitchell-JohnsonandClaude Fable 5.1 3c0c504e35 README: the public install one-liner
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 10:57:31 -07:00
Mitchell-JohnsonandClaude Fable 5.1 e75fa0d759 install.sh: an interactive installer — user, address, HTTPS, firewall, password — that still runs unattended; make publish
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 10:56:42 -07:00
Mitchell-JohnsonandClaude Fable 5.1 a838adcb27 Machine menu shows each machine's address instead of its load
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 10:45:24 -07:00
Mitchell-JohnsonandClaude Fable 5.1 d3f2a83249 Terminal: Ctrl+C copies a selection (else ^C), Ctrl+V pastes, over http too; drop Log out everywhere
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 10:44:17 -07:00
Mitchell-JohnsonandClaude Fable 5.1 d7ed4d8549 Quality of life: in-app dialogs, selections in the URL, editable widgets, token dialog, gallery search, status history, stream errors that say why, tab reordering and shortcuts, log out everywhere
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 10:34:59 -07:00
Mitchell-JohnsonandClaude Fable 5.1 8ee2a2d293 Bug bash 2026-09-19: 17 bugs fixed and verified (BUGS.md)
Widgets: a bad pattern no longer silences every widget; malformed layouts load.
Shell: lost session returns to login at once; tab titles; Escape closes menus and dialogs.
Files: names are one path segment; unsaved edits ask before page/machine/tab changes;
only folders that opened are remembered. Terminals: unique default names; 64-char names.
API: queries capped at 10 000 values; 32 streams per client, 512 in all; only
/embed/widget is frameable. ZFS sensors: one unprivileged zpool list, no sudo journal spam.
Phone: 36 px inputs; dataset rows as wide as their cells. Machines: valid name pattern.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 10:18:23 -07:00
Mitchell-JohnsonandClaude Fable 5.1 36de4ad4a1 external demo: public-token guidance
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 09:14:30 -07:00
Mitchell-JohnsonandClaude Fable 5.1 47b21a6eca Hub proxy: don't relay a node's policy headers on top of the hub's own
Access-Control-Allow-Origin arrived twice on /api/nodes/<m>/sensors, which a
browser on another origin refuses. Also: demo lists say when the token hides them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 09:11:35 -07:00
Mitchell-JohnsonandClaude Fable 5.1 62605d57eb Widgets tab links the external demo when config.json names one (demo_url)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 08:58:36 -07:00
Mitchell-JohnsonandClaude Fable 5.1 65a9888784 Sensor API answers other origins (token only); external demo page
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 08:49:59 -07:00
Mitchell-JohnsonandClaude Fable 5.1 0b0b2897cb Rail: Widgets sits under Machines
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 08:37:52 -07:00
Mitchell-JohnsonandClaude Fable 5.1 b1c1404181 Widgets tab: every widget type and every sensor, live, one of each
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 08:29:15 -07:00
Mitchell-JohnsonandClaude Fable 5.1 4c2eb57ea6 Sensors: per-service and container sensors, pattern-limited tokens, fleet query, steady sampler
- service.<unit>.active|state for services that are enabled, up or failed
- containers.* and container.<name>.running|state|health|mem|cpu (cgroup reads, no docker stats)
- sensors:read tokens can be limited to ID patterns, enforced on every endpoint and,
  through the hub's filter header, on managed machines; the header can only narrow
- /api/fleet/sensors/query: one shaped query, every machine, one request
- the sampler waits what's left of the interval, so 250 ms means 4 a second
- Monitors asks for the families it draws instead of *
- unpowered hwmon sentinels (-93 °C) are not temperatures

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 08:08:44 -07:00
Mitchell-JohnsonandClaude Fable 5.1 4ddf10be51 The full switch: Monitors and Disks read only the sensor API; /api/metrics retired
- stock Monitors modules fed by one sensor stream, the catalog and one batch of
  histories (GET /api/sensors/history?ids=…), through sampleAdapter.ts
- the stream closes while the page is hidden and refills its curves on return
- Disks tab live rates from the stream
- new sensors: hamlab.sample_interval, ups.last_event
- stream: asking for about the sampling rate means every sample
- NUT runtime rounded at the source

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 07:52:14 -07:00
Mitchell-JohnsonandClaude Fable 5.1 f01175ceca Embed: bare=1 is really transparent
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 07:40:36 -07:00
Mitchell-JohnsonandClaude Fable 5.1 b05a053920 Sensors: read-only tokens, live stream, Prometheus, status sensors, widgets, embeds
- sensors:read tokens open the sensor endpoints (here and on managed machines
  through the hub) and nothing else; full tokens are never accepted from a URL
- /api/sensors/stream: a shaped query that keeps answering, over SSE
- /metrics: Prometheus text format
- services, updates, ZFS and SMART as background status sensors
- widget library over the sensor API; custom widgets on the dashboard
- /embed/widget: one live widget in its own bundle, the only frameable path
- Dashboard tab is now Monitors

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 07:37:10 -07:00
Mitchell-JohnsonandClaude Fable 5.1 468f45251d priv: a failed root probe is retried, not cached for the life of the daemon
The Mac mini's daemon started while the machine was still booting, sudo -n true
took longer than the 3 s timeout, and sync.Once kept the answer: no root, so the
next self-update could not replace the binary.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 07:05:32 -07:00
Mitchell-JohnsonandClaude Fable 5.1 43bf787696 Sensors: one standard read interface — catalog, single value, history, shaped query
Every number the collector samples becomes a sensor with a stable dotted ID
(cpu.core.3.usage, fan.nct6775.fan2, fs.root.used_pct). Clients discover them
from the catalog and ask for exactly what they want, including as a shaped
query: send the object you want, get it back with the values filled in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 07:04:03 -07:00
Mitchell-JohnsonandClaude Fable 5.1 01aeb226ea Disks: show eMMC firmware revision as text when it is text
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 04:50:05 -07:00
Mitchell-JohnsonandClaude Fable 5.1 232163ff4c Disks: eMMC is one device, not three, and reports wear from its own registers
mmcblkNboot0/boot1/rpmb are hardware areas of the chip; hide them on Disks and
Dashboard. Badge eMMC/SD instead of SSD. smartctl cannot talk to MMC, so read
life_time and pre_eol_info from /sys instead of showing its error.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 04:48:52 -07:00
Mitchell-JohnsonandClaude Fable 5.1 488d929733 Portable install: the binary installs itself; static builds on five architectures; root via sudo, doas or uid 0; every package manager; older systemd; no lsblk
- hamlab install / uninstall (internal/setup): systemd or OpenRC, elevates through
  sudo or doas, runs as the invoking user, sets or generates the password, names only
  journal groups that exist, waits for /healthz, reports an active firewalld/ufw.
  --purge and the data chown never touch a directory that isn't Ham Lab's alone.
- install.sh: curl-able bootstrap (arch detection, SHA256SUMS, offline beside the
  binaries, noexec /tmp fallback). make release builds dist/ for amd64, arm64, arm
  (ARMv6), 386, riscv64. Every build is CGO_ENABLED=0; the native one used to link glibc.
- internal/priv replaces every literal `sudo -n`: uid 0, sudo or doas.
- Updates acts on dnf, zypper, pacman and apk as well as apt; dnf shows installed versions.
- Services falls back to the plain systemctl table before systemd 246 and says so on
  hosts without systemd. Disks falls back to /sys/block without lsblk.
- Listener binds with IP_FREEBIND, so a LAN address that isn't up at boot is no failure.
- Self-update: the new build must pass `check` against the live config, is put in
  place by atomic rename, and the daemon re-execs itself (same under any supervisor).
- State and config writes are fsynced; a corrupt state.json is set aside, not fatal.
- Terminal shutdown no longer empties the saved list, so names come back after a
  restart by design rather than by winning a race.
- Config default is ~/.config/hamlab; an existing ~/apps/hamlab is still found.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 23:00:20 -07:00
Mitchell-JohnsonandClaude Fable 5.1 91b7f4f009 Terminal sessions move from the rail into a tab strip on the terminal page
The rail is now a flat list of pages; Terminal is one of them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 22:10:22 -07:00
Mitchell-JohnsonandClaude Fable 5.1 7eb00d089f BUGS.md: accurate reap verification; note the resurrect fix
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 18:04:20 -07:00
Mitchell-JohnsonandClaude Fable 5.1 1ee4673d7c Don't resurrect exited terminals on daemon restart
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:53:29 -07:00
Mitchell-JohnsonandClaude Fable 5.1 44e85b768f BUGS.md: fix pass outcomes
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:53:05 -07:00
Mitchell-JohnsonandClaude Fable 5.1 c1805f6ad5 Bug 14: stream journal reads instead of buffering them; soft memory limit for the GC
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:51:09 -07:00
Mitchell-JohnsonandClaude Fable 5.1 03be65ee99 pprof behind the login for memory investigation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:47:16 -07:00
Mitchell-JohnsonandClaude Fable 5.1 f093b322ee Bug bash fixes: hash routing + back button, hub-only pages reset on machine switch, exited terminals reap after 10 min, self-update refuses while busy unless forced, touch-sized controls, power confirm by machine name, down interfaces collapsed and empty modules hidden, load label, offline backoff, cron log newest first, procs empty message, timer timestamp parsing
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:46:16 -07:00
Mitchell-JohnsonandClaude Fable 5.1 27f9665606 Bug bash fixes: special-file reads, per-client lockout, prefs validation, security headers, kernel-thread guard, login feedback, error text; plus journal-quiet sudoers + journal groups, upload overwrite guard, machines pattern, dnf freshness, cron/services speed, clean version
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:41:52 -07:00
Mitchell-JohnsonandClaude Fable 5.1 57c58ebffb Bug bash 2026-09-18: findings list (discovery only)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 17:34:55 -07:00
Mitchell-JohnsonandClaude Fable 5.1 0af24f41c3 Disks: one card per physical drive (NVMe namespaces folded under their controller), partitions summarised on one line instead of listed
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 14:18:20 -07:00
Mitchell-JohnsonandClaude Fable 5.1 a372378098 Mac mini support: arm64 cross-build installed beside the binary and pushed by arch; ARM machine name from the device tree
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 14:13:09 -07:00
Mitchell-JohnsonandClaude Fable 5.1 b6502ba362 Disks: one toggle opens SMART for every drive, remembered per device
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 13:41:38 -07:00
Mitchell-JohnsonandClaude Fable 5.1 4b8a2b15ed Refresh rate is honoured exactly everywhere: no list floor, sampler up to 10 s, server caches reduced to coalescing only
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 13:32:36 -07:00
Mitchell-JohnsonandClaude Fable 5.1 bbf0f2580f Global refresh rate in Settings drives every live view and each machine's sampler; per-page pickers removed
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 13:27:13 -07:00
Mitchell-JohnsonandClaude Fable 5.1 a4f39859b6 Settings page: themes (Dark, Light, Cyberpunk, Retro) with xterm palettes and hub-synced choice, change password, access tokens, about
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 13:14:01 -07:00
Mitchell-JohnsonandClaude Fable 5.1 254e0cdd58 Update from hub: node self-update endpoint, hub push per machine / all, push-nodes CLI run by make deploy, outdated badges + Update buttons on Machines
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:04:31 -07:00