Commit Graph
55 Commits
Author SHA1 Message Date
Mitchell-JohnsonandClaude Opus 5 fd88c4ff78 A KVM tab: paste an address, get the device's own page
Ham Lab proxies each KVM from its own origin, which is what makes the simple version
possible. A KVM that speaks only plain HTTP would otherwise be blocked outright as mixed
content in a TLS console; served from here it loads, it is behind this login rather than a
second one, and the iframe is same-origin.

Nothing in it knows anything about any particular device. The prefix /kvm/<id>/ is only the
entry point — a device UI asks for its assets by absolute path, which no prefix can rewrite
away, so any request Ham Lab does not serve itself is routed back to whichever KVM the
asking page belongs to, worked out from the Referer and from a cookie when there is no
Referer, as on a WebSocket upgrade. Ham Lab's own routes always match first, so a device can
never shadow the console, and the fallback is gated on a live session.

The list is hub-side on purpose: a KVM earns its keep when the machine is down, which is
when that machine's own Ham Lab cannot answer.

On the keyboard, a page cannot forward keys into an iframe and does not need to — one with
focus receives them directly. Fullscreen plus navigator.keyboard.lock() is what takes
Escape, Tab and Ctrl-W back from the browser; that is Chromium-only and the tab says so
rather than pretending otherwise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 16:39:27 -07:00
Mitchell-JohnsonandClaude Opus 5 daf33a44b2 The datasets table is a snapshot explorer, and snapshots can be deleted
Renamed Datasets to Snapshots and turned the columns around to match: how many each
dataset has and what they cost come first, the dataset's own size after. Opening one lists
its snapshots with when it was taken, when it expires — read from the TTL in its own name,
so no server call is needed — its size, and a Delete button. Deleting takes the same-named
snapshot on descendant datasets too, which is how a recursive schedule made it, and says so
before it does anything.

Dropped "What would it delete?" from the schedule cards, and the endpoint behind it. Run
now says plainly that expired snapshots go at the same time, which is the whole of what the
preview was telling anyone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 13:16:13 -07:00
Mitchell-JohnsonandClaude Opus 5 09b7558d44 Snapshot schedules are real cron jobs, written and owned by Ham Lab
A schedule is now a line in /etc/cron.d/hamlab-zfs, which Ham Lab owns and rewrites whole
whenever the schedules change; the line calls `hamlab zfs-snapshot <id>` as the account Ham
Lab runs as, reaching zfs through sudo exactly as the daemon does. The daemon's own ticker
is gone. Snapshots are backup-shaped and should not stop because a web console is down,
being upgraded, or uninstalled — and a cron line is greppable by anyone looking at the
machine the normal way, which a schedule hidden inside a daemon is not.

A cron expression is now the single description of when a schedule fires: the four common
shapes render to one, a custom schedule states it outright, and the next run is read back
with the same evaluator the Jobs tab uses on everyone else's lines. So the page, the file
and the prediction cannot disagree.

Schedules gain a custom cron expression and an optional snapshot name prefix. What a run
did moves out of the policy into zfs-runs/<id>.json beside the config, because the cron job
is a separate process and must never write state.json behind the daemon's back.

The detected external job can be adopted: its schedule, retention and dataset are read out
of the cron line into a new Ham Lab schedule, ready to check rather than retype.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 13:09:43 -07:00
Mitchell-JohnsonandClaude Opus 5 7d0ec5e018 ZFS gets its own tab, with scheduled snapshots and a retention period
Pools, datasets and snapshots move out of Disks — Disks is the hardware, this is the
filesystem on top of it. New: snapshot schedules, hourly to monthly, with a retention
period and a floor that keeps the newest few whatever their age.

The naming is zfSnap's, deliberately. grid has run zfSnap from root cron since before this
repo existed and it works, so rather than invent a second convention this writes and reads
exactly what zfSnap does: 2026-09-24_02.30.01--6w, with the retention in the name. Ham Lab
can therefore show and expire the snapshots zfSnap already took, the two cannot disagree
about what a snapshot is, and changing a schedule never re-dates old ones — each carries
the rule it was taken under. An existing cron job is detected and shown on the page, with a
warning if a Ham Lab schedule is running as well.

Nothing is destroyed unseen. Every schedule answers what it would delete, with the list,
before Run now touches anything, and only names that parse as a stamp and a TTL are ever
candidates. A snapshot taken by hand is invisible to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 13:01:53 -07:00
Mitchell-JohnsonandClaude Opus 5 9bf60deffc Retire the Dashboard tab; Processes is the front page
The app repeated itself. The module list, /api/prefs/dashboard, applyPrefs and the
per-user dashboard blob are gone. Widgets stays and now says plainly that it is for
dashboards outside Ham Lab.

This also closes a bug: applyPrefs set the sampling rate from intervalMs inside the
dashboard layout, and reset it to the default when that field was absent — so with
per-user layouts, one account saving a dashboard could reset the sampling rate for the
whole machine. The sampler now has one source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 23:03:49 -07:00
Mitchell-JohnsonandClaude Opus 5 363ff02bda Drop the external demo: the Widgets tab is the demo
The hamlab-demo site, its Widgets-tab button, the demo_url config key and the make publish
target that fed it are all gone, along with the public read-only token it needed. The
example stays in examples/external-demo as something to read. The installer one-liner
already comes from Gitea releases.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 20:17:22 -07:00
Mitchell-JohnsonandClaude Opus 5 68bf3bb218 An updater: what is published on Gitea, and installing it from Settings
A new selfupdate package asks one URL what the newest build is — a Gitea/Forgejo repo
API base, or any directory holding VERSION, SHA256SUMS and hamlab-linux-<arch>, which is
what make release already writes and what install.sh already downloads. The binary is
checked against SHA256SUMS before it goes near the running one, and a token is sent when
the source is behind a login, so a private Gitea serves its own fleet without opening the
instance to everyone. Version ordering is install.sh's vnum rule, so the daemon and the
installer never disagree about which build is newer.

Settings gains an Updates card: installed, published, when it last looked, the source,
release notes, Check for updates, and Install. The rail badges Settings when a newer
build is out. Nothing installs unasked — it restarts the daemon and drops terminals.

scripts/gitea-release.sh (make gitea-release) publishes dist/ as a Gitea release.
Deliberate, and not part of deploy: deploy pushes to the machines this hub owns,
publishing offers the build to every install anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 14:31:32 -07:00
Mitchell-JohnsonandClaude Fable 5.1 53d1da08a5 Space keeps its scans and refreshes them: zfs diff, folder mtimes, resume after cancel
A scan is saved beside the config and read back after a restart. The next scan starts
from it: on a pool with snapshots zfs diff names what changed and only those folders are
read; elsewhere a folder whose mtime is unchanged keeps its list and only its kept files
are stat'ed again. A cancelled scan keeps what it walked and Resume reads the rest.
Forget drops the saved result. The treemap is every file a tile, grouped by folder, with
no nested boxes: the folder chains of a Linux system made a staircase of empty headers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 00:43:30 -07:00
Mitchell-JohnsonandClaude Fable 5.1 e9fa21768e Space: the unit is a disk on its own or a ZFS pool as a whole, picked from a dropdown
A pool's member disks are listed under it, not offered apart; the picker shows the
layout, members, size and scan state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 20:26:03 -07:00
Mitchell-JohnsonandClaude Fable 5.1 fc5236471e Space: what is using a disk, WizTree-style
Pick a disk, not a partition; the scan walks every filesystem it backs, as root when
the host allows, in a helper process that streams the tree back. A tree of folders
largest-first over a treemap of the selected folder, nested to the file and coloured by
kind, plus the thousand largest files. The rail no longer offers the hub-only pages
while a remote machine is being looked at.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 20:18:36 -07:00
Mitchell-JohnsonandClaude Fable 5.1 2b54a06821 Users, with their own settings, and a list of where each is signed in
The login is a user name and a password. The old single password becomes the account
named after the OS user on first boot, along with its sessions and the machine-wide look.
Settings gains Users (add, remove, reset a password — your own password confirms) and
Sessions (device, IP, last seen; revoke one or sign out everywhere else). Theme, accents
and the dashboard layout follow the account; the refresh rate, graph history and alerts
stay the machine's. Pairing a machine takes a user on it. Log lines name the actor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 18:20:30 -07:00
Mitchell-JohnsonandClaude Opus 5 3c7e46104c Bug bash 5: the URL, the fleet loop, and an empty card
A link to a hub page no longer reloads onto the last machine; a link from a hub-only page to
a machine's page arrives at that page; a page being left no longer asks about the next page's
selection; the hub holds its machines to the rate it is itself running.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 23:26:36 -07:00
Mitchell-JohnsonandClaude Opus 5 dbfe163a75 A machine is titled by its own hostname, and follows a rename
The name a machine was added under stays its identity — the URL, its accent, what the hub's
API calls it — and is what the Machines page shows as 'Added as'. The hostname is read live
rather than at start-up, so renaming a machine reaches the console without a restart.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 17:04:17 -07:00
Mitchell-JohnsonandClaude Fable 5.1 be0f2a5aaf The hub keeps every machine's sampling rate and history window in step
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 10:14:23 -07:00
Mitchell-JohnsonandClaude Fable 5.1 668e716601 Top wears the skin; the refresh rate reaches every machine; bug bash 4
The process table's columns fit the box they are in, the rate control saves, curves are
timed on the viewer's clock and keep to the stride, the power icon is drawn, and a young
history draws in from the right instead of being stretched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 10:12:45 -07:00
Mitchell-JohnsonandClaude Opus 5 04dd98e2cd Settings: one window for how far back every graph reaches, 1 minute to 24 hours
Each machine keeps 300 points per curve whatever the window, so memory doesn't follow it;
a wider window means wider points, each the average of its bucket rather than one instant
out of it. The hub passes the setting to every machine it manages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 19:07:55 -07:00
Mitchell-JohnsonandClaude Fable 5.1 1e992ca1eb Stacks: Duplicate, Labels and Network in the form, task verdict badges, a short Where; Cancel really cancels
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-20 13:12:54 -07:00
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 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 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 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 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 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 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 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 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 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
Mitchell-JohnsonandClaude Fable 5.1 7dcdb5dc98 Multi-host phase 1: API tokens, auto TLS with pinned fingerprints, hub-side node manager + reverse proxy (WebSockets included), machine switcher in the rail, Machines page
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 11:35:29 -07:00
Mitchell-JohnsonandClaude Fable 5.1 e4df878ac0 Processes tab (btop-style): every process with CPU%, memory, cpu time, start, sortable + filter, per-core and memory bars, detail pane with terminate/kill; module removed from the dashboard; sampling only while the tab is open
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 06:01:12 -07:00
Mitchell-JohnsonandClaude Fable 5.1 ff922bda74 Dashboard: Processes module (top by CPU/memory, PID, user, threads, SIGTERM), sampled at most every 2 s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 05:58:11 -07:00
Mitchell-JohnsonandClaude Fable 5.1 63b020d556 Files tab: browser with breadcrumbs/sort/filter, text editor, image preview, streamed upload with progress, download, mkdir/touch/rename/move/chmod/delete, open-in-terminal; root toggle via sudo-reexecuted 'hamlab fs' helper
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 05:39:57 -07:00
Mitchell-JohnsonandClaude Fable 5.1 c704e87065 Disks tab: lsblk devices with partitions/usage/live IO, SMART via smartctl (standby-aware, self-tests, install button), ZFS pools/datasets/snapshots with scrub and snapshot-now
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 22:48:08 -07:00
Mitchell-JohnsonandClaude Fable 5.1 acb11193b9 Updates tab (apt list/refresh/upgrade/changelog, reboot flag) on a new background tasks package with live WS streaming; header power button with hostname confirmation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 17:40:29 -07:00
Mitchell-JohnsonandClaude Fable 5.1 41c2f05535 Cron tab: crontabs, cron.d, user crontabs, run-parts dirs and systemd timers in one list; next/last run, run now, enable/disable, in-place editing, cron log and output file
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 16:21:35 -07:00
Mitchell-JohnsonandClaude Fable 5.1 bc891b1042 Services tab: systemd unit list/filter, detail with start/stop/restart/reload/enable/disable, live journal tail over WS, unit file viewer
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 15:35:03 -07:00
Mitchell-JohnsonandClaude Fable 5.1 1751afc974 Configurable update interval (100 ms – 2 s): drives daemon sampling and UI polling, saved with the dashboard layout
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 13:54:48 -07:00