diff --git a/hub/internal/api/managed_floor_ack_test.go b/hub/internal/api/managed_floor_ack_test.go index 08abee44..ef22daa7 100644 --- a/hub/internal/api/managed_floor_ack_test.go +++ b/hub/internal/api/managed_floor_ack_test.go @@ -13,7 +13,7 @@ import ( // report handler to the ungated EffectiveMinControllerVersion → the held case leaks the floor. func TestReportACK_ManagedFloorHold(t *testing.T) { h, st, _ := newTestHandler(t) - st.SetDefaultMinControllerVersion("0.113.0") // controller floor + st.SetDefaultMinControllerVersion("0.113.0") // controller floor _ = st.SetArtifactManifest(store.ArtifactManifest{GoldenVersion: "0.113.0", MinAgent: "0.81.0"}) // coupled golden if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c", RetrievalPassword: "pw", APIKey: "k", ConfigJSON: "{}"}); err != nil { t.Fatal(err) diff --git a/hub/internal/api/offsite_test.go b/hub/internal/api/offsite_test.go index aa7e0d57..60621846 100644 --- a/hub/internal/api/offsite_test.go +++ b/hub/internal/api/offsite_test.go @@ -68,7 +68,9 @@ func (f *fakeKeySvc) ConfirmKey(_ context.Context, _ string, fp string) (int, er func (f *fakeKeySvc) OpenWindowFor(context.Context, string, int) (offsitekeys.WindowGrant, error) { return offsitekeys.WindowGrant{Granted: false, Reason: "not due"}, f.err } -func (f *fakeKeySvc) CloseWindowFor(context.Context, string, offsitekeys.WindowResult) error { return f.err } +func (f *fakeKeySvc) CloseWindowFor(context.Context, string, offsitekeys.WindowResult) error { + return f.err +} func (f *fakeKeySvc) RequestAbandon(context.Context, string, string) (offsitekeys.AbandonStatus, error) { return offsitekeys.AbandonStatus{State: "pending"}, f.err } diff --git a/hub/internal/api/routes.go b/hub/internal/api/routes.go index 3d1b5c32..751e920a 100644 --- a/hub/internal/api/routes.go +++ b/hub/internal/api/routes.go @@ -11,13 +11,13 @@ package api type apiAuth string const ( - authPublic apiAuth = "public" // no credential by design — the reason is on the entry - authCustomer apiAuth = "customer" // the household's controller key (or the global key) - authHost apiAuth = "host" // the box's own host key, self-scoped (or the global key) - authGlobal apiAuth = "global" // the operator/global key only - authPassphrase apiAuth = "passphrase" // X-Retrieval-Password of that household - authAppliance apiAuth = "appliance" // the appliance's own bearer token - authRecovery apiAuth = "recovery" // only while the operator has armed recovery mode for the host + authPublic apiAuth = "public" // no credential by design — the reason is on the entry + authCustomer apiAuth = "customer" // the household's controller key (or the global key) + authHost apiAuth = "host" // the box's own host key, self-scoped (or the global key) + authGlobal apiAuth = "global" // the operator/global key only + authPassphrase apiAuth = "passphrase" // X-Retrieval-Password of that household + authAppliance apiAuth = "appliance" // the appliance's own bearer token + authRecovery apiAuth = "recovery" // only while the operator has armed recovery mode for the host ) type apiRoute struct { diff --git a/hub/internal/hetznerapi/hetznerapi.go b/hub/internal/hetznerapi/hetznerapi.go index f931f899..a21edd47 100644 --- a/hub/internal/hetznerapi/hetznerapi.go +++ b/hub/internal/hetznerapi/hetznerapi.go @@ -47,8 +47,8 @@ type AccessSettings struct { type Subaccount struct { ID int64 `json:"id"` StorageBox int64 `json:"storage_box"` - Username string `json:"username"` // uXXXXXX-subN - Server string `json:"server"` // …-subN.your-storagebox.de + Username string `json:"username"` // uXXXXXX-subN + Server string `json:"server"` // …-subN.your-storagebox.de HomeDirectory string `json:"home_directory"` AccessSettings AccessSettings `json:"access_settings"` Labels map[string]string `json:"labels"` @@ -56,11 +56,11 @@ type Subaccount struct { // StorageBox mirrors a dedicated storage box (non-secret fields). type StorageBox struct { - ID int64 `json:"id"` - Username string `json:"username"` // uXXXXXX (empty until active) - Name string `json:"name"` - Status string `json:"status"` // initializing | active | … - Server string `json:"server"` // uXXXXXX.your-storagebox.de + ID int64 `json:"id"` + Username string `json:"username"` // uXXXXXX (empty until active) + Name string `json:"name"` + Status string `json:"status"` // initializing | active | … + Server string `json:"server"` // uXXXXXX.your-storagebox.de Labels map[string]string `json:"labels"` // StorageBoxType + Stats added v0.64.0 (R-5 pool-box aggregate). Shape pinned by the Phase-0 probe diff --git a/hub/internal/monitor/r723_new_box_test.go b/hub/internal/monitor/r723_new_box_test.go index f56ace6b..06dcb0f0 100644 --- a/hub/internal/monitor/r723_new_box_test.go +++ b/hub/internal/monitor/r723_new_box_test.go @@ -34,8 +34,8 @@ func TestR723_ANewBoxIsNotARecovery(t *testing.T) { if sc.GetState("c1") != "down" { t.Fatalf("setup: want down, got %q", sc.GetState("c1")) } - hostFor(t, st, path, 2*time.Minute) // the new box enrolled 2 min ago - saveReportAged(t, st, path, "ok", 0) // …and reported + hostFor(t, st, path, 2*time.Minute) // the new box enrolled 2 min ago + saveReportAged(t, st, path, "ok", 0) // …and reported sc.Check() if len(*events) != 0 { t.Fatalf("a new box's first report emitted %v", *events) diff --git a/hub/internal/monitor/storage_fill_test.go b/hub/internal/monitor/storage_fill_test.go index 6fb24674..10af2b49 100644 --- a/hub/internal/monitor/storage_fill_test.go +++ b/hub/internal/monitor/storage_fill_test.go @@ -104,7 +104,7 @@ func TestStorageFill_BornPersistent(t *testing.T) { func TestStorageFill_RootExcluded(t *testing.T) { st := newDiskStore(t) saveStorageReport(t, st, - stTarget{"local", "local", "", 96}, // PVE builtin local = root-backed → excluded by Type + stTarget{"local", "local", "", 96}, // PVE builtin local = root-backed → excluded by Type stTarget{"rootmnt", "local-dir", "/", 96}, // a target literally at "/" → excluded by mount path ) var types []string diff --git a/hub/internal/offsiteheal/reconciler.go b/hub/internal/offsiteheal/reconciler.go index 557075d6..1e48c497 100644 --- a/hub/internal/offsiteheal/reconciler.go +++ b/hub/internal/offsiteheal/reconciler.go @@ -100,7 +100,9 @@ func (a storeActions) Reissue(ctx context.Context, customerID string) error { } // NewActions builds the production mutation seam. -func NewActions(st *store.Store, reissuer Reissuer) Actions { return storeActions{st: st, reissuer: reissuer} } +func NewActions(st *store.Store, reissuer Reissuer) Actions { + return storeActions{st: st, reissuer: reissuer} +} // debounceState tracks, per customer, the last DISTINCT report seen and how many consecutive // distinct reports have carried the declaration. diff --git a/hub/internal/offsitekeys/service.go b/hub/internal/offsitekeys/service.go index 5cd77fa5..ceeb830a 100644 --- a/hub/internal/offsitekeys/service.go +++ b/hub/internal/offsitekeys/service.go @@ -6,8 +6,8 @@ import ( "errors" "fmt" "log" - "strings" "strconv" + "strings" "time" "gitea.dooplex.hu/admin/felhom-hub/internal/offsite" @@ -247,12 +247,12 @@ func (s *Service) AuditAll(ctx context.Context, windowOpen WindowOpenFunc) []Aud // ── Decision 68: the clean-up window ────────────────────────────────────────────────────────────── const ( - EventWindowDrop = "offsite_window_drop" // error: more snapshots went than a window may remove - EventWindowFailed = "offsite_window_failed" // warning: a window errored or was left open - EventGuardRefused = "offsite_prune_guard_refused" // error: the box's fake-snapshot guard refused (R-822) - EventWindowLargeGrant = "offsite_window_large_grant" // warning: the operator raised one window's cap (R-833) - windowLength = 20 * time.Minute - windowCadence = 6*24*time.Hour + 12*time.Hour // "weekly", with slack for the night chain's drift + EventWindowDrop = "offsite_window_drop" // error: more snapshots went than a window may remove + EventWindowFailed = "offsite_window_failed" // warning: a window errored or was left open + EventGuardRefused = "offsite_prune_guard_refused" // error: the box's fake-snapshot guard refused (R-822) + EventWindowLargeGrant = "offsite_window_large_grant" // warning: the operator raised one window's cap (R-833) + windowLength = 20 * time.Minute + windowCadence = 6*24*time.Hour + 12*time.Hour // "weekly", with slack for the night chain's drift ) // WindowGrant is the hub's answer to the box. diff --git a/hub/internal/offsitekeys/service_test.go b/hub/internal/offsitekeys/service_test.go index ffd8fc29..d36d81ae 100644 --- a/hub/internal/offsitekeys/service_test.go +++ b/hub/internal/offsitekeys/service_test.go @@ -2,10 +2,10 @@ package offsitekeys import ( "context" - "strings" "log" "os" "path/filepath" + "strings" "testing" "gitea.dooplex.hu/admin/felhom-hub/internal/store" diff --git a/hub/internal/semver/semver_test.go b/hub/internal/semver/semver_test.go index 37754e2f..cd10bfc0 100644 --- a/hub/internal/semver/semver_test.go +++ b/hub/internal/semver/semver_test.go @@ -10,12 +10,12 @@ func TestCompare(t *testing.T) { {"0.81.0", "0.82.0", -1}, {"0.82.0", "0.81.0", 1}, {"0.81.0", "0.81.0", 0}, - {"v0.81.0", "0.81.0", 0}, // v-prefix tolerated - {"0.100.0", "0.81.0", 1}, // numeric, not lexicographic - {"1.0.0", "0.99.9", 1}, // major - {"1.2.10", "1.2.3", 1}, // patch numeric - {"garbage", "0.81.0", 0}, // parse error → 0 (callers guard with Valid) - {"0.81", "0.81.0", 0}, // too few parts → 0 + {"v0.81.0", "0.81.0", 0}, // v-prefix tolerated + {"0.100.0", "0.81.0", 1}, // numeric, not lexicographic + {"1.0.0", "0.99.9", 1}, // major + {"1.2.10", "1.2.3", 1}, // patch numeric + {"garbage", "0.81.0", 0}, // parse error → 0 (callers guard with Valid) + {"0.81", "0.81.0", 0}, // too few parts → 0 } for _, c := range cases { if got := Compare(c.a, c.b); got != c.want { diff --git a/hub/internal/store/drtier_backfill_test.go b/hub/internal/store/drtier_backfill_test.go index 8a34e70e..6eda0699 100644 --- a/hub/internal/store/drtier_backfill_test.go +++ b/hub/internal/store/drtier_backfill_test.go @@ -58,10 +58,10 @@ func TestDRTierBackfill_InitializesFromReality(t *testing.T) { mustExec(`INSERT INTO customer_configs (customer_id, retrieval_password, api_key) VALUES (?, ?, ?)`, id, "pw", "key-"+id) } - seedCustomer("applied") // host carries an ENABLED descriptor → must flip ON - seedCustomer("disabled") // descriptor present but enabled:false → must stay OFF - seedCustomer("plain") // no descriptor at all → must stay OFF - seedCustomer("hostless") // no host row → must stay OFF + seedCustomer("applied") // host carries an ENABLED descriptor → must flip ON + seedCustomer("disabled") // descriptor present but enabled:false → must stay OFF + seedCustomer("plain") // no descriptor at all → must stay OFF + seedCustomer("hostless") // no host row → must stay OFF mustExec(`INSERT INTO hosts (host_id, customer_id, api_key, desired_json) VALUES (?, ?, ?, ?)`, "applied-01", "applied", "h1", `{"pbs_dr":{"enabled":true,"storage_id":"felhom-pbs","namespace":"applied"}}`) diff --git a/hub/internal/store/floor_test.go b/hub/internal/store/floor_test.go index a4324fa5..95d85098 100644 --- a/hub/internal/store/floor_test.go +++ b/hub/internal/store/floor_test.go @@ -76,9 +76,9 @@ func TestGlobalFloor_DBOverridesDefault(t *testing.T) { // EffectiveMinControllerVersion ever returns the global when an override is set. func TestEffectiveFloor_OverrideBeatsGlobal(t *testing.T) { s := newFloorTestStore(t) - s.SetDefaultMinControllerVersion("0.85.0") // global - mkCustomer(t, s, "c", "0.87.0") // per-customer override - mkCustomer(t, s, "other", "") // no override → uses global + s.SetDefaultMinControllerVersion("0.85.0") // global + mkCustomer(t, s, "c", "0.87.0") // per-customer override + mkCustomer(t, s, "other", "") // no override → uses global if got := s.EffectiveMinControllerVersion("c"); got != "0.87.0" { t.Errorf("effective floor for c = %q, want 0.87.0 (override beats global)", got) diff --git a/hub/internal/store/logbundle.go b/hub/internal/store/logbundle.go index ce945bdc..08f3809a 100644 --- a/hub/internal/store/logbundle.go +++ b/hub/internal/store/logbundle.go @@ -42,7 +42,7 @@ const ( // matcher: fingerprints/checksums are logged by design and are public. var secretPatterns = []*regexp.Regexp{ regexp.MustCompile(`-----BEGIN [A-Z ]*PRIVATE KEY-----`), - regexp.MustCompile(`\bre_[A-Za-z0-9]{10,}`), // Resend API key shape + regexp.MustCompile(`\bre_[A-Za-z0-9]{10,}`), // Resend API key shape regexp.MustCompile(`PVEAPIToken=[^\s"']+:\S+`), // Proxmox token WITH secret part regexp.MustCompile(`(?i)\b(password|passwd|api_key|apikey|token_secret|client_secret)=[^\s"']{6,}`), regexp.MustCompile(`Authorization:\s*Bearer\s+\S{16,}`), diff --git a/hub/internal/store/notifications_seed_test.go b/hub/internal/store/notifications_seed_test.go index 0c2c14f1..13db839f 100644 --- a/hub/internal/store/notifications_seed_test.go +++ b/hub/internal/store/notifications_seed_test.go @@ -24,10 +24,10 @@ func TestLastCustomerSentAt_PairingQuery(t *testing.T) { s := newTestStore(t) // Noise that must NOT count: - insertNotifLog(t, s, "c1", "node_down", "sent", "operator", "2026-07-22 15:00:00") // wrong channel - insertNotifLog(t, s, "c1", "node_down", "failed", "customer", "2026-07-22 16:00:00") // wrong status + insertNotifLog(t, s, "c1", "node_down", "sent", "operator", "2026-07-22 15:00:00") // wrong channel + insertNotifLog(t, s, "c1", "node_down", "failed", "customer", "2026-07-22 16:00:00") // wrong status insertNotifLog(t, s, "c1", "backup_failed", "sent", "customer", "2026-07-22 17:00:00") // wrong type - insertNotifLog(t, s, "c2", "node_down", "sent", "customer", "2026-07-22 18:00:00") // wrong customer + insertNotifLog(t, s, "c2", "node_down", "sent", "customer", "2026-07-22 18:00:00") // wrong customer // No qualifying row yet: _, ok, err := s.LastCustomerSentAt("c1", []string{"node_stale", "node_down"}) diff --git a/hub/internal/store/telemetry.go b/hub/internal/store/telemetry.go index 4ea892e0..00be4845 100644 --- a/hub/internal/store/telemetry.go +++ b/hub/internal/store/telemetry.go @@ -10,7 +10,7 @@ import ( var ( reANSI = regexp.MustCompile(`\x1b\[[0-9;]*m`) - reTimestamp = regexp.MustCompile(`\d{4}[-/]\d{2}[-/]\d{2}[T ]\d{2}:\d{2}:\d{2}[.\d]*([+-]\d{2}:?\d{2})?[Z ]?:? ?`) + reTimestamp = regexp.MustCompile(`\d{4}[-/]\d{2}[-/]\d{2}[T ]\d{2}:\d{2}:\d{2}[.\d]*([+-]\d{2}:?\d{2})?[Z ]?:? ?`) reSyslog = regexp.MustCompile(`[A-Z][a-z]{2}\s+\d{1,2} \d{2}:\d{2}:\d{2} `) ) diff --git a/hub/internal/store/wg_operator.go b/hub/internal/store/wg_operator.go index 1d78dc0c..dc719fa8 100644 --- a/hub/internal/store/wg_operator.go +++ b/hub/internal/store/wg_operator.go @@ -7,7 +7,7 @@ import ( ) // OperatorOOBNote marks the single fleet-wide operator OOB peer among wg_peers (TASK H1). It is an -// UNBOUND peer (host_id ''), so it rides the existing empty-host_id support + the partial-unique +// UNBOUND peer (host_id ”), so it rides the existing empty-host_id support + the partial-unique // idx_wg_peers_host (which only constrains non-empty host_id) without collision [OF-4]. It is pushed // to the endpoint by peersync like any peer (so the endpoint accepts the operator's handshake), and // its assigned_ip becomes each box's desired-state oob_peer_ip (rendered into wg-felhom AllowedIPs). @@ -33,7 +33,7 @@ func (s *Store) GetOperatorOOBPeer() (*WGPeer, error) { // endpoint's STATIC forward chain can hardcode the operator /32). Last-write-wins: any prior // operator-oob row is replaced (key rotation / IP change). Validated: assignedIP is an IPv4 host // address inside the endpoint's tunnel_subnet, not the network/broadcast/pbs_tunnel_ip, and not -// already taken by a DIFFERENT (customer) peer. host_id stays '' (unbound) so it is never counted as +// already taken by a DIFFERENT (customer) peer. host_id stays ” (unbound) so it is never counted as // a customer host. func (s *Store) SetOperatorOOBPeer(pubkey, assignedIP string) error { ip, err := netip.ParseAddr(assignedIP) @@ -100,7 +100,9 @@ const oobOperatorSSHKeyKey = "oob_operator_ssh_pubkey" // SetOOBOperatorSSHKey stores the operator's SSH PUBLIC key (an authorized_keys line, delivered to // every box via the desired-state so felhom-sshd honours the operator login). "" clears it. -func (s *Store) SetOOBOperatorSSHKey(pubkey string) error { return s.setSetting(oobOperatorSSHKeyKey, pubkey) } +func (s *Store) SetOOBOperatorSSHKey(pubkey string) error { + return s.setSetting(oobOperatorSSHKeyKey, pubkey) +} // GetOOBOperatorSSHKey returns the operator SSH public key ("" if unset). func (s *Store) GetOOBOperatorSSHKey() string { return s.getSetting(oobOperatorSSHKeyKey) } diff --git a/hub/internal/web/artifact_installability_test.go b/hub/internal/web/artifact_installability_test.go index 1d36dbd0..a5948d66 100644 --- a/hub/internal/web/artifact_installability_test.go +++ b/hub/internal/web/artifact_installability_test.go @@ -29,7 +29,7 @@ type fakeRegistry struct { downloadGone map[string]bool // shaless models a version whose metadata answers but carries no sha256. shaless map[string]bool - hits int + hits int } func (f *fakeRegistry) start(t *testing.T) *gitea.Client { diff --git a/hub/internal/web/customer_tabs_test.go b/hub/internal/web/customer_tabs_test.go index 5ab32fee..df7e1fb8 100644 --- a/hub/internal/web/customer_tabs_test.go +++ b/hub/internal/web/customer_tabs_test.go @@ -116,10 +116,10 @@ func TestTemplates_CustomerHostTab(t *testing.T) { // The Host tab panel carries the host_detail_body content. for _, want := range []string{ - "demo-felhom-01", // host identity - "Storage Targets", // shared body section - "felhom-usb", // storage target from the report - ">9201<", // guest vmid + "demo-felhom-01", // host identity + "Storage Targets", // shared body section + "felhom-usb", // storage target from the report + ">9201<", // guest vmid `href="/hosts/demo-felhom-01"`, // cross-link to the standalone page } { if !strings.Contains(html, want) { diff --git a/hub/internal/web/funcmap_test.go b/hub/internal/web/funcmap_test.go index 0b5bd12e..a7968cc7 100644 --- a/hub/internal/web/funcmap_test.go +++ b/hub/internal/web/funcmap_test.go @@ -13,15 +13,15 @@ func TestStatusColorTruthTable(t *testing.T) { status string want string }{ - {"ok", "nominal"}, // operating normally — blue - {"warn", "warn"}, // degraded / stale report — amber - {"stale", "warn"}, // host stale state - {"down", "crit"}, // outage — red - {"fail", "crit"}, // health=fail — red - {"pending", "neutral"}, // not-yet-provisioned customer is a NORMAL fleet state - {"disabled", "neutral"}, // deliberately paused — not a deviation - {"blocked", "warn"}, // operator cut-off: intentional but attention-worthy - {"bogus", "neutral"}, // unknown values stay quiet + {"ok", "nominal"}, // operating normally — blue + {"warn", "warn"}, // degraded / stale report — amber + {"stale", "warn"}, // host stale state + {"down", "crit"}, // outage — red + {"fail", "crit"}, // health=fail — red + {"pending", "neutral"}, // not-yet-provisioned customer is a NORMAL fleet state + {"disabled", "neutral"}, // deliberately paused — not a deviation + {"blocked", "warn"}, // operator cut-off: intentional but attention-worthy + {"bogus", "neutral"}, // unknown values stay quiet {"", "neutral"}, } for _, c := range cases { diff --git a/hub/internal/web/offsite.go b/hub/internal/web/offsite.go index 18f8eb44..cd231c87 100644 --- a/hub/internal/web/offsite.go +++ b/hub/internal/web/offsite.go @@ -143,14 +143,14 @@ func (s *Server) handleOffsite(w http.ResponseWriter, r *http.Request) { pbsView := s.pbsdrBoxData() // R-5 v0.65.0 PBS-DR datastore panel (PBS DR tab) data := map[string]interface{}{ - "Endpoints": cards, - "HasEndpoints": len(cards) > 0, - "Peers": rows, - "OffsiteBox": boxView, - "OffsiteCusts": custRows, - "PBSBox": pbsView, - "CSRFToken": s.getCSRFToken(r), - "Flash": r.URL.Query().Get("flash"), + "Endpoints": cards, + "HasEndpoints": len(cards) > 0, + "Peers": rows, + "OffsiteBox": boxView, + "OffsiteCusts": custRows, + "PBSBox": pbsView, + "CSRFToken": s.getCSRFToken(r), + "Flash": r.URL.Query().Get("flash"), } if err := s.templates.ExecuteTemplate(w, "offsite.html", data); err != nil { s.logger.Printf("[ERROR] offsite.html template: %v", err) diff --git a/hub/internal/web/r331_backup_card_test.go b/hub/internal/web/r331_backup_card_test.go index d4c80220..af1f50b1 100644 --- a/hub/internal/web/r331_backup_card_test.go +++ b/hub/internal/web/r331_backup_card_test.go @@ -147,7 +147,10 @@ func TestFmtBytesAuto_ScalesInsteadOfCollapsingToZero(t *testing.T) { // The Offsite page's fmtBytesGB renders demo-hp's real size as "0.1 GB". On a card whose entire // defect was under-reporting a real backup, that reads as "nearly nothing" — which is why this // formatter exists rather than reusing that one. - for _, tc := range []struct{ in int64; want string }{ + for _, tc := range []struct { + in int64 + want string + }{ {140829678, "134.3 MB"}, // demo-hp, measured {0, "0 B"}, {1 << 10, "1.0 KB"}, diff --git a/hub/internal/web/tokens.go b/hub/internal/web/tokens.go index 3cd21611..7f47bc37 100644 --- a/hub/internal/web/tokens.go +++ b/hub/internal/web/tokens.go @@ -254,7 +254,6 @@ func (s *Server) tokenRows() []tokenRow { // tokenDaysChoices are the expiry options of the create form; "never" is not offered. var tokenDaysChoices = []int{30, tokenDefaultDays, 180, tokenMaxDays} - // Handler is the operator web surface as main mounts it: ServeHTTP behind RequireAuth, unconditionally. func (s *Server) Handler() http.Handler { return s.RequireAuth(http.HandlerFunc(s.ServeHTTP))