v0.23.0: device-ROLE classification + tiered storage-wipe gate (user-data customer-confirmable; system/backup operator-only)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+23
-11
@@ -43,7 +43,7 @@ import (
|
||||
|
||||
// version is the agent version. Overridable at build time with
|
||||
// -ldflags "-X main.version=<v>"; defaults to the in-repo CHANGELOG version.
|
||||
var version = "0.22.0"
|
||||
var version = "0.23.0"
|
||||
|
||||
func main() {
|
||||
var (
|
||||
@@ -580,21 +580,33 @@ func buildLocalAPIServer(cfg config.Config, px *proxmox.Client, store *backup.St
|
||||
return srv
|
||||
}
|
||||
|
||||
// storageGateAdapter bridges the local-API disk-format path to the slice-4 reversibility gate
|
||||
// (8C): a data-bearing format is a ClassStorageWipe destructive op, so with no operator signature
|
||||
// the gate returns pending_signature and the agent refuses. The signed completion is slice 10.
|
||||
// storageGateAdapter bridges the local-API disk-format path to the reversibility gate, TIERED by the
|
||||
// agent's authoritative device-role verdict: a USER-DATA data-bearing wipe is customer-confirmable
|
||||
// (durable-id-bound, no signature); a SYSTEM/BACKUP wipe stays operator-signature (unsigned →
|
||||
// pending_signature; the signed completion runs via the signed-jobs runner).
|
||||
type storageGateAdapter struct {
|
||||
gate *reconcile.Gate
|
||||
hostID string
|
||||
}
|
||||
|
||||
func (a storageGateAdapter) AuthorizeWipe(device string) (bool, string) {
|
||||
params, _ := json.Marshal(map[string]string{"device": device, "op": "format"})
|
||||
dec := a.gate.Authorize(
|
||||
reconcile.IntentForStorageDestructive(reconcile.ClassStorageWipe, a.hostID, device, params, reconcile.SourceOneShotJob),
|
||||
nil, // no operator signature in 8C → pending_signature
|
||||
)
|
||||
return dec.Allowed, string(dec.Reason)
|
||||
func (a storageGateAdapter) AuthorizeWipe(req localapi.WipeRequest) localapi.WipeDecision {
|
||||
dec := a.gate.AuthorizeStorageWipe(reconcile.StorageWipeAuthz{
|
||||
HostID: a.hostID,
|
||||
Role: req.Role,
|
||||
DeviceDurableID: req.DeviceDurableID,
|
||||
Confirmed: req.Confirmed,
|
||||
ConfirmDurableID: req.ConfirmDurableID,
|
||||
}, nil) // no operator signature on the inline path → system/backup is pending_signature
|
||||
tier := "destructive"
|
||||
if dec.Disposition == reconcile.CustomerConfirmable {
|
||||
tier = "customer_confirmable"
|
||||
}
|
||||
return localapi.WipeDecision{
|
||||
Allowed: dec.Allowed,
|
||||
Tier: tier,
|
||||
Reason: string(dec.Reason),
|
||||
NeedsConfirmation: dec.Reason == reconcile.ReasonPendingConfirmation,
|
||||
}
|
||||
}
|
||||
|
||||
// reconcileJournalPath chooses the op-journal path: a `journal.log` sibling of the
|
||||
|
||||
Reference in New Issue
Block a user