hub v0.44.0: PBS DR tier SLICE 1 — felhom-tenantsync surface (script+client) + hub provisioning flow (consume-once host secret, pbs_dr desired-state descriptor, fail-closed + idempotent, re-issue)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-10 20:49:48 +02:00
parent 00afadc1fe
commit ce6a56691e
19 changed files with 1743 additions and 0 deletions
+21
View File
@@ -413,12 +413,14 @@ func (s *Server) handleConfigNewForm(w http.ResponseWriter, r *http.Request) {
ActiveNav string
Error string
CSRFField template.HTML
PBSDR pbsDRView
}{
IsNew: true,
Config: &store.CustomerConfig{},
Overrides: make(map[string]interface{}),
ActiveNav: "configs",
CSRFField: s.csrfField(r),
PBSDR: s.pbsDRViewFor(""),
}
s.templates.ExecuteTemplate(w, "config_form.html", data)
}
@@ -484,6 +486,13 @@ func (s *Server) handleConfigCreate(w http.ResponseWriter, r *http.Request) {
return
}
// PBS DR tier (fail-closed, same discipline; the descriptor lives in the HOST desired-state).
if err := s.applyPBSDR(r.Context(), r, cfg); err != nil {
s.logger.Printf("[ERROR] pbsdr provision for %s: %v", customerID, err)
http.Error(w, "PBS DR provisioning failed: "+err.Error(), http.StatusBadGateway)
return
}
if err := s.store.SaveCustomerConfig(cfg); err != nil {
s.logger.Printf("[ERROR] Failed to save config for %s: %v", customerID, err)
http.Error(w, "Internal error", http.StatusInternalServerError)
@@ -512,12 +521,14 @@ func (s *Server) handleConfigEditForm(w http.ResponseWriter, r *http.Request, cu
ActiveNav string
Error string
CSRFField template.HTML
PBSDR pbsDRView
}{
IsNew: false,
Config: cfg,
Overrides: overrides,
ActiveNav: "configs",
CSRFField: s.csrfField(r),
PBSDR: s.pbsDRViewFor(customerID),
}
s.templates.ExecuteTemplate(w, "config_form.html", data)
}
@@ -546,6 +557,14 @@ func (s *Server) handleConfigUpdate(w http.ResponseWriter, r *http.Request, cust
return
}
// PBS DR tier (fail-closed; idempotent on an already-provisioned descriptor — no re-key,
// no second secret, no spurious generation bump).
if err := s.applyPBSDR(r.Context(), r, cfg); err != nil {
s.logger.Printf("[ERROR] pbsdr provision for %s: %v", customerID, err)
http.Error(w, "PBS DR provisioning failed: "+err.Error(), http.StatusBadGateway)
return
}
if err := s.store.SaveCustomerConfig(cfg); err != nil {
s.logger.Printf("[ERROR] Failed to update config for %s: %v", customerID, err)
http.Error(w, "Internal error", http.StatusInternalServerError)
@@ -897,6 +916,7 @@ func (s *Server) renderConfigForm(w http.ResponseWriter, r *http.Request, isNew
ActiveNav string
Error string
CSRFField template.HTML
PBSDR pbsDRView
}{
IsNew: isNew,
Config: cfg,
@@ -904,6 +924,7 @@ func (s *Server) renderConfigForm(w http.ResponseWriter, r *http.Request, isNew
ActiveNav: "configs",
Error: errMsg,
CSRFField: s.csrfField(r),
PBSDR: s.pbsDRViewFor(cfg.CustomerID),
}
s.templates.ExecuteTemplate(w, "config_form.html", data)
}