Skip to main content

Starlark Policies

Use engine: starlark for policies that need loops, helper functions, recursion, or custom aggregation. A rule is a function body that returns a boolean. It uses the same attachments, input collectors, denial rules, and hook executor as CEL policies.

Define a rule​

Save this manifest as policy.d/protect-production.yaml:

apiVersion: atmos/v1
kind: Policy
metadata:
name: protect-production
description: Prevent deletion of production Helm releases.
spec:
policies:
prevent-delete:
on: operation
engine: starlark
match:
labels: {stage: prod}
action: deny
reason: Production releases must not be deleted
rule: |
return ctx.input["event"] == "before.helm.delete"

True selects the declared action; False makes this rule not applicable. A missing return, None, a string, or a number is an error—not an implicit boolean conversion. CEL remains the default when engine is omitted. Both languages can coexist in one document or directory.

Input bindings​

BindingContents
ctx.inputNative data for the rule's on kind; all supported input kinds are available
ctx.paramsRule's with object; empty by default
ctx.ciProvider-native snapshot, or None outside detected CI
ctx.requestkind, revisions, and optional instance subject

Nested objects use dictionary indexing, not attribute access:

return ctx.request["subject"]["metadata"]["labels"].get("owner") == "platform"

Mappings support get, keys, values, and items. Lists and mappings supplied by Atmos are read-only. Local lists and dictionaries can be built and changed inside the rule. Integer inputs retain their precision. json.encode and json.decode are available.

For optional fields use object.get("field") or "field" in object; JSON null is None. Missing required fields must block or select a denial, not silently become an empty result. Declare required CI evidence with required_ci; it is checked before the predicate, even if the predicate would return False.

Aggregate plan changes​

total = 0
for resource in ctx.input.get("resource_changes", []):
if resource["mode"] == "managed" and "delete" in resource["change"]["actions"]:
total += 1
return total > ctx.params["maximum"]

The weighted change budget library entry includes a complete manifest and tested input snapshots. Its scope is a single plan, not an aggregation of separate CI jobs.

Select lifecycle hooks​

Use on: operation and action: hooks with the existing hook requirements. The predicate only selects obligations:

return ctx.input["event"] == "after.terraform.plan" and ctx.input["status"] == "success"

The lifecycle executor runs the selected hooks. Starlark predicates cannot call hooks themselves, and successful hooks cannot override a separate denial.

Execution limits​

Rules are limited to 64 KiB of source and 100,000 interpreter steps per evaluation. Cancellation, limit exhaustion, compilation errors, missing values, and non-boolean results block enforcement. These are execution limits, not an operating-system memory sandbox; native builtins can allocate memory and do work outside interpreter-step accounting. Protect policy definitions and review changes.

There is no load, print, shell, filesystem, network, environment, or automation-host access. exec, fs, steps, and the automation ci module are unavailable. Policy CI data is available only through ctx.ci; reading it never performs API requests. Error messages omit source and input values. Policies cannot evaluate deferred YAML functions or fetch additional component configuration.