hub v0.128.0: set-aside deletion through the hub after a 7-day wait (decision 74, R-823), key-file clean-up route (R-826), read-only key check (R-827), window cap = half
gates / gates (push) Successful in 29s
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:
@@ -72,14 +72,17 @@ type Server struct {
|
||||
// offsiteWindowAdmin: operator one-shot grant / weekly switch (decision 68). nil → 503.
|
||||
offsiteWindowGrant func(customerID string) error
|
||||
offsiteWindowSwitch func(on bool) error
|
||||
offsiteBox func() (monitor.BoxSnapshot, bool) // optional (v0.64.0, R-5); the restic pool-box aggregate snapshot accessor
|
||||
pbsdrBox func() (monitor.PBSBoxSnapshot, bool) // optional (v0.65.0, R-5); the PBS-DR datastore fill snapshot accessor
|
||||
tenantsync tenancyProvisioner // optional; enables PBS DR tier provisioning (web/pbsdr.go)
|
||||
claimEngine *claim.Engine // optional; enables the customer-claim resend button (v0.50.0)
|
||||
selfBindMailer SelfBindMailer // optional; enables the customer self-bind link button (v0.66.0, R-27)
|
||||
bindLimiter *bindRateLimiter // per-IP throttle for the PUBLIC /bind/ surface (v0.66.0, R-27)
|
||||
bindResendMu sync.Mutex // R-719: the fresh-link resend limiter
|
||||
bindResendAt map[string]time.Time // customer → last fresh-link mail (R-719)
|
||||
// operator key-file clean-up (decision 72) and abandonment cancel (decision 74). nil → 503.
|
||||
offsiteRemoveUnpinned func(ctx context.Context, customerID string) (int, error)
|
||||
offsiteAbandonCancel func(customerID, by string) (int, error)
|
||||
offsiteBox func() (monitor.BoxSnapshot, bool) // optional (v0.64.0, R-5); the restic pool-box aggregate snapshot accessor
|
||||
pbsdrBox func() (monitor.PBSBoxSnapshot, bool) // optional (v0.65.0, R-5); the PBS-DR datastore fill snapshot accessor
|
||||
tenantsync tenancyProvisioner // optional; enables PBS DR tier provisioning (web/pbsdr.go)
|
||||
claimEngine *claim.Engine // optional; enables the customer-claim resend button (v0.50.0)
|
||||
selfBindMailer SelfBindMailer // optional; enables the customer self-bind link button (v0.66.0, R-27)
|
||||
bindLimiter *bindRateLimiter // per-IP throttle for the PUBLIC /bind/ surface (v0.66.0, R-27)
|
||||
bindResendMu sync.Mutex // R-719: the fresh-link resend limiter
|
||||
bindResendAt map[string]time.Time // customer → last fresh-link mail (R-719)
|
||||
// intentHub (v0.58.0, Direction-2 immediate-sync) is Bumped by every operator-intent handler
|
||||
// (config save/delete, claim resend, offsite re-issue/freeze, floor, block/unblock, log pull)
|
||||
// so a box long-polling GET /api/v1/wait wakes in seconds. Shared with the API handler. nil =
|
||||
@@ -202,6 +205,11 @@ func (s *Server) SetOffsiteProvisioner(p *offsite.Provisioner) { s.offsite = p }
|
||||
// SetOffsiteKeyAudit wires the on-demand run of the daily off-site key check (decision 69).
|
||||
func (s *Server) SetOffsiteKeyAudit(fn func(ctx context.Context) any) { s.offsiteKeyAudit = fn }
|
||||
|
||||
// SetOffsiteKeyAdmin wires the operator's key-file clean-up and abandonment cancel.
|
||||
func (s *Server) SetOffsiteKeyAdmin(remove func(context.Context, string) (int, error), cancel func(string, string) (int, error)) {
|
||||
s.offsiteRemoveUnpinned, s.offsiteAbandonCancel = remove, cancel
|
||||
}
|
||||
|
||||
// SetOffsiteWindowAdmin wires the operator's window controls (decision 68).
|
||||
func (s *Server) SetOffsiteWindowAdmin(grant func(string) error, sw func(bool) error) {
|
||||
s.offsiteWindowGrant, s.offsiteWindowSwitch = grant, sw
|
||||
@@ -617,6 +625,26 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
s.handleConfigEditForm(w, r, customerID)
|
||||
}
|
||||
case strings.HasPrefix(path, "/offsite/remove-unpinned/") || strings.HasPrefix(path, "/offsite/abandon-cancel/"):
|
||||
// Operator: rewrite a sub-account's key file keeping only pinned lines (decision 72); cancel a
|
||||
// household's pending set-aside deletion (decision 74).
|
||||
if r.Method != http.MethodPost || s.offsiteRemoveUnpinned == nil {
|
||||
http.Error(w, "unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
var n int
|
||||
var err error
|
||||
if strings.HasPrefix(path, "/offsite/remove-unpinned/") {
|
||||
n, err = s.offsiteRemoveUnpinned(r.Context(), strings.TrimPrefix(path, "/offsite/remove-unpinned/"))
|
||||
} else {
|
||||
n, err = s.offsiteAbandonCancel(strings.TrimPrefix(path, "/offsite/abandon-cancel/"), "operator")
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]int{"changed": n})
|
||||
case strings.HasPrefix(path, "/offsite/window-grant/") || path == "/offsite/windows-enabled":
|
||||
// Operator (decision 68): a one-shot grant lets the customer's NEXT window request through;
|
||||
// the switch turns the WEEKLY window on/off fleet-wide (off = the interim: nothing prunes).
|
||||
|
||||
Reference in New Issue
Block a user