hub v0.133.0 (R-859 test approvals end with the test; R-840 bundle on the System page, manifest, alarm); installer 1.31.0 (root files from the config bundle); bundle bootstrap script; golden 0.293.0 evidence; rulings 96–99; drill-r50 removed (evidence)
gates / gates (push) Successful in 29s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-04 20:18:37 +02:00
parent 6b820143f8
commit ff1db11db4
45 changed files with 1707 additions and 42 deletions
+42 -4
View File
@@ -31,9 +31,10 @@ type systemRow struct {
FactsNote string
// host
PVE, KernelRunning, KernelNextBoot, HostDebian cell
HostRelease, HostPending, HostNotCovered cell
Held, RebootSince, KernelPanic, Oops cell
CrashRestarts24h, Guard cell
HostRelease, HostPending, HostNotCovered cell
Held, RebootSince, KernelPanic, Oops cell
CrashRestarts24h, Guard cell
Bundle cell // R-840: the root-owned config bundle
// guest
GuestDebian, GuestRelease, GuestPending, GuestRestart cell
// docker
@@ -46,13 +47,43 @@ type systemRow struct {
type OSSystemView interface {
Fleet() ([]osupdates.FleetLine, error)
Releases() []osupdates.ReleaseInfo
CancelledReleases() []osupdates.ReleaseInfo
Candidates() []osupdates.Status
Thresholds() (stale, reboot, notCovered time.Duration)
BundleThreshold() time.Duration
ApproveDocker() (string, error)
}
func plain(s string) cell { return cell{Text: s} }
// bundleCell is the "Root files" cell (R-840): the box's config bundle against the vouched agent's. Amber while behind,
// red from the alarm's wait on, amber when a file was changed by hand (drift); "unknown" is never coloured as a fact.
func bundleCell(f sysfacts.System, vouchedAgent, vouchedSHA string, since time.Time, after time.Duration, now time.Time) cell {
b := f.Bundle
if !f.Present || b.Version == "" || b.Version == sysfacts.Unknown {
return unknownCell("")
}
c := cell{Text: b.Version}
switch {
case vouchedSHA == "":
c.Title = "no vouched bundle to compare with (the vouched agent carries none)"
case b.BundleSHA256 != vouchedSHA:
c.Class, c.Title = "warn", "behind the vouched agent "+vouchedAgent+"'s bundle — send it with a signed agent_config_update"
if !since.IsZero() {
c.Title += " (behind since " + since.UTC().Format("2006-01-02 15:04") + " UTC)"
if now.Sub(since) >= after {
c.Class = "bad"
}
}
}
if len(b.Drift) > 0 {
c.Text += " (changed by hand)"
c.Class = "warn"
c.Title = "files differ from the installed bundle: " + strings.Join(b.Drift, ", ")
}
return c
}
func unknownCell(s string) cell {
if s == "" || s == sysfacts.Unknown {
return cell{Text: "unknown", Class: "warn", Title: "the box could not read it (agent older than v0.142.0, or the guest is down)"}
@@ -207,9 +238,16 @@ func (s *Server) handleSystem(w http.ResponseWriter, r *http.Request) {
}
}
stale, reboot, notCov := view.Thresholds()
rows := buildSystemRows(lines, facts, names, stale, reboot, notCov, time.Now())
man := s.store.GetArtifactManifest()
for i := range rows {
rows[i].Bundle = bundleCell(facts[rows[i].HostID], man.AgentVersion, man.BundleSHA256,
s.store.BundleBehindSince(rows[i].HostID), view.BundleThreshold(), time.Now())
}
data := map[string]interface{}{
"Rows": buildSystemRows(lines, facts, names, stale, reboot, notCov, time.Now()),
"Rows": rows,
"Releases": view.Releases(),
"Cancelled": view.CancelledReleases(),
"Candidates": view.Candidates(),
"Flash": r.URL.Query().Get("flash"),
"FlashErr": r.URL.Query().Get("err"),