Documentation
¶
Overview ¶
Package approval implements the ApprovalRequest FSM service.
States: PENDING -> APPROVED | DENIED | EXPIRED
Transitions are single-direction and fail-closed: any attempt to decide an already-decided approval returns ErrAlreadyDecided. Every state change emits an audit event with actor_type=human (for decisions) or actor_type=system (for expirations).
Pure business logic: storage is injected via the Store interface so this package can be tested with an in-memory fake.
Index ¶
- Variables
- func Decide(ctx context.Context, st Store, id uuid.UUID, approve bool, ...) (types.ApprovalRequest, error)
- func ExpireStale(ctx context.Context, st Store, olderThan time.Duration) (int, error)
- func RequestApproval(ctx context.Context, st Store, req types.ApprovalRequest) (types.ApprovalRequest, error)
- type Store
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyDecided = errors.New("approval: already decided")
ErrAlreadyDecided is re-exported here for callers that import only this package. The underlying store returns the same sentinel.
Functions ¶
func Decide ¶
func Decide(ctx context.Context, st Store, id uuid.UUID, approve bool, decidedByType types.ActorType, decidedBy, reason string) (types.ApprovalRequest, error)
Decide approves or denies an existing approval request. decidedBy is the principal that decided and decidedByType is its actor type (human for an OIDC session or a LocalMode operator; system for a bare admin-token caller). The audit event records that exact type so an admin-token decision is not mislabelled as a human approval (invariant 4/6 attribution honesty).
func ExpireStale ¶
ExpireStale transitions all PENDING approvals that were requested before the cutoff (time.Now().UTC().Add(-olderThan)) to EXPIRED and emits one audit event per expiration. Returns the number of approvals expired.
func RequestApproval ¶
func RequestApproval(ctx context.Context, st Store, req types.ApprovalRequest) (types.ApprovalRequest, error)
RequestApproval creates a new PENDING approval, or returns the existing PENDING approval when one already exists for the same run+kind+scope hash (deduplication guard).
Types ¶
type Store ¶
type Store interface {
// CreateApproval persists a new PENDING approval and returns it.
CreateApproval(ctx context.Context, a types.ApprovalRequest) (types.ApprovalRequest, error)
// GetApproval fetches an approval by id.
GetApproval(ctx context.Context, id uuid.UUID) (types.ApprovalRequest, error)
// ListApprovals returns approvals filtered by state (empty = all).
ListApprovals(ctx context.Context, stateFilter types.ApprovalState) ([]types.ApprovalRequest, error)
// DecideApproval transitions state from PENDING; returns ErrAlreadyDecided
// if the approval is not PENDING.
DecideApproval(ctx context.Context, id uuid.UUID, state types.ApprovalState, decidedBy, reason string) (types.ApprovalRequest, error)
// Record appends an audit event (approval.decide, approval.expire).
Record(ctx context.Context, ev types.AuditEvent) error
}
Store is the narrow persistence interface the approval FSM needs. The real implementation is internal/store; tests supply a fake.