policydecision

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package policydecision defines the protocol-neutral policy decision vocabulary, record model, observer contracts, and bounded evidence normalization shared by core extension runners, diagnostics, and tests.

Safety rules

Records and contexts carry only safe scope values and bounded strings. Raw prompts, raw backend payloads, transport headers, credentials, resume tokens, and unvetted claims must never be placed in a Record or Context. ClientMessage and ClientCategory are the only fields intended for frontend use.

Dependency boundaries

This package may depend only on other public SDK packages under pkg/lipsdk and the standard library. It must not import internal packages, frontend or backend plugin packages, provider SDKs, or transport wire/server packages.

Index

Constants

View Source
const (
	CategoryAllowed   = "policy_allowed"
	CategorySkipped   = "policy_skipped"
	CategoryDenied    = "policy_denied"
	CategoryFailure   = "policy_failure"
	CategoryObserved  = "policy_observed"
	CategoryMalformed = "policy_malformed"
)

Client category values carried on Record.ClientCategory. They are the evidence taxonomy shared by core extension runners and projected records (requirements 1.7, 4.6, 9.1). Only ClientCategory and ClientMessage are intended for frontend use; the values mirror the stable policy error kinds so a projected record can be classified the same way as an explicit policy error. These constants are the canonical owner of the category strings; extensions aliases and policy error helpers reference them where boundary rules permit. Wire/JSON values are unchanged.

View Source
const (
	MaxProviderIDBytes      = 128
	MaxIdentifierBytes      = 128
	MaxReasonCodeBytes      = 96
	MaxClientCategoryBytes  = 96
	MaxClientMessageBytes   = lipapi.MaxClientMessageBytes
	MaxAnnotationEntries    = 40
	MaxAnnotationKeyBytes   = 64
	MaxAnnotationValueBytes = 256
)

Normalization bounds for evidence fields (design §Evidence Normalization Contract). Exported so operators, diagnostics, and tests can rely on the same bounds the normalizer applies.

Variables

This section is empty.

Functions

func IsLegalPair

func IsLegalPair(stage string, outcome Outcome, effect Effect) bool

IsLegalPair reports whether (stage, outcome, effect) is one of the allowed combinations in the legality table. Unknown stages, OutcomeUnknown, unknown effects, or pairs not listed return false.

func IsLegalStageID

func IsLegalStageID(stage string) bool

IsLegalStageID reports whether stage is one of the legal pipeline stages.

func IsNoopObserver

func IsNoopObserver(obs Observer) bool

IsNoopObserver reports whether obs is a disabled default observer: a nil observer, NoopObserver, or an empty ChainObserver. Composition roots use it to skip evidence attachment and logging for deployments without policy decision observation, keeping the no-op path cheap (requirements 7.6, 10.5). A ChainObserver wrapping only NoopObserver children is not treated as no-op here; such wiring is not produced by the default composition root.

func LegalDecisionStages

func LegalDecisionStages() []string

LegalDecisionStages returns the unique legal stage IDs that have at least one allowed decision, in canonical pipeline order. It is the set of stages for which policy decision records may be emitted or accepted.

func LegalStageIDs

func LegalStageIDs() []string

LegalStageIDs returns the ordered legal pipeline stage IDs (delegates to pkg/lipsdk/feature so callers do not need a second stage taxonomy).

func ValidateRecord

func ValidateRecord(record Record) error

ValidateRecord validates a policy decision record against the legality descriptors (requirements 1.5, 3.6, 4.4, 6.6). It rejects unknown stages, OutcomeUnknown, unknown effects, and illegal outcome/effect pairs as malformed policy decisions. It does not merge or order records: deterministic application order remains with the stage runners.

Stage identifiers may carry surrounding whitespace that normalization would trim; callers that want whitespace-tolerant validation must trim the stage before calling ValidateRecord.

Types

type AccountingProjection

type AccountingProjection struct {
	ReasonCode       AccountingReasonCode
	RuleID           string
	Authority        string
	ReservationID    string
	SettlementStatus string
}

AccountingProjection carries safe accounting metadata that may be projected into a policydecision.Record without changing the base policy outcome/effect.

type AccountingReasonCode

type AccountingReasonCode string

AccountingReasonCode is the bounded reason taxonomy used when projecting accounting decisions into policy-compatible evidence.

const (
	AccountingReasonAllowed           AccountingReasonCode = "allowed"
	AccountingReasonAdvisory          AccountingReasonCode = "advisory"
	AccountingReasonClamped           AccountingReasonCode = "clamped"
	AccountingReasonReserved          AccountingReasonCode = "reserved"
	AccountingReasonReconciled        AccountingReasonCode = "reconciled"
	AccountingReasonQuotaExceeded     AccountingReasonCode = "quota_exceeded"
	AccountingReasonRateLimited       AccountingReasonCode = "rate_limited"
	AccountingReasonBudgetExceeded    AccountingReasonCode = "budget_exceeded"
	AccountingReasonReservationFailed AccountingReasonCode = "reservation_failed"
	AccountingReasonUnavailable       AccountingReasonCode = "unavailable"
	AccountingReasonError             AccountingReasonCode = "error"
)

func (AccountingReasonCode) IsKnown

func (r AccountingReasonCode) IsKnown() bool

IsKnown reports whether r is one of the documented accounting reason codes.

type AllowedDecision

type AllowedDecision struct {
	Stage   string
	Outcome Outcome
	Effects []Effect
}

AllowedDecision describes one legal (stage, outcome, effect) combination that core may emit or accept for a stage (requirements 1.5, 3.6, 4.4, 6.6). The table is intentionally stricter than feature.StageMutationRole: the feature role says what the stage family may do, while this table says which shared policy-decision records core may emit or accept for that stage.

func AllowedDecisionsForStage

func AllowedDecisionsForStage(stage string) []AllowedDecision

AllowedDecisionsForStage returns the legal decision descriptors for stage in canonical order, or nil if the stage is unknown. The returned slice and its effect sub-slices are defensive copies; callers may mutate them without affecting the package table (requirements 1.5, 3.6).

type ChainObserver

type ChainObserver struct {
	// contains filtered or unexported fields
}

ChainObserver fans a record out to each non-nil child observer in registration order. Each child receives its own cloned copy of the record; child errors are ignored (fail-open) so a misbehaving observer cannot change request execution. A nil or empty chain behaves as NoopObserver.

func NewChainObserver

func NewChainObserver(observers ...Observer) ChainObserver

NewChainObserver returns a ChainObserver that fans records out to the supplied observers in order. nil observers are dropped.

func (ChainObserver) Observers

func (c ChainObserver) Observers() []Observer

Observers returns a defensive copy of the child observers in chain order.

func (ChainObserver) OnPolicyDecision

func (c ChainObserver) OnPolicyDecision(ctx context.Context, record Record) error

OnPolicyDecision implements Observer by delivering a fresh clone of record to each child. Errors are ignored so request execution is unaffected (requirement 7.6).

type Context

type Context struct {
	TraceID            string                   `json:"trace_id"`
	ALegID             string                   `json:"a_leg_id"`
	BLegID             string                   `json:"b_leg_id"`
	AttemptSeq         int                      `json:"attempt_seq"`
	Stage              string                   `json:"stage"`
	ProviderID         string                   `json:"provider_id"`
	Scope              scope.PrincipalScopeView `json:"scope"`
	Principal          execview.PrincipalView   `json:"principal"`
	Session            session.SessionView      `json:"session"`
	Workspace          workspace.WorkspaceView  `json:"workspace"`
	Annotations        map[string]string        `json:"annotations,omitempty"`
	OutputCommitted    bool                     `json:"output_committed"`
	EvaluationTimeout  time.Duration            `json:"evaluation_timeout,omitempty"`
	EvaluationDeadline time.Time                `json:"evaluation_deadline,omitzero"`
}

Context is the safe, request-scoped attribution and lifecycle metadata for one decision evaluation or emitted record (requirements 2.1-2.6, 7.5). It is safe-by-construction: raw credentials, headers, resume tokens, and unvetted claims are never fields here. Authority comes from the accepted request's authoritative scope view; legacy principal projection is carried separately so policy code can distinguish authoritative scope from compatibility fields.

func (Context) Clone

func (c Context) Clone() Context

Clone returns a deep copy of the context so callers and observers cannot mutate shared context state through slices, maps, or the embedded scope view (requirements 1.1, 2.1, 7.7). Nil slices and maps are preserved as nil.

type Effect

type Effect string

Effect describes what a decision does to request or response content beyond the outcome itself (requirement 1.2). EffectNone is the zero value and is always legal when paired with a known outcome that does not require mutation.

const (
	EffectNone     Effect = "none"
	EffectAnnotate Effect = "annotate"
	EffectMutate   Effect = "mutate"
	EffectReplace  Effect = "replace"
	EffectReplay   Effect = "replay"
	EffectSwallow  Effect = "swallow"
)

func (Effect) IsKnown

func (e Effect) IsKnown() bool

IsKnown reports whether e is one of the documented effects.

type EvidenceVisibility

type EvidenceVisibility string

EvidenceVisibility controls whether privileged diagnostic detail may leave the core extension runner (requirement 7.4). EvidenceDefault is the zero value and the only value emitted unless explicit diagnostics exposure posture is enabled.

const (
	EvidenceDefault    EvidenceVisibility = "default"
	EvidencePrivileged EvidenceVisibility = "privileged"
)

func (EvidenceVisibility) IsKnown

func (v EvidenceVisibility) IsKnown() bool

IsKnown reports whether v is one of the documented visibility values.

type FailureBehavior

type FailureBehavior string

FailureBehavior records how a stage runner should treat provider failures for the decision (requirement 6.1, 6.2). FailureBehaviorUnspecified is the zero value.

const (
	FailureBehaviorUnspecified FailureBehavior = ""
	FailureBehaviorFailOpen    FailureBehavior = "fail_open"
	FailureBehaviorFailClosed  FailureBehavior = "fail_closed"
)

func (FailureBehavior) IsKnown

func (b FailureBehavior) IsKnown() bool

IsKnown reports whether b is one of the documented non-unspecified behaviors.

type NoopObserver

type NoopObserver struct{}

NoopObserver is the default disabled observer. It is cheap and safe to share.

func (NoopObserver) OnPolicyDecision

func (NoopObserver) OnPolicyDecision(context.Context, Record) error

OnPolicyDecision implements Observer by returning nil without touching the record.

type Observer

type Observer interface {
	OnPolicyDecision(ctx context.Context, record Record) error
}

Observer receives normalized policy decision records from the core evidence emitter (requirements 7.6, 7.7). Implementations must not change request execution; the evidence emitter isolates observer failures from runtime outcomes. Each observer in a chain receives its own cloned record so observer mutation cannot affect another observer or runtime state.

type Outcome

type Outcome string

Outcome is the protocol-neutral result of one policy decision provider at a lifecycle position. OutcomeUnknown is the zero value and is never legal in an emitted record (requirement 1.1, 1.5).

const (
	OutcomeUnknown Outcome = "unknown"
	OutcomeAllow   Outcome = "allow"
	OutcomeDeny    Outcome = "deny"
	OutcomeSkip    Outcome = "skip"
	OutcomeError   Outcome = "error"
)

func (Outcome) IsKnown

func (o Outcome) IsKnown() bool

IsKnown reports whether o is one of the documented non-unknown outcomes.

type ProviderRef

type ProviderRef struct {
	ID    string `json:"id"`
	Stage string `json:"stage"`
}

ProviderRef identifies the decision provider that produced a record and the stage it ran at. IDs are stable plugin identifiers; Stage is a legal feature stage ID.

type Record

type Record struct {
	TraceID            string                   `json:"trace_id"`
	ALegID             string                   `json:"a_leg_id"`
	BLegID             string                   `json:"b_leg_id"`
	AttemptSeq         int                      `json:"attempt_seq"`
	Stage              string                   `json:"stage"`
	Provider           ProviderRef              `json:"provider"`
	Outcome            Outcome                  `json:"outcome"`
	Effect             Effect                   `json:"effect"`
	ReasonCode         string                   `json:"reason_code"`
	ClientCategory     string                   `json:"client_category"`
	ClientMessage      string                   `json:"client_message"`
	FailureBehavior    FailureBehavior          `json:"failure_behavior"`
	Visibility         EvidenceVisibility       `json:"visibility"`
	Scope              scope.PrincipalScopeView `json:"scope"`
	Annotations        map[string]string        `json:"annotations,omitempty"`
	OutputCommitted    bool                     `json:"output_committed"`
	BackendAttempted   bool                     `json:"backend_attempted"`
	EvaluationTimeout  time.Duration            `json:"evaluation_timeout,omitempty"`
	EvaluationDeadline time.Time                `json:"evaluation_deadline,omitzero"`
}

Record is the in-memory decision evidence DTO for one provider decision or one projected extension outcome (requirements 1.1, 1.6, 7.1). Mutable maps and the embedded scope view are deep-cloned at observer boundaries; callers and observers must not mutate a shared record in place.

func NormalizeRecord

func NormalizeRecord(record Record) Record

NormalizeRecord returns a bounded, safe copy of record suitable for observer delivery and structured logging (requirements 7.3, 7.7). It clones maps and the embedded scope view, trims and bounds strings, normalizes reason codes and client categories to safe-token form, removes control characters from client messages, drops oversized or invalid annotation keys, truncates annotation values, and marks truncation with a bounded annotation. It does not validate legality; validation is performed by core before normalization is applied.

func ProjectAccountingRecord

func ProjectAccountingRecord(record Record, projection AccountingProjection) (Record, bool)

ProjectAccountingRecord attaches bounded accounting annotations to record while preserving the base decision fields. It returns ok=false when a caller supplies an unknown accounting reason code or when the resulting record is not a legal policydecision record.

func (Record) Clone

func (r Record) Clone() Record

Clone returns a deep copy of the record so callers and observers cannot mutate shared record state through maps or the embedded scope view (requirements 1.1, 7.7). Nil maps are preserved as nil.

Jump to

Keyboard shortcuts

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