Documentation
¶
Overview ¶
Package responsesaga is the pure-domain state machine for a GOVERNED response action's full lifecycle (Phase C, C6 #680) — the distributed saga from proposal through approval, agent execution, and a TELEMETRY-VERIFIED post-condition to an optional rollback. Its defining discipline: CommandApplied is NOT VerifiedSucceeded — a successful `kill` syscall is not proof of containment — so a response can only reach StateVerifiedSucceeded by passing through StateVerifying (a telemetry check), never directly from StateCommandApplied. Targets are identified by a stable TargetFingerprint (never a bare PID), and each at-least-once execution attempt is journaled with an idempotency key so a re-issue cannot double-apply.
This package owns the saga model + transition rules; the agent-side execution, the live telemetry-verification, and the wiring onto the incident (C1 ResponseRequested/ResponseVerified events) are composed on top of it.
Index ¶
- Variables
- func CanTransition(from, to SagaState) bool
- type FingerprintKind
- type ResponseAttempt
- type ReversibilityClass
- type Saga
- func (s *Saga) ActionID() shared.ID
- func (s *Saga) Attempts() []ResponseAttempt
- func (s *Saga) Contained() bool
- func (s *Saga) RecordAttempt(a ResponseAttempt) error
- func (s *Saga) Reversibility() ReversibilityClass
- func (s *Saga) State() SagaState
- func (s *Saga) Target() TargetFingerprint
- func (s *Saga) Transition(to SagaState) error
- type SagaState
- type TargetFingerprint
- type VerificationOutcome
Constants ¶
This section is empty.
Variables ¶
var ErrHaltLatched = errors.New("response halt is latched")
ErrHaltLatched means tenant response dispatch remains disabled until a separately governed resume.
var ErrStaleHaltGeneration = errors.New("stale response halt generation")
ErrStaleHaltGeneration means a kill-switch fence advanced after an operation began admission.
Functions ¶
func CanTransition ¶
CanTransition reports whether from -> to is a legal saga transition.
Types ¶
type FingerprintKind ¶
type FingerprintKind string
FingerprintKind is the sort of target a response acts on.
const ( FingerprintProcess FingerprintKind = "process" FingerprintFile FingerprintKind = "file" FingerprintHost FingerprintKind = "host" )
type ResponseAttempt ¶
type ResponseAttempt struct {
ActionID shared.ID
Attempt int
IdempotencyKey string
Target TargetFingerprint
IsReversal bool
State SagaState
CommandOutcome string
VerificationOutcome VerificationOutcome
HaltGeneration int64
ObservedRadius offensivepolicy.Radius
AffectedCount int
AlreadyApplied bool
DecidedBy string
ExecutorID string
ExecutorAgentID shared.ID
VerificationChallenge string
VerifierID string
VerificationEvidenceID shared.ID
At time.Time
DeadlineAt time.Time
TerminalReason string
}
ResponseAttempt is one control-plane execution attempt persisted before dispatch. The endpoint executor must additionally journal the IdempotencyKey locally before crossing its side-effect boundary; this record alone cannot make delivery idempotent. TargetFingerprint pins exactly what was acted on.
func (ResponseAttempt) Validate ¶
func (a ResponseAttempt) Validate() error
Validate enforces a well-formed attempt.
type ReversibilityClass ¶
type ReversibilityClass string
ReversibilityClass states how reversible a response is — a reversal COMMAND is not the same as restored STATE, so this is declared up front and a rollback still carries its own verified post-condition.
const ( ReversibilityGuaranteed ReversibilityClass = "guaranteed" ReversibilityCompensating ReversibilityClass = "compensating" ReversibilityBestEffort ReversibilityClass = "best_effort" ReversibilityIrreversible ReversibilityClass = "irreversible" )
func (ReversibilityClass) Valid ¶
func (r ReversibilityClass) Valid() bool
Valid reports whether r is a known reversibility class.
type Saga ¶
type Saga struct {
// contains filtered or unexported fields
}
Saga is the governed-response state machine for one action: its target, declared reversibility, current state, and the journal of execution attempts. Its safety-critical fields are UNEXPORTED so the only way to advance state is Transition — a caller cannot assign state = StateVerifiedSucceeded directly and fabricate a "contained" verdict that skipped the telemetry Verifying gate, nor swap the validated Target for an unvalidated one. Construct with NewSaga; read via the getters.
func NewSaga ¶
func NewSaga(actionID shared.ID, target TargetFingerprint, reversibility ReversibilityClass) (*Saga, error)
NewSaga starts a saga in StateProposed for a validated target + reversibility class.
func (*Saga) Attempts ¶
func (s *Saga) Attempts() []ResponseAttempt
Attempts returns a copy of the journaled attempts in record order.
func (*Saga) Contained ¶
Contained reports whether the response reached a telemetry-verified success — StateVerifiedSucceeded, or StateCompleted (verified then finalized without a rollback). It is NEVER true for StateCommandApplied on its own: a command being issued is not proof the post-condition held.
func (*Saga) RecordAttempt ¶
func (s *Saga) RecordAttempt(a ResponseAttempt) error
RecordAttempt journals an execution attempt, idempotently by IdempotencyKey: re-recording an identical attempt under a seen key is a no-op (so an at-least-once re-issue does not double-journal). The attempt must be valid and belong to this saga's action. A seen key re-used for a DIFFERENT attempt is rejected — silently masking it would hide a caller bug that could double-apply a distinct destructive action.
func (*Saga) Reversibility ¶
func (s *Saga) Reversibility() ReversibilityClass
Reversibility is the declared reversibility class of the response.
func (*Saga) Target ¶
func (s *Saga) Target() TargetFingerprint
Target is the stable fingerprint of what the response acts on.
func (*Saga) Transition ¶
Transition advances the saga to a new state, rejecting an illegal transition (in particular CommandApplied -> VerifiedSucceeded, which must pass through Verifying).
type SagaState ¶
type SagaState string
SagaState is a stage in the governed-response lifecycle.
const ( StateProposed SagaState = "proposed" StateAwaitingApproval SagaState = "awaiting_approval" StateApproved SagaState = "approved" StateRejected SagaState = "rejected" StateIssued SagaState = "issued" StateClaimed SagaState = "claimed" StateExecuting SagaState = "executing" StateCommandApplied SagaState = "command_applied" StateCommandFailed SagaState = "command_failed" StateOutcomeUnknown SagaState = "outcome_unknown" StateVerifying SagaState = "verifying" StateVerifiedSucceeded SagaState = "verified_succeeded" StateVerificationFailed SagaState = "verification_failed" StateVerificationUnknown SagaState = "verification_unknown" StateTimedOut SagaState = "timed_out" StateManualIntervention SagaState = "manual_intervention_required" StateRollbackRequested SagaState = "rollback_requested" StateRollingBack SagaState = "rolling_back" StateRollbackVerifying SagaState = "rollback_verifying" StateRollbackUnknown SagaState = "rollback_outcome_unknown" StateRolledBack SagaState = "rolled_back" StateRollbackFailed SagaState = "rollback_failed" // StateCompleted is the terminal state of a telemetry-verified response that was accepted and NOT // reverted, so a contained response reaches a real end state — a worker keying "done" off Terminal() // would otherwise loop forever on a StateVerifiedSucceeded whose only other exit is a rollback. StateCompleted SagaState = "completed" )
type TargetFingerprint ¶
type TargetFingerprint struct {
Kind FingerprintKind
// process
ProcessAssetID shared.ID
ProcessEntityID shared.ID
// file
FilePath string
FileDevice uint64
FileInode uint64
FileHash string
// host
HostID shared.ID
NetpolGeneration int64
}
TargetFingerprint stably identifies what a response acts on, so a re-issued action cannot hit the wrong thing after a PID/path is recycled. It is NEVER a bare PID: a process target is the A1 ProcessEntityID (hash of asset+boot+pid+start-time); a file target is device+inode (+optional content hash), not just a rebindable path; a host target is the host id plus the network-policy generation the action assumes, so a stale management-channel change is detectable.
func (TargetFingerprint) Validate ¶
func (f TargetFingerprint) Validate() error
Validate enforces a stable, non-PID identity appropriate to the kind.
type VerificationOutcome ¶
type VerificationOutcome string
VerificationOutcome is the result of checking a response's post-condition via telemetry. Note the explicit "unknown, insufficient coverage" — the honest answer when the telemetry needed to confirm the effect was not observed; it is NOT treated as success.
const ( VerificationSucceeded VerificationOutcome = "succeeded" VerificationFailed VerificationOutcome = "failed" VerificationUnknown VerificationOutcome = "unknown_insufficient_coverage" VerificationTimedOut VerificationOutcome = "timed_out" VerificationPending VerificationOutcome = "" // not yet verified )
func (VerificationOutcome) Valid ¶
func (v VerificationOutcome) Valid() bool
Valid reports whether v is a known (or the pending zero) outcome.