responsesaga

package
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
var ErrHaltLatched = errors.New("response halt is latched")

ErrHaltLatched means tenant response dispatch remains disabled until a separately governed resume.

View Source
var ErrStaleHaltGeneration = errors.New("stale response halt generation")

ErrStaleHaltGeneration means a kill-switch fence advanced after an operation began admission.

Functions

func CanTransition

func CanTransition(from, to SagaState) bool

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) ActionID

func (s *Saga) ActionID() shared.ID

ActionID is the action this saga governs.

func (*Saga) Attempts

func (s *Saga) Attempts() []ResponseAttempt

Attempts returns a copy of the journaled attempts in record order.

func (*Saga) Contained

func (s *Saga) Contained() bool

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) State

func (s *Saga) State() SagaState

State is the current saga state.

func (*Saga) Target

func (s *Saga) Target() TargetFingerprint

Target is the stable fingerprint of what the response acts on.

func (*Saga) Transition

func (s *Saga) Transition(to SagaState) error

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"
)

func (SagaState) Terminal

func (s SagaState) Terminal() bool

Terminal reports whether s has no outgoing transitions.

func (SagaState) Valid

func (s SagaState) Valid() bool

Valid reports whether s is a known state.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL