hub v0.41.0: OffsiteChecker (fill 90/95 + staleness >48h) + operator freeze lever (SLICE 4)

Sibling checker over the controller report's offsite object: quota-fill
warn/crit + the silently-stuck staleness detector (escrowed-only,
red-proofed; nil-safe on pre-v0.109 reports; same-second tie-guard).
SetOffsiteFrozen flips ONLY readonly on the exactly-1 labelled sub-account
(SSH preserved); Freeze/Unfreeze buttons — manual only, never automatic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-09 23:57:12 +02:00
parent cb26dc7e83
commit fad5573dd3
10 changed files with 543 additions and 0 deletions
+40
View File
@@ -581,6 +581,46 @@ func (s *Server) handleOffsiteReissue(w http.ResponseWriter, r *http.Request, cu
http.Redirect(w, r, "/customers/"+customerID+"?flash=offsite_reissued", http.StatusSeeOther)
}
// handleOffsiteFreeze (SLICE 4) freezes/unfreezes the customer's shared sub-account (readonly) — an
// OPERATOR lever, never automatic (freezing also blocks prune, the customer's only way down from
// over-quota). Shared model only; the exactly-1 label guard lives in the provisioner. Action logged,
// no secrets involved.
func (s *Server) handleOffsiteFreeze(w http.ResponseWriter, r *http.Request, customerID string, frozen bool) {
if s.offsite == nil {
http.Error(w, "Offsite provisioning is not configured on this hub", http.StatusBadGateway)
return
}
cfg, err := s.store.GetCustomerConfig(customerID)
if err != nil || cfg == nil {
http.NotFound(w, r)
return
}
var overrides struct {
Offsite struct {
Enabled bool `json:"enabled"`
Type string `json:"type"`
} `json:"offsite"`
}
_ = json.Unmarshal([]byte(cfg.ConfigJSON), &overrides)
if !overrides.Offsite.Enabled || overrides.Offsite.Type != "shared" {
http.Error(w, "Freeze applies to a provisioned SHARED offsite tier only", http.StatusBadRequest)
return
}
ctx, cancel := context.WithTimeout(context.WithoutCancel(r.Context()), 2*time.Minute)
defer cancel()
if err := s.offsite.SetOffsiteFrozen(ctx, customerID, frozen); err != nil {
s.logger.Printf("[ERROR] offsite freeze(%v) for %s: %v", frozen, customerID, err)
http.Error(w, "Offsite freeze/unfreeze failed: "+err.Error(), http.StatusBadGateway)
return
}
s.logger.Printf("[INFO] offsite frozen=%v (readonly) for %s (operator action)", frozen, customerID)
flash := "offsite_frozen"
if !frozen {
flash = "offsite_unfrozen"
}
http.Redirect(w, r, "/customers/"+customerID+"?flash="+flash, http.StatusSeeOther)
}
// handleConfigDelete deletes a customer config.
func (s *Server) handleConfigDelete(w http.ResponseWriter, r *http.Request, customerID string) {
if err := s.store.DeleteCustomerConfig(customerID); err != nil {