Remediation¶
s.a.u never executes a destructive cloud operation without a compensation handler. Here is the full lifecycle.
Architecture Overview¶
sequenceDiagram
participant User
participant DEVI
participant s.a.u
participant Plugin
User->>DEVI: "Quarantine this idle instance"
DEVI->>SAU: request_quarantine(crn, justification)
SAU->>User: HUMAN-IN-THE-LOOP: Approve?
User->>SAU: Approved
SAU->>Plugin: CheckReversibility(crn)
Plugin-->>SAU: Safe=true (no termination protection)
SAU->>Plugin: Remediate(crn, "stop", dry_run=true)
Plugin-->>SAU: dry_run OK
SAU->>Plugin: Remediate(crn, "stop", dry_run=false)
Plugin-->>SAU: Success, snapshot={ami_id: "ami-xyz"}
SAU->>SAU: Record CRDT DeltaState for rollback
Key principle: The engine never makes cloud API calls. Plugins do. The engine only knows about CRNs, actions, and snapshots.
The Remediation Lifecycle¶
Quarantine Request¶
DEVI or Human
Via the DEVI chat in the Web Dashboard or TUI:
"Stop the idle t2.micro in us-east-1"
DEVI calls the request_quarantine MCP tool. s.a.u intercepts and holds for human approval.
Human Approval¶
The TUI, Web UI, or Slack integration presents the quarantine request with:
- The resource CRN
- DEVI's justification
- Estimated monthly savings
- CheckReversibility result (are protection flags set?)
Pre-Flight Validation¶
// The engine calls the plugin's Remediate with dry_run=true
resp, err := plugin.Remediate(ctx, &RemediateRequest{
Crn: "crn:devi:aws:us-east-1:123:ec2:instance:i-0abc",
Action: "stop",
DryRun: true,
Properties: map[string]string{
"region": "us-east-1",
},
})
Saga Execution¶
s.a.u wraps the remediation in a Saga with automatic rollback:
saga := &crdt.Saga{
Steps: []crdt.Step{
{
Execute: func(ctx context.Context) error {
// Record pre-remediation state snapshot
return remediator.RecordState(ctx, node)
},
Compensate: func(ctx context.Context) error {
// Restore from CRDT DeltaState
return remediator.Restore(ctx, node)
},
},
{
Execute: func(ctx context.Context) error {
// Call plugin.Remediate over gRPC
return remediator.Purge(ctx, node)
},
Compensate: func(ctx context.Context) error {
// Plugin-specific rollback using snapshot
return remediator.Rollback(ctx, node, snapshot)
},
},
},
}
Snapshot Recording¶
Every plugin returns a Snapshot map in the RemediateResponse:
| Cloud | Action | Snapshot Contains |
|---|---|---|
| AWS EC2 | stop | ami_id (pre-stop AMI) |
| AWS EBS | delete | snapshot_id (EBS snapshot) |
| AWS RDS | delete | final_snapshot_id (DB snapshot) |
| GCP GCE | stop | operation (GCE operation name) |
| GCP Disk | delete | snapshot_name (GCE disk snapshot) |
| Azure VM | stop | resource_group, vm_name |
| Linode | stop | linode_id |
Reversibility¶
Automatic Rollback¶
If any Saga step fails, s.a.u executes compensating operations in reverse order:
func (m *SAUEngine) rollback(ctx context.Context, s *Saga, failedIndex int) {
// Detach from parent cancellation to guarantee rollback time
unwindCtx, cancel := context.WithTimeout(
context.WithoutCancel(ctx), 5*time.Minute,
)
defer cancel()
for i := failedIndex - 1; i >= 0; i-- {
_ = s.Steps[i].Compensate(unwindCtx)
}
}
Manual Rollback¶
s.a.u retrieves the CRDT DeltaState payload and re-creates the resource from the snapshot.
Plugin Remediation Actions¶
AWS Plugin¶
cloudslash-plugin-aws
| Resource | Action | What Happens |
|---|---|---|
| EC2 Instance | stop |
CreateImage (AMI) → StopInstances |
| EC2 Instance | terminate |
TerminateInstances |
| EBS Volume | delete |
CreateSnapshot → DeleteVolume |
| RDS Instance | stop |
StopDBInstance |
| RDS Instance | delete |
CreateFinalSnapshot → DeleteDBInstance |
| Lambda Function | throttle |
PutFunctionConcurrency(0) |
GCP Plugin¶
cloudslash-plugin-gcp
| Resource | Action | What Happens |
|---|---|---|
| GCE Instance | stop |
instances.Stop() |
| GCE Instance | delete |
instances.Delete() |
| Persistent Disk | delete |
CreateSnapshot → disks.Delete() |
| Cloud SQL | stop |
Patch ActivationPolicy=NEVER |
| Cloud SQL | delete |
instances.Delete() |
Azure Plugin¶
cloudslash-plugin-azure
| Resource | Action | What Happens |
|---|---|---|
| Virtual Machine | stop |
BeginDeallocate() |
| Virtual Machine | delete |
BeginDelete() |
| Managed Disk | delete |
BeginDelete() |
| SQL Database | stop |
BeginPause() |
| SQL Database | delete |
BeginDelete() |
Linode Plugin¶
cloudslash-plugin-linode
| Resource | Action | What Happens |
|---|---|---|
| Linode Instance | stop |
ShutdownInstance() |
| Linode Instance | delete |
DeleteInstance() |
| Volume | detach |
DetachVolume() |
| Volume | delete |
DeleteVolume() |
| NodeBalancer | delete |
DeleteNodeBalancer() |
CheckReversibility¶
Before any remediation, the engine calls CheckReversibility to detect protection flags:
| Cloud | What It Checks |
|---|---|
| AWS | EC2 termination protection (DisableApiTermination) |
| GCP | GCE deletion protection (deletionProtection) |
| Azure | do-not-delete tag, env=production tag |
| Linode | production tag |
If a protection flag is detected, the remediation is blocked and you are notified.