Per machine like every other tab: the interfaces as the kernel has them
(addresses, MAC, MTU, negotiated link speed, carrier, totals, live rates from
the sampler's own sensors), the default route and resolvers, what the internet
sees of this connection, and a speed test with the last fifty results kept.
The test runs in the daemon, not the browser, so the figure is the line of the
machine in the switcher — the router's, if you switch to gate — and it carries
on when the phone locks. It measures against Cloudflare's public speed
endpoints, so there is nothing to install on any host, which is what makes it
work the same on Debian and on OpenWrt where flash is scarce.
What the measurement does and does not count:
- Several streams, HTTP/2 deliberately off. One stream over one connection
measures a congestion window, and HTTP/2 would put every stream back onto
one connection and measure the same thing.
- The first second of each phase is thrown away as the ramp.
- A phase ends on 64 MiB as well as on four seconds, so a fast line says what
it is without moving a gigabyte. A minimum window keeps a very fast line
from filling the cap before there is anything to divide by.
- Latency is the round trip less the far end's own Server-Timing. A zero-byte
response is mostly Cloudflare's worker thinking: left in, it reported 31 ms
for a line the edge itself measures at 3.5. Jitter is the mean change
between consecutive probes, so one slow probe cannot run away with it.
- The far end rate-limits by volume. So: a 20 s cooldown counted from the last
attempt rather than the last success, streams that retry twice, the rate
computed over the window in which bytes were actually still arriving, and a
plain sentence when it was refused. Coasting to the end of the clock with
nothing arriving reported a third of the truth.
The last result is published as net.internet.* sensors, each stamped with when
the test ran, so the line can be a widget, a graph, an alert or a Prometheus
series like anything else. The public address is refreshed on its own quarter
hour, which is worth having here: it is a DHCP lease and it moves.
Tests are offline — the window, the byte cap, the refusal and the cancellation
run against a local server; the route parser's byte order is pinned by a case
that would have caught the bug it did. The live measurement is behind
HAMLAB_NET_TEST=1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
101 lines
6.3 KiB
Markdown
101 lines
6.3 KiB
Markdown
# Ham Lab
|
|
|
|
Web-based Linux management console for bare-metal Linux, in the style of the Proxmox
|
|
web UI. Named after Ham the cat (see also HamTV).
|
|
|
|
Status: **v0.1** — persistent web terminals and a live Dashboard tab. Scope and roadmap in
|
|
[`PLAN.md`](PLAN.md).
|
|
|
|
## Runs on any Linux box
|
|
|
|
One static Go binary with the UI embedded: no cgo, no libc, no Node at runtime, no agents.
|
|
The same file runs on Debian, Ubuntu, Fedora, RHEL/Rocky/Alma, openSUSE, Arch and Alpine
|
|
(musl), on bare metal, VMs and containers. Builds: `amd64`, `arm64`, `arm` (ARMv6
|
|
hard-float and up, so every Raspberry Pi), `386`, `riscv64`.
|
|
|
|
```sh
|
|
sudo ./hamlab-linux-amd64 install # that's the whole install
|
|
```
|
|
|
|
or, from wherever `make release` output is served:
|
|
|
|
```sh
|
|
curl -fsSL https://git.bigham.dev/mitchell/hamlab/releases/download/latest/install.sh | sh # asks: user, address, HTTPS, firewall, password
|
|
curl -fsSL https://git.bigham.dev/mitchell/hamlab/releases/download/latest/install.sh | sh -s -- --yes # asks nothing, all defaults
|
|
curl -fsSL https://git.bigham.dev/mitchell/hamlab/releases/download/latest/install.sh | sh -s -- --listen 0.0.0.0:8100 --tls auto # flags go to `hamlab install`
|
|
```
|
|
|
|
`install.sh` is a wizard when there is a terminal (it reads answers from `/dev/tty`, so it
|
|
works piped from curl) and silent otherwise. Either way it picks the build for `uname -m`,
|
|
checks it against `SHA256SUMS` and runs `hamlab install`; re-running it upgrades in place.
|
|
`latest` is an alias Gitea resolves to the newest release, so that URL never changes.
|
|
`make gitea-release` publishes a release there (`HAMLAB_URL` points the installer elsewhere); run the script beside the `hamlab-linux-*` files and it installs offline.
|
|
`hamlab install`:
|
|
|
|
- copies the binary to `/usr/local/bin` (`--prefix`), atomically;
|
|
- gets root through `sudo` or `doas` if you aren't root already, and runs the console as
|
|
**you** (`--user`), so terminals are your login shell with your rights;
|
|
- writes the config to `~/.config/hamlab/` (`--data`; an existing `~/apps/hamlab/` is kept)
|
|
and asks for the first account's password (the account is named after the user the
|
|
console runs as), or generates one and prints it once (`--password-stdin` for scripts);
|
|
- registers a boot-time service with **systemd** or **OpenRC**, naming only groups that
|
|
exist on the host, and waits for `/healthz`; with neither init it prints the one line
|
|
to put under your own supervisor;
|
|
- says so when firewalld or ufw will block the port (`--open-firewall` opens it).
|
|
|
|
Re-running it upgrades in place and keeps config, user and accounts. `hamlab uninstall
|
|
[--purge]` reverses it.
|
|
|
|
Accounts: every person has their own user name and password, and the theme, accent
|
|
colours and dashboard layout follow the account. The **Users** tab has it all: your own
|
|
password, where you are signed in (sign out any device), and the accounts (add, remove,
|
|
reset a password; your own password confirms the last two). From the shell:
|
|
`hamlab passwd [user]` sets a password and makes the account if it is new, `hamlab
|
|
users` lists them, `hamlab user-rm <user>` removes one. Machine settings — refresh rate,
|
|
graph history, alert rules — stay the machine's and are shared by everyone.
|
|
|
|
**Guest logins**, for showing somebody the console: Users → Guest logins makes a
|
|
throwaway user name and password, good for half an hour by default (up to 24 hours), and
|
|
hands you the pair to paste into a message. A guest is an ordinary account with a clock on
|
|
it — the whole console, every machine, the terminal included — that stops working when the
|
|
time is up, wherever it is signed in, or the moment you revoke it. There is no reduced
|
|
guest: give one to somebody you would hand the password to, and revoke it if you change
|
|
your mind.
|
|
|
|
What adapts to the host, rather than assuming Debian:
|
|
|
|
| | |
|
|
|---|---|
|
|
| Root | already root, else passwordless `sudo`, else passwordless `doas`; without any, the console is read-only where root is needed and says so |
|
|
| Updates tab | apt, dnf, zypper, pacman, apk: list, refresh, upgrade, install (pacman: all or nothing) |
|
|
| Services / timers | systemd of any age (JSON output where it exists, the plain table before v246); a clear message on hosts without systemd |
|
|
| Disks | `lsblk`, or `/sys/block` when it's missing (busybox) or too old; SMART and ZFS only when their tools are there |
|
|
| Network | `/proc` and `/sys` for the interfaces, addresses, link speed and default route. The speed test needs no tool installed: it measures against Cloudflare's public speed endpoints, so it works the same on Debian and on the OpenWrt router |
|
|
| Dashboard | `/proc` and `/sys` only: hwmon or thermal-zone temps, fans, NICs, disks, mounts; empty sections where the hardware has nothing to report. UPS from whichever of `pwrstat`, NUT (`upsc`) or `apcupsd` is installed |
|
|
| Listen address | bound with `IP_FREEBIND`, so a LAN address that isn't up yet at boot doesn't fail the start |
|
|
| Self-update | the new build must run and load this host's config before it replaces the old one; atomic rename; the daemon then re-execs itself, so it restarts the same way under systemd, OpenRC or nothing; `hamlab.prev` kept |
|
|
|
|
Building: `make build` (web + Go), `make release` (every architecture, `install.sh` and
|
|
`SHA256SUMS` in `dist/`), `make test`.
|
|
|
|
Terminals run as the user the service runs as, with that user's login shell.
|
|
|
|
## Managing several machines
|
|
|
|
Install Ham Lab on each host; on the ones the hub should reach:
|
|
`hamlab install --listen <lan-ip>:8100 --tls auto` (a self-signed certificate is generated
|
|
on first start). Then on the hub, open **Machines**, enter a name, the address
|
|
(`https://host:8100`) and a user and password on that machine once (the user may be
|
|
left out when it has only one). The hub pins the
|
|
certificate, keeps an access token, and every tab works against the chosen machine
|
|
through the switcher at the top of the rail.
|
|
|
|
Updating: `make deploy` on the hub installs the new build there and then pushes it to
|
|
every managed machine (`hamlab push-nodes`), which install it and restart. The Machines
|
|
page flags machines running an older version and offers the same push per machine.
|
|
Mixed architectures are fine: `make deploy` also cross-compiles every other architecture,
|
|
and the hub sends each machine the build for its own.
|
|
|
|
- On `core` it is public at `https://hamlab.bigham.dev` behind Nginx Proxy Manager.
|
|
- Installable as a PWA.
|