rig platform: what is this machine, calculated at run time, stored nowhere #64

Closed
opened 2026-07-19 18:35:46 +00:00 by dan-claude-bot · 0 comments
dan-claude-bot commented 2026-07-19 18:35:46 +00:00 (Migrated from github.com)

rig platform: what is this machine, calculated at run time, stored nowhere

The gap

rig reads no hardware. At all. The single exception is uname -m in
runner-install.sh:147-150, used to pick a runner tarball and then discarded.

So "is this the 32GB one, or the M900?" is a question you answer by logging in
and running free -h, nproc, df -h and uname -r by hand, four commands
deep, on a machine you are already unsure about. There is no rig inspect and
no machine-wide rig status; the nearest things are rig runner status and
rig users status, each of which knows exactly one domain.

Why this is a command and not a file

It would be easy to write the specs into /etc/rig/manifest during bootstrap.
That is the wrong shape, and the reason is the whole design:

Specs change without rig doing anything. Someone adds RAM, resizes the
root disk, or the unattended-upgrades bootstrap itself enables
(bootstrap.sh:198-201) patches the kernel. A stored spec is stale the moment
the machine changes, and refreshing it on every run collides with the contract
at bootstrap.sh:3"Convergent: safe to re-run; a second run changes
nothing"
— enforced by cmp-guards at nine sites.

Computing at run time removes the problem instead of managing it. The answer
is correct by construction because there is nothing to go stale, and the
convergence contract is never touched because nothing is written.

The corollary is a property worth having deliberately: rig platform works
on a machine rig has never converged.
It reads only /proc, uname and
/etc/os-release, so it runs on bare Debian before bootstrap — which makes it
useful for deciding what to converge this into, not just for auditing
afterwards. It needs no root either.

#61 keeps the complementary half: provenance — which rig, and when — which is
decided rather than observed, so it is written once and never changes.

Proposed output

$ rig platform
HOSTNAME   hetzner-cp-1
OS         Debian GNU/Linux 13 (trixie)
KERNEL     6.12.95-1 (x86_64)
CPU        AMD Ryzen 7 3700X 8-Core Processor (16 cores)
MEMORY     31Gi total, 24Gi available
DISK       456Gi total, 201Gi free on /
VIRT       kvm

RIG        0.4.0, bootstrapped 2026-07-19T14:24:51Z
ROLE       dev (class=human host=yes join=authkey)

The bottom block is read from /etc/rig/manifest (#61) and /etc/rig/role
read, never written. On a machine with no manifest those two lines are
omitted or render as not bootstrapped, which is itself the useful answer.
That degradation is why this command must not hard-depend on #61 landing
first; the two are independent and either can go in on its own.

Sources

All coreutils, /proc and systemd — nothing rig would have to add as a
dependency, and nothing absent on a minimal Debian:

field source
HOSTNAME hostname
OS . /etc/os-release && printf '%s %s' "$NAME" "$VERSION"
KERNEL uname -r, uname -m
CPU /proc/cpuinfo first model name; nproc
MEMORY /proc/meminfo MemTotal / MemAvailable
DISK df -h /
VIRT systemd-detect-virt (systemd is already a hard dependency)

One trap, already documented in five places in the tree
(bootstrap.sh:155-159, runner-install.sh:88-92, and three more):
/etc/os-release must be sourced in a subshell. It defines VERSION,
NAME and ID, and sourcing it in the main shell silently clobbers
same-named script variables. The existing sites all use
$(. /etc/os-release && printf ...) — follow that verbatim.

systemd-detect-virt exits non-zero on bare metal (printing none), which is
a normal result and not an error — worth stating because set -e will
otherwise turn a bare-metal machine into a failed command.

The name — platform over status

Both were on the table. I recommend rig platform, for two reasons:

  • status means something specific in this tree already. rig users status and rig runner status both cross-check recorded state against
    live state and print DRIFT (users-status.sh:77-84). That is the house
    meaning of the word: is reality still what we recorded? This command
    cannot drift — it records nothing and computes everything — so calling it
    status borrows a promise it structurally cannot make.
  • It leaves rig status free for the aggregate it will eventually want to
    be: a machine-wide health roll-up over users, runner, box and tailscale,
    each of which already has its own status. Spending the name now on a
    hardware description would be hard to walk back.

rig platform says what it is: a description of the platform, no judgement.

Out of scope

  • Any storage. This command writes nothing, ever. That is its defining
    property, not an implementation detail.
  • NIC names, MAC addresses, PCI inventory, mount tables, sensors. This is a
    cheatsheet, not inxi — the bar is "what would I want to know before I SSH
    in"
    , and the proposed set fits on one screen.
  • Health judgements — "disk nearly full", "swap thrashing". That is rig status's job if it ever exists, and it needs thresholds this command has no
    business owning.

Open questions

  • --json? Everything else in rig prints for humans, and rig famously has
    no jq on the target. But this is the one output a fleet-wide sweep would
    want to collect and diff across machines. A flat key=value mode (rig platform --raw) is probably the rig-shaped answer, matching the manifest's
    own format and parseable with read.
  • Does it show the tailnet? tailscale status --json is available on a
    converged machine and the hostname/tags are genuinely part of "what is this
    machine". Against: it is a network call, it is slow when the tailnet is
    unreachable, and it makes the command fail in ways /proc never will.
    Probably a separate line item, or behind a flag.
  • Container and guest machines. Inside a box-minted guest, CPU and memory
    reflect the host's hardware filtered through the instance's limits, and
    VIRT will say lxc. Worth confirming the numbers shown there are the
    limits rather than the host's totals — otherwise the output is actively
    misleading on exactly the machines rig converges most often.

Complements #61 (provenance: which rig, and when). Neither blocks the other.

# `rig platform`: what is this machine, calculated at run time, stored nowhere ## The gap rig reads no hardware. At all. The single exception is `uname -m` in `runner-install.sh:147-150`, used to pick a runner tarball and then discarded. So "is this the 32GB one, or the M900?" is a question you answer by logging in and running `free -h`, `nproc`, `df -h` and `uname -r` by hand, four commands deep, on a machine you are already unsure about. There is no `rig inspect` and no machine-wide `rig status`; the nearest things are `rig runner status` and `rig users status`, each of which knows exactly one domain. ## Why this is a command and not a file It would be easy to write the specs into `/etc/rig/manifest` during bootstrap. That is the wrong shape, and the reason is the whole design: **Specs change without rig doing anything.** Someone adds RAM, resizes the root disk, or the unattended-upgrades bootstrap itself enables (`bootstrap.sh:198-201`) patches the kernel. A stored spec is stale the moment the machine changes, and refreshing it on every run collides with the contract at `bootstrap.sh:3` — *"Convergent: safe to re-run; a second run changes nothing"* — enforced by cmp-guards at nine sites. Computing at run time removes the problem instead of managing it. The answer is correct by construction because there is nothing to go stale, and the convergence contract is never touched because nothing is written. The corollary is a property worth having deliberately: **`rig platform` works on a machine rig has never converged.** It reads only `/proc`, `uname` and `/etc/os-release`, so it runs on bare Debian before bootstrap — which makes it useful for deciding *what to converge this into*, not just for auditing afterwards. It needs no root either. #61 keeps the complementary half: provenance — which rig, and when — which is decided rather than observed, so it is written once and never changes. ## Proposed output ``` $ rig platform HOSTNAME hetzner-cp-1 OS Debian GNU/Linux 13 (trixie) KERNEL 6.12.95-1 (x86_64) CPU AMD Ryzen 7 3700X 8-Core Processor (16 cores) MEMORY 31Gi total, 24Gi available DISK 456Gi total, 201Gi free on / VIRT kvm RIG 0.4.0, bootstrapped 2026-07-19T14:24:51Z ROLE dev (class=human host=yes join=authkey) ``` The bottom block is read from `/etc/rig/manifest` (#61) and `/etc/rig/role` — **read, never written**. On a machine with no manifest those two lines are omitted or render as `not bootstrapped`, which is itself the useful answer. That degradation is why this command must not hard-depend on #61 landing first; the two are independent and either can go in on its own. ## Sources All coreutils, `/proc` and systemd — nothing rig would have to add as a dependency, and nothing absent on a minimal Debian: | field | source | |---|---| | `HOSTNAME` | `hostname` | | `OS` | `. /etc/os-release && printf '%s %s' "$NAME" "$VERSION"` | | `KERNEL` | `uname -r`, `uname -m` | | `CPU` | `/proc/cpuinfo` first `model name`; `nproc` | | `MEMORY` | `/proc/meminfo` `MemTotal` / `MemAvailable` | | `DISK` | `df -h /` | | `VIRT` | `systemd-detect-virt` (systemd is already a hard dependency) | One trap, already documented in five places in the tree (`bootstrap.sh:155-159`, `runner-install.sh:88-92`, and three more): `/etc/os-release` **must be sourced in a subshell**. It defines `VERSION`, `NAME` and `ID`, and sourcing it in the main shell silently clobbers same-named script variables. The existing sites all use `$(. /etc/os-release && printf ...)` — follow that verbatim. `systemd-detect-virt` exits non-zero on bare metal (printing `none`), which is a normal result and not an error — worth stating because `set -e` will otherwise turn a bare-metal machine into a failed command. ## The name — `platform` over `status` Both were on the table. I recommend `rig platform`, for two reasons: - **`status` means something specific in this tree already.** `rig users status` and `rig runner status` both cross-check recorded state against live state and print `DRIFT` (`users-status.sh:77-84`). That is the house meaning of the word: *is reality still what we recorded?* This command cannot drift — it records nothing and computes everything — so calling it `status` borrows a promise it structurally cannot make. - **It leaves `rig status` free** for the aggregate it will eventually want to be: a machine-wide health roll-up over users, runner, box and tailscale, each of which already has its own `status`. Spending the name now on a hardware description would be hard to walk back. `rig platform` says what it is: a description of the platform, no judgement. ## Out of scope - Any storage. This command writes nothing, ever. That is its defining property, not an implementation detail. - NIC names, MAC addresses, PCI inventory, mount tables, sensors. This is a cheatsheet, not `inxi` — the bar is *"what would I want to know before I SSH in"*, and the proposed set fits on one screen. - Health judgements — "disk nearly full", "swap thrashing". That is `rig status`'s job if it ever exists, and it needs thresholds this command has no business owning. ## Open questions - **`--json`?** Everything else in rig prints for humans, and rig famously has no `jq` on the target. But this is the one output a fleet-wide sweep would want to collect and diff across machines. A flat `key=value` mode (`rig platform --raw`) is probably the rig-shaped answer, matching the manifest's own format and parseable with `read`. - **Does it show the tailnet?** `tailscale status --json` is available on a converged machine and the hostname/tags are genuinely part of "what is this machine". Against: it is a network call, it is slow when the tailnet is unreachable, and it makes the command fail in ways `/proc` never will. Probably a separate line item, or behind a flag. - **Container and guest machines.** Inside a box-minted guest, CPU and memory reflect the host's hardware filtered through the instance's limits, and `VIRT` will say `lxc`. Worth confirming the numbers shown there are the limits rather than the host's totals — otherwise the output is actively misleading on exactly the machines rig converges most often. Complements #61 (provenance: which rig, and when). Neither blocks the other.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/rig#64
No description provided.