hub v0.159.0: a refused whole-guest backup has its own alarm (R-1150); a reinstall re-issues the claim code and the off-site login when the old host is stale or off (R-1154)

R-1150: the agent's localapi:backup-cadence-cap is a separate per-host state in
HostCapabilityChecker: alone it raises whole_guest_backup_refused (warning, plain text,
agent reason in details) and whole_guest_backup_refusal_cleared (info); other critical
caps keep agent_capability_degraded/_recovered unchanged. New types operator-only and
hub-origin only (not in allowedEventTypes: the suspected controller must not post them).
agent_capability_degraded/_recovered join operatorOnlyEvents (the checker's "never the
customer" claim was configuration only).

R-1154: handleHostEnroll's reuse path re-issues (claim code + off-site login) when no
controller wait is open and either deliberate_off_at is held or both the host report and
the controller report are older than alerting.stale_threshold (wired from main); once per
silence (host_reenroll_reissued record, also written at mint); a live box gets nothing
re-issued and the skip is recorded (host_reenroll_not_reissued). 05 section 8 [DESIGN].

Tests red-proved; go build/vet/test green.
This commit is contained in:
admin committed 2026-10-19 03:18:34 +02:00
1 parent e4f022a0d9
commit 5121536f10
14 files changed
+995 -33

No files matched your search

+3
View File
@@ -337,6 +337,9 @@ func main() {
}
apiHandler := api.New(dataStore, cfg.API.ReportAPIKey, cfg.Notifications.ResendAPIKey, cfg.Notifications.FromEmail, templateProvider, logger)
apiHandler.SetAssetManager(assetsMgr)
// R-1154: the host-enroll reuse path judges "the old box is gone" by the same threshold as hostStatus and the
// staleness checkers (unwired = it never re-issues). Pinned by r1154_wiring_test.go.
apiHandler.SetStaleThreshold(staleThreshold)
// Initialize notification dispatcher
dispatcher := notify.NewDispatcher(
+41
View File
@@ -0,0 +1,41 @@
package main
import (
"go/ast"
"go/parser"
"go/token"
"testing"
)
// R-1154 seam wiring: main() must hand the api handler the SAME stale threshold the staleness checkers get
// (apiHandler.SetStaleThreshold(staleThreshold)). Unwired, the host-enroll reuse path never re-issues the claim
// code and the off-site login on a reinstall — the R-1154 defect, back in silence.
//
// COMPANION RED-PROOF (observed 2026-10-19): with the call deleted from main.go this fails with
// "cmd/hub/main.go never calls apiHandler.SetStaleThreshold(staleThreshold)". Restored.
func TestR1154_MainWiresTheStaleThresholdIntoTheAPI(t *testing.T) {
f, err := parser.ParseFile(token.NewFileSet(), "main.go", nil, 0)
if err != nil {
t.Fatal(err)
}
wired := false
ast.Inspect(f, func(n ast.Node) bool {
c, ok := n.(*ast.CallExpr)
if !ok {
return true
}
sel, ok := c.Fun.(*ast.SelectorExpr)
if !ok || sel.Sel.Name != "SetStaleThreshold" || len(c.Args) != 1 {
return true
}
recv, _ := sel.X.(*ast.Ident)
arg, _ := c.Args[0].(*ast.Ident)
if recv != nil && recv.Name == "apiHandler" && arg != nil && arg.Name == "staleThreshold" {
wired = true
}
return true
})
if !wired {
t.Error("cmd/hub/main.go never calls apiHandler.SetStaleThreshold(staleThreshold)")
}
}