Documentation
¶
Overview ¶
Package policy provides experimental, side-effect-free policy evaluation.
Callers supply already resolved, trusted rules and collected input snapshots. The explicit plan-directory loader and planfile subpackage support a narrow Terraform command gate. This package does not discover trusted tiers, resolve trust, collect CI facts, compute affected inventories, verify approvals or execute hooks. It must not be treated as an enforcement boundary on its own.
Evaluator adapters receive materialized JSON, never lazy configuration values or an automation host. NewEngine registers CEL and restricted Starlark adapters. Existing settings.validation behavior is unchanged.
Index ¶
- Constants
- func BeginExplanation(emit func(Explanation), rules []Rule) func()
- func DirectoryFromEnvironment() (string, error)
- func EnforceDecision(decision Decision) error
- func Explain(kind InputKind, rule, status string)
- func InputSchema(kind InputKind) (*jsonschema.Schema, error)
- func IsAdmissionError(err error) bool
- func IsDestroyInvocation(path, args []string) bool
- func ValidateTerraformPlan(data json.RawMessage) error
- type AffectedInput
- type ApprovalRequirement
- type Availability
- type Bundle
- type CEL
- type CICollector
- type CIRequirement
- type CISnapshot
- type ChangeAction
- type CommandInput
- type ComponentCommandInput
- type Dataset
- type Decision
- type DecisionStatus
- type Effects
- type Engine
- type Evaluation
- type Evaluator
- type Explanation
- type FieldChange
- type FileChange
- type Finding
- type FunctionInput
- type GitHubApprovalSpec
- type GitHubReview
- type HookRequirement
- type InputKind
- type InventorySnapshot
- type ObjectChange
- type ObjectIdentity
- type OperationInput
- type Provenance
- type Request
- type Revisions
- type Rule
- type Selector
- type SourceInput
- type Starlark
- type Subject
- type SubjectMetadata
- type TestCase
- type TestResult
- type Violation
Constants ¶
const CELEngine = "cel"
CELEngine is the explicit registry key for the CEL predicate adapter.
const StarlarkEngine = "starlark"
StarlarkEngine is the registry key for restricted Starlark policy predicates.
Variables ¶
This section is empty.
Functions ¶
func BeginExplanation ¶
func BeginExplanation(emit func(Explanation), rules []Rule) func()
BeginExplanation installs an invocation-scoped, serialized diagnostic sink. The returned cleanup reports configured input kinds never reached by this invocation.
func DirectoryFromEnvironment ¶
DirectoryFromEnvironment resolves an explicit override or the default policy.d directory. The default is the policy.d next to the configuration that atmos.yaml discovery selects, using the same precedence: --config/--config-path (then ATMOS_CONFIG and ATMOS_CONFIG_PATH), ATMOS_CLI_CONFIG_PATH, the working directory, then the Git root unless ATMOS_GIT_ROOT_BASEPATH=false, then the nearest ancestor holding atmos.yaml, which config loading also reads. The first of those directories holding a policy.d wins; the working directory is always considered. Discovery does not establish trust. A missing default is allowed; an explicit missing directory is returned so loading fails rather than falling back.
func EnforceDecision ¶
EnforceDecision converts a pure decision into a local gate. This executor has no approval verifier or hook executor: outstanding obligations must not become success. Denial explanations include only static rule identifiers and author-supplied reasons, never evaluated values.
func InputSchema ¶
func InputSchema(kind InputKind) (*jsonschema.Schema, error)
InputSchema returns the schema for an implemented input, never a proposed capability.
func IsAdmissionError ¶
IsAdmissionError reports whether err is a policy admission failure: a denial, an unmet requirement, or a policy that could not be loaded or evaluated. Admission fails closed, so callers must surface these errors unchanged rather than relabeling them as hook or execution failures.
func IsDestroyInvocation ¶
IsDestroyInvocation reports whether a command invocation destroys infrastructure. It derives the boolean exposed as `destroy` in command and component.command policy inputs, so rule authors never parse argument values themselves.
The path is the canonical command path (for example ["terraform", "apply"]), or the component type followed by the subcommand. The args are the raw command-line arguments of the invocation (positionals and flags in any order). They are only inspected for the Terraform family, and only for the `destroy` flag; no other argument value is derived or exposed.
The result is true when:
- the path is `terraform destroy` or `helmfile destroy`, or
- the path belongs to the terraform family (OpenTofu shares it) and any argument is the destroy flag: -destroy or --destroy, with or without a boolean value (-destroy=true, --destroy=1, -destroy=T, ...), including after the `--` separator.
A false boolean value such as -destroy=false, -destroy=0, or -destroy=F does not make a command a destroy. Values are parsed with strconv.ParseBool, like Go's flag package. The check fails closed: an unparseable value counts as true, and any true occurrence wins over a later false one, because the final argument order seen by the tool can differ from the order typed.
It cannot see destruction that was decided earlier, such as applying a saved plan created with `plan -destroy`. Terraform plan policies cover that case.
func ValidateTerraformPlan ¶
func ValidateTerraformPlan(data json.RawMessage) error
ValidateTerraformPlan validates the native show -json plan envelope without replacing it with a summary or discarding unknown/sensitive/forward-compatible fields. It does not attest that supplied JSON came from a saved binary; the collector does that.
Types ¶
type AffectedInput ¶
type AffectedInput struct {
Complete bool `json:"complete"`
Reasons []string `json:"reasons"`
Files []FileChange `json:"files"`
Components []ObjectChange `json:"components"`
Instances []ObjectChange `json:"instances"`
Stacks []ObjectChange `json:"stacks"`
Changes []FieldChange `json:"changes"`
}
AffectedInput is a materialized analysis payload for Request.Input. The command collector compares unfiltered static inventories under active discovery settings; it does not reuse the filtered describe-affected presentation as its inventory. Empty, complete lists must be encoded as [] rather than unavailable/null lists. Evaluating this payload never computes inventories or schedules component work.
func BuildAffectedInput ¶
func BuildAffectedInput(before, after InventorySnapshot, files []FileChange, sourceAffected []ObjectIdentity) (AffectedInput, error)
BuildAffectedInput compares independently collected snapshots. SourceAffected identifies instances whose component files changed without a stack-value change.
type ApprovalRequirement ¶
type ApprovalRequirement struct {
ID string `json:"id"`
Provider string `json:"provider"`
Spec json.RawMessage `json:"spec"`
}
ApprovalRequirement remains outstanding until a trusted CI resolver verifies it. Spec is provider-owned data, not a universal team/review vocabulary.
type Availability ¶
type Availability string
Availability is explicit: an absent/zero state is never complete evidence.
const ( Available Availability = "available" Incomplete Availability = "incomplete" )
type Bundle ¶
Bundle contains governing rules and hook definitions, never workspace hooks. Hook payloads use the existing hook schema and are decoded by that executor.
func LoadDirectory ¶
LoadDirectory loads explicit, protected local policy documents. It performs no imports, template expansion, hook execution or automatic trust discovery.
type CEL ¶
type CEL struct{}
CEL evaluates boolean predicates with no custom functions or I/O capabilities. The zero value uses a bounded default runtime cost. Compile size is bounded too.
type CICollector ¶
type CICollector interface {
Collect(ctx context.Context) (*CISnapshot, error)
}
CICollector is the provider adapter seam outside pure evaluation. Implementations must document schemas, token permissions, and capabilities. Native CI integration uses pkg/ci.PolicySnapshotProvider; the evaluator never invokes collectors.
type CIRequirement ¶
type CIRequirement struct {
Provider string `json:"provider" yaml:"provider"`
Schema string `json:"schema" yaml:"schema"`
Dataset string `json:"dataset" yaml:"dataset"`
Fact string `json:"fact,omitempty" yaml:"fact,omitempty"`
}
CIRequirement names an exact provider/schema and dataset required before evaluation. Dataset is metadata, event, or facts; Fact names the provider-specific fact for facts.
type CISnapshot ¶
type CISnapshot struct {
Provider string `json:"provider"`
Schema string `json:"schema"`
Metadata Dataset `json:"metadata"`
Event Dataset `json:"event"`
Facts map[string]Dataset `json:"facts"`
}
CISnapshot is a small envelope with provider-owned schemas and JSON payloads. Triggering Event is separate from subsequently fetched Facts (reviews, checks, etc.). This does not replace pkg/ci.Context or the boolean workflow/hook condition ci.
type ChangeAction ¶
type ChangeAction string
ChangeAction distinguishes additions, modifications, and removals. Collectors represent ambiguous moves conservatively as removal/addition, with Ambiguous set.
const ( Added ChangeAction = "add" Modified ChangeAction = "modify" Removed ChangeAction = "remove" )
type CommandInput ¶
type CommandInput struct {
Command string `json:"command"`
Path []string `json:"path"`
Destroy bool `json:"destroy"`
}
CommandInput is the canonical command path without argument values. Destroy is the only value derived from arguments: a normalized boolean, see IsDestroyInvocation.
type ComponentCommandInput ¶
type ComponentCommandInput struct {
Command string `json:"command"`
ComponentType string `json:"component_type"`
Component string `json:"component"`
Stack string `json:"stack"`
DryRun bool `json:"dry_run"`
Destroy bool `json:"destroy"`
Capabilities []string `json:"capabilities"`
}
ComponentCommandInput describes shared component admission.
type Dataset ¶
type Dataset struct {
Data json.RawMessage `json:"data,omitempty"`
Status Availability `json:"status"`
Complete bool `json:"complete"`
Provenance Provenance `json:"provenance"`
Reason string `json:"reason,omitempty"`
}
Dataset keeps native data alongside its availability, completeness, and origin. Adapters must not mark truncated pagination complete or fabricate event payloads.
type Decision ¶
type Decision struct {
Status DecisionStatus `json:"status"`
Findings []Finding `json:"findings"`
}
Decision is a pure evaluation result, not a final CI or operation result. Error dominates deny, then awaiting approval, allow, and not applicable.
func ResolveGitHubApprovals ¶
func ResolveGitHubApprovals(decision Decision, head, author string, reviews []GitHubReview) (Decision, error)
ResolveGitHubApprovals is pure; the caller must supply complete, freshly fetched reviews bound to the current open PR and the revision being executed. The latest decisive review wins. Comments never grant or revoke approval.
type DecisionStatus ¶
type DecisionStatus string
DecisionStatus distinguishes authorization from missing evidence and errors.
const ( Allow DecisionStatus = "allow" Deny DecisionStatus = "deny" AwaitingApproval DecisionStatus = "awaiting_approval" NotApplicable DecisionStatus = "not_applicable" EvaluationError DecisionStatus = "error" )
type Effects ¶
type Effects struct {
Deny bool `json:"deny"`
Reason string `json:"reason,omitempty"`
Approvals []ApprovalRequirement `json:"approvals,omitempty"`
Hooks []HookRequirement `json:"hooks,omitempty"`
}
Effects are obligations selected by a predicate. They cannot perform side effects.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine owns an instance-local language registry; it has no implicit evaluators. Its zero value is usable. Register adapters before evaluation for reproducibility.
func NewEngine ¶
func NewEngine() *Engine
NewEngine returns an isolated registry with the supported pure evaluators.
type Evaluation ¶
type Evaluation struct {
Expression string `json:"expression"`
Parameters json.RawMessage `json:"parameters,omitempty"`
Request *Request `json:"request"`
}
Evaluation contains detached, materialized data for a single predicate. Adapters must be concurrency-safe, honor cancellation, and perform no I/O.
type Evaluator ¶
type Evaluator interface {
Evaluate(ctx context.Context, evaluation Evaluation) (bool, error)
}
Evaluator is the language extension point for pure predicates, including CEL and restricted Starlark, without exposing the script automation engine.
type Explanation ¶
type Explanation struct {
Kind InputKind `json:"kind"`
Rule string `json:"rule,omitempty"`
Status string `json:"status"`
}
Explanation describes evaluation without exposing input values or expression errors.
type FieldChange ¶
type FieldChange struct {
Identity ObjectIdentity `json:"identity"`
Path string `json:"path"`
Action ChangeAction `json:"action"`
BeforePresent bool `json:"before_present"`
AfterPresent bool `json:"after_present"`
Before json.RawMessage `json:"before,omitempty"`
After json.RawMessage `json:"after,omitempty"`
}
FieldChange uses a JSON Pointer path and explicit presence to distinguish a missing field from a field whose value is null. Collectors populate both flags.
type FileChange ¶
type FileChange struct {
Path string `json:"path"`
PreviousPath string `json:"previous_path,omitempty"`
Action ChangeAction `json:"action"`
Ambiguous bool `json:"ambiguous"`
}
FileChange records both paths for detected renames without assuming object identity.
type Finding ¶
type Finding struct {
RuleID string `json:"rule_id"`
Status DecisionStatus `json:"status"`
Effects Effects `json:"effects"`
Error string `json:"error,omitempty"`
}
Finding preserves each rule's outcome and obligations, including on aggregate denial.
type FunctionInput ¶
type FunctionInput = guard.FunctionInput
FunctionInput is the common function collector contract.
type GitHubApprovalSpec ¶
type GitHubApprovalSpec struct {
Reviewers []string `json:"reviewers"`
Minimum int `json:"minimum"`
}
GitHubApprovalSpec uses explicitly maintained logins, not live organization teams.
func ParseGitHubApproval ¶
func ParseGitHubApproval(requirement ApprovalRequirement) (GitHubApprovalSpec, error)
ParseGitHubApproval validates the supported provider-native approval contract.
type GitHubReview ¶
type GitHubReview struct {
ID int64 `json:"id"`
Login string `json:"login"`
State string `json:"state"`
CommitID string `json:"commit_id"`
SubmittedAt time.Time `json:"submitted_at"`
}
GitHubReview retains the provider fields required for review verification.
type HookRequirement ¶
type HookRequirement struct {
Ref string `json:"ref" yaml:"ref"`
Event string `json:"event" yaml:"event"`
Required bool `json:"required" yaml:"required"`
RequireSuccess bool `json:"require_success" yaml:"require_success"`
}
HookRequirement references a trusted hook definition resolved by the caller. Required (must execute) and RequireSuccess (must succeed if executed) are distinct. Neither an allow decision nor a selected hook is evidence of hook completion.
type InputKind ¶
type InputKind string
InputKind identifies a policy input contract, not a component or CI provider.
const ( Validation InputKind = "validation" Plan InputKind = "plan" TerraformPlan InputKind = "terraform.plan" Operation InputKind = "operation" Command InputKind = "command" ComponentCommand InputKind = "component.command" Function InputKind = "function" Source InputKind = "source" GitChange InputKind = "git.change" AtmosAffected InputKind = "atmos.affected" GitHubEvent InputKind = "github.event" GitHubApproval InputKind = "github.approval" )
Well-known input kind names. Collectors and command integration are separate.
type InventorySnapshot ¶
type InventorySnapshot struct {
Stacks map[string]map[string]map[string]json.RawMessage
}
InventorySnapshot is a complete, unfiltered resolved stack inventory. Callers must establish collection completeness before constructing a policy input.
type ObjectChange ¶
type ObjectChange struct {
Identity ObjectIdentity `json:"identity"`
Action ChangeAction `json:"action"`
Before json.RawMessage `json:"before,omitempty"`
After json.RawMessage `json:"after,omitempty"`
Reasons []string `json:"reasons"`
Ambiguous bool `json:"ambiguous"`
}
ObjectChange holds separate before/after objects; selectors must not merge them. Absent before/after fields differ from explicit JSON null values.
type ObjectIdentity ¶
type ObjectIdentity struct {
Name string `json:"name"`
ComponentType string `json:"component_type,omitempty"`
Stack string `json:"stack,omitempty"`
}
ObjectIdentity distinguishes shared components from configured instances. An instance uses Stack + ComponentType + Name; a shared component omits Stack.
type OperationInput ¶
type OperationInput struct {
Event string `json:"event"`
ComponentType string `json:"component_type"`
Command string `json:"command"`
Component string `json:"component"`
Stack string `json:"stack"`
Status string `json:"status"`
ExitCode int `json:"exit_code"`
DryRun bool `json:"dry_run"`
}
OperationInput describes a concrete lifecycle event.
type Provenance ¶
type Provenance struct {
Source string `json:"source"`
CollectedAt time.Time `json:"collected_at"`
Revision string `json:"revision,omitempty"`
}
Provenance describes collection, not proof that the source was trusted.
type Request ¶
type Request struct {
Subject *Subject `json:"subject,omitempty"`
Kind InputKind `json:"kind"`
Input json.RawMessage `json:"input"`
CI *CISnapshot `json:"ci,omitempty"`
Revisions Revisions `json:"revisions"`
}
Request contains one explicitly selected input kind. Input must be a JSON object. No input kind implicitly triggers collection of another kind (including affected).
type Revisions ¶
type Revisions struct {
Governing string `json:"governing"`
Base string `json:"base"`
Head string `json:"head"`
}
Revisions keeps the governing policy revision separate from the comparison base. Values are supplied by a trusted caller; the engine does not verify Git objects.
type Rule ¶
type Rule struct {
AllowedRoots []string `json:"allowed_roots,omitempty"`
Match *Selector `json:"match,omitempty"`
ID string `json:"id"`
Description string `json:"description,omitempty"`
On InputKind `json:"on"`
Engine string `json:"engine"`
Expression string `json:"expression"`
Parameters json.RawMessage `json:"parameters,omitempty"`
RequiredCI []CIRequirement `json:"required_ci,omitempty"`
Effects Effects `json:"effects"`
}
Rule is an already resolved definition, not a policy manifest or trust assertion. A true predicate selects its effects; false means not applicable, never denial.
func LoadPlanDirectory ¶
LoadPlanDirectory loads inline CEL or Starlark plan manifests. An explicit directory is required. No discovery, imports, templates or trust claims are made. Unsupported fields/capabilities are errors, not ignored declarations.
type Selector ¶
type Selector struct {
Tags []string `json:"tags,omitempty" yaml:"tags"`
TagMode tags.TagMode `json:"tag_mode,omitempty" yaml:"-"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels"`
Components []string `json:"components,omitempty" yaml:"components"`
Stacks []string `json:"stacks,omitempty" yaml:"stacks"`
ComponentTypes []string `json:"component_types,omitempty" yaml:"component_types"`
}
Selector attaches a rule to instances. Fields combine with AND; label entries all match. Multiple tags use the same any/all convention as component selection.
type SourceInput ¶
type SourceInput = guard.SourceInput
SourceInput is the destination collector contract. Rules with allowed_roots also receive within_allowed_roots.
type Starlark ¶
type Starlark struct{}
Starlark evaluates boolean function bodies over detached, read-only JSON data. It has no automation host, module loader, filesystem, network, or logging API.
type Subject ¶
type Subject struct {
ObjectIdentity
Metadata SubjectMetadata `json:"metadata"`
// TemplateLeft identifies unresolved metadata under a configured delimiter.
TemplateLeft string `json:"template_left,omitempty"`
}
Subject identifies one instance and its resolved, inherited selector metadata. A nil subject means unavailable, not an instance with no labels.
func NewSubject ¶
func NewSubject(identity ObjectIdentity, metadata any, delimiters ...string) (*Subject, error)
NewSubject copies already resolved metadata without evaluating templates or functions. Malformed and unresolved metadata must not silently detach a policy.
type SubjectMetadata ¶
type SubjectMetadata struct {
Tags []string `json:"tags"`
Labels map[string]string `json:"labels"`
}
SubjectMetadata contains only data used for attachment, never executable configuration.
type TestCase ¶
type TestCase struct {
Name string `json:"name"`
Request Request `json:"request"`
Status DecisionStatus `json:"status"`
Error string `json:"error,omitempty"`
Hooks []HookRequirement `json:"hooks,omitempty"`
Approvals []ApprovalRequirement `json:"approvals,omitempty"`
}
TestCase is the offline fixture format shared with the distributed policy catalog.
type TestResult ¶
type TestResult struct {
Name string `json:"name"`
Passed bool `json:"passed"`
Expected DecisionStatus `json:"expected"`
Actual DecisionStatus `json:"actual"`
}
TestResult reports observable fixture outcomes without retaining sensitive inputs.
type Violation ¶
Violation identifies one rule that denied an execution, so reporters such as CI annotations can name the rule without parsing the rendered explanation. Reason is the author's static Markdown and is empty when the rule has none.
func Violations ¶
Violations returns the rules that denied err, in decision order, or nil when err is not a policy denial. Wrapping err does not hide its violations.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package admission enforces policy before execution, outside pure evaluators.
|
Package admission enforces policy before execution, outside pure evaluators. |
|
Package guard supplies dependency-free pre-I/O hooks to configuration loaders.
|
Package guard supplies dependency-free pre-I/O hooks to configuration loaders. |
|
Package planfile binds policy evaluation to saved Terraform artifacts.
|
Package planfile binds policy evaluation to saved Terraform artifacts. |