Files
mesh-host/internal/profile/detectors.go
T
jschoubben 73c010e7ef Stage 1 — the host reports what a machine is and can do
Tier 0's first slice, per novox/hq 03-DESIGN/01-to-be/05-the-node-host.md. It
applies nothing, connects to nothing, listens on nothing. 2.9 MB, static, no
dynamic dependencies: copy it onto a machine and run it is the whole install,
which is the property ADR 0041 rests on.

A capability is detected, never assumed. Every detector runs something that only
succeeds if the thing FUNCTIONS — the daemon is asked for its version, the
package database is queried, the firewall is asked to list a ruleset, which
needs the privilege as well as the tool. 04-ISSUES/007 is the fault this
prevents: a client on disk with its daemon down looks exactly like a working
runtime, and a node assigned work on that basis fails when the work arrives.

Every verdict carries the reason and the method. A capability reported absent
with no reason is the same fault in a new place: something nobody can act on.

Two bugs found by running rather than reasoning, both silent:

systemctl is-system-running exits non-zero for every state except `running` —
including `degraded`, which means units failed and the init is emphatically
there. Reading the exit code reported NO service manager on a machine whose init
it was. That is 007 in the mirror, and both directions place work wrongly. A
verdict now reads what a tool says about itself, not only how it exited.

And `mesh-host inventory --json` printed text: the standard library stops
parsing at the first non-flag argument, so the flag sat unread and the command
exited 0 having ignored what was asked. The parser now takes the subcommand off
the front, and a stray or mistyped argument is refused rather than dropped.

Detection deliberately does NOT follow ADR 0008. That rule governs applying
state, where a failed step means the machine is not what was asked for. A failed
probe is a finding — "absent, because the probe failed" — and aborting would
replace one legible absence with total ignorance of the rest.

25 tests: structure and logic with a fake runner, and the same detectors against
this machine, because a test that fakes the system under detection asserts only
that the fake behaves as expected.
2026-08-26 00:25:08 +02:00

207 lines
7.4 KiB
Go

package profile
import (
"context"
"fmt"
"os"
"strings"
)
// Named capabilities. Constants rather than strings at the call site, because a capability
// nothing declares is a capability nothing can require, and a typo would produce exactly that.
const (
CapContainerRuntime = "container-runtime"
CapPackageManager = "package-manager"
CapServiceManager = "service-manager"
CapFirewall = "firewall"
CapOverlay = "overlay"
CapGraphicalSession = "graphical-session"
CapPrivileged = "privileged"
)
// commandCapability is the shape most detectors take: run something, and treat a working
// invocation as evidence.
//
// It runs a command that only succeeds if the thing is FUNCTIONING, never `--version` alone.
// A version string proves a binary is on disk, which is the assumption 04-ISSUES/007 records
// as false: the package was installed and the daemon was not running.
type commandCapability struct {
name string
command string
args []string
// why describes what a success actually proves, and is reported as the detector's `How`.
why string
// interpret decides the verdict from what the command said and how it exited.
//
// Exists because "exit zero" is not a universal answer. A degraded service manager reports
// its state on stdout and exits non-zero — it is running, and reading only the exit code
// declared no service manager on a machine whose init it was. That is 04-ISSUES/007 in the
// mirror: 007 is installed-but-broken reported present; this is working-but-imperfect
// reported absent. Both place work wrongly, and this one was only visible by running
// against a real machine.
//
// nil means the ordinary rule: success is exit zero.
interpret func(stdout string, err error) (present bool, detail string)
runner Runner
}
func (c commandCapability) Name() string { return c.name }
func (c commandCapability) Detect(ctx context.Context) Verdict {
out, err := c.runner(ctx, c.command, c.args...)
interpret := c.interpret
if interpret == nil {
interpret = exitZero
}
present, detail := interpret(out, err)
if strings.TrimSpace(detail) == "" {
// A verdict with no reason is the fault in a new place: something nobody can act on.
// Reached when a command fails silently, which systemctl does.
if present {
detail = "responded"
} else {
detail = fmt.Sprintf("%s gave no reason", c.command)
}
}
return Verdict{Name: c.name, Present: present, Detail: firstLine(detail), How: c.why}
}
// exitZero is the ordinary rule: the command worked, so the capability is there.
func exitZero(stdout string, err error) (bool, string) {
if err != nil {
return false, err.Error()
}
return true, stdout
}
// systemRunning reads what an init system says about itself rather than how it exited.
//
// `is-system-running` exits non-zero for every state except `running` — including `degraded`,
// which means units failed and the init is emphatically present. Treating that as absent made
// a machine running systemd report no service manager.
func systemRunning(stdout string, err error) (bool, string) {
state := strings.TrimSpace(firstLine(stdout))
switch state {
case "running", "degraded", "starting", "maintenance", "stopping":
return true, state
case "":
if err != nil {
return false, err.Error()
}
return false, "said nothing"
default:
// `offline` and `unknown` mean it is not managing this machine.
return false, state
}
}
func firstLine(s string) string {
s = strings.TrimSpace(s)
if i := strings.IndexByte(s, '\n'); i >= 0 {
s = s[:i]
}
if len(s) > 200 {
s = s[:200] + "…"
}
return s
}
// privileged reports whether the host can change this machine at all.
//
// Reported as a capability rather than checked at startup on purpose: a host that cannot act
// is still a host that can report, and novox/hq ADR 0036 says what varies between nodes lives
// here rather than in the definition of a node.
type privileged struct{}
func (privileged) Name() string { return CapPrivileged }
func (privileged) Detect(context.Context) Verdict {
uid := os.Geteuid()
if uid == 0 {
return Verdict{
Name: CapPrivileged, Present: true,
Detail: "effective uid 0",
How: "effective uid — the host changes a machine, which needs root",
}
}
return Verdict{
Name: CapPrivileged, Present: false,
Detail: fmt.Sprintf("effective uid %d, not 0", uid),
How: "effective uid — the host changes a machine, which needs root",
}
}
// graphicalSession reports whether anything could display a window here.
//
// Environment rather than a probe, because a display server is reachable through a socket a
// detector would have to guess at, and the variables are what an application would use anyway.
// Stated so the limit is visible: this detects that a session is ADVERTISED, which is weaker
// than the other detectors here.
type graphicalSession struct{}
func (graphicalSession) Name() string { return CapGraphicalSession }
func (graphicalSession) Detect(context.Context) Verdict {
const how = "DISPLAY / WAYLAND_DISPLAY — weaker than the other checks: advertised, not probed"
if d := os.Getenv("WAYLAND_DISPLAY"); d != "" {
return Verdict{Name: CapGraphicalSession, Present: true, Detail: "wayland: " + d, How: how}
}
if d := os.Getenv("DISPLAY"); d != "" {
return Verdict{Name: CapGraphicalSession, Present: true, Detail: "x11: " + d, How: how}
}
return Verdict{
Name: CapGraphicalSession, Present: false,
Detail: "neither DISPLAY nor WAYLAND_DISPLAY is set",
How: how,
}
}
// Default returns the detectors the host runs when nobody says otherwise.
//
// Each command is chosen to prove the thing WORKS rather than exists:
// - the container runtime is asked for server-side information, which fails when the daemon
// is down even though the client is installed — the exact shape of 04-ISSUES/007;
// - the service manager is asked whether it is the running init, not whether it is present;
// - the firewall is asked to list a ruleset, which needs both the tool and the permission.
func Default(runner Runner) []Detector {
if runner == nil {
runner = ExecRunner
}
return []Detector{
privileged{},
graphicalSession{},
commandCapability{
name: CapContainerRuntime, command: "docker", args: []string{"info", "--format", "{{.ServerVersion}}"},
why: "asks the daemon for its version — a running daemon, not an installed client",
runner: runner,
},
commandCapability{
name: CapPackageManager, command: "pacman", args: []string{"-Q", "pacman"},
why: "queries the package database — a working database, not a binary on disk",
runner: runner,
},
commandCapability{
name: CapServiceManager, command: "systemctl", args: []string{"is-system-running"},
why: "reads the init's own account of its state — degraded is still running",
interpret: systemRunning,
runner: runner,
},
commandCapability{
name: CapFirewall, command: "nft", args: []string{"list", "ruleset"},
why: "lists the ruleset — needs the tool AND the privilege to use it",
runner: runner,
},
commandCapability{
name: CapOverlay, command: "wg", args: []string{"show", "interfaces"},
why: "asks the kernel for interfaces — needs the module, not just the tool",
runner: runner,
},
}
}
// isRoot is the same question `privileged` answers, exposed for tests that must check the
// detector against something other than itself.
func isRoot() bool { return os.Geteuid() == 0 }