Skip to main content

OPA, JSON Schema, and File Checks

OPA and JSON Schema are embedded policy engines. They work with the same captured inputs and actions as CEL and Starlark, including native Terraform plans. No external OPA executable is needed. Existing settings.validation behavior is unchanged.

For a guided migration from custom plan commands, follow Replace Terraform Plan Wrappers with Policies and Hooks.

EngineWhat selects the configured action?
cel, starlarkA true predicate
opatrue, or a nonempty array/set of violations
jsonschemaThe input fails schema validation

A false predicate, empty violation collection, or valid document produces not_applicable: the rule does not select its action. Invalid policies, unavailable query results, and evaluation failures are errors, rather than successful checks.

OPA​

policy.d/plan.yaml
apiVersion: atmos/v1
kind: Policy
metadata:
name: terraform
spec:
policies:
check-changes:
on: terraform.plan
engine: opa
opa:
query: data.terraform.analysis.warn
modules: [rego/terraform.rego]
rego_version: 0
action: deny
reason: Review the planned resource deletions.
policy.d/rego/terraform.rego
package terraform.analysis

warn[msg] {
resource := input.resource_changes[_]
resource.change.actions[_] == "delete"
msg := sprintf("Resource %s will be deleted", [resource.address])
}

opa.query must evaluate to one defined boolean or collection. Collection entries may be strings or objects. Objects retain all fields in diagnostics[].details; message (or msg) and path also populate the corresponding diagnostic fields. Scalars, invalid collection entries, undefined queries, and queries returning multiple results or variable bindings fail evaluation. Use an empty set/array or default deny = false for a successful no-match result.

Module paths are relative to the policy document directory. Multiple modules support shared Rego packages. The default rego_version: 0 accepts legacy Rego syntax; set rego_version: 1 for v1 syntax. Rego receives the original document as input; data.atmos_context exposes params, ci, and request, matching the CEL context without wrapping the input document.

Modules are loaded and compiled when the bundle is loaded. Evaluation uses captured module contents without reading files. Network, clock, randomness, runtime inspection, tracing, and printing builtins are unavailable. Evaluation has a 20-second deadline, also respecting an earlier caller deadline. Combined module source is limited to 1 MiB per rule; queries are limited to 64 KiB.

JSON Schema​

policy.d/owners.yaml
apiVersion: atmos/v1
kind: Policy
metadata:
name: ownership
spec:
policies:
owner:
on: file
files:
include: ["config/**/*.json", "stacks/**/*.yaml"]
exclude: ["stacks/vendor/**"]
engine: jsonschema
schema:
path: schemas/owner.schema.json
action: deny
reason: If metadata.owner is present, it must be platform.
policy.d/schemas/owner.schema.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"metadata": {
"type": "object",
"properties": {
"owner": { "const": "platform" }
}
}
}
}

This schema accepts absent metadata or owner properties. If metadata.owner is present, it must equal platform; another string or null fails. Add required at the relevant object level to require a property. JSON Schema conditionals can express more complex dependencies; no separate when expression is needed.

schema.path is relative to the policy document directory. Keep schemas in a subdirectory: top-level .json files in policy.d/ are interpreted as Policy manifests. Local relative $ref files and internal references are supported. Schemas and references must remain within the policy directory, including symlink targets. Remote references are not fetched. The dialect defaults to Draft 2020-12; a supported $schema declaration selects its dialect. Combined schema resources are limited to 1 MiB per rule. Schema evaluation is synchronous; cancellation is checked before and after compilation/validation, not during the library's validation call.

Schema failures retain messages, input JSON Pointer paths, and schema keyword paths in findings[].diagnostics. Invalid schemas are configuration errors rather than input violations.

File checks​

atmos policy check
atmos policy check --root /path/to/project
atmos policy check --syntax-only

check first validates the complete bundle, then evaluates on: file rules against documents selected by files.include and optional files.exclude. Patterns use forward slashes and support **; they are relative to --root (the current working directory by default). Absolute patterns and parent traversal are rejected. Only .json, .yaml, and .yml files are selected. Directory symlinks are not recursively followed; file reads cannot escape the root. Overlapping patterns do not run the same rule twice on a file. A rule matching no documents is an error, making glob typos visible.

Files are checked as written, without stack merging, Atmos template expansion, or YAML function execution. Custom YAML tags, duplicate YAML keys, non-string mapping keys, multiple documents, malformed data, and files larger than 16 MiB fail the check. Objects, arrays, and scalar JSON values are supported. All engines receive the document itself as input; the root-relative filename is available separately as request.file (or ctx.request.file in Starlark).

JSON output includes valid, bundle counts, and a sorted files array containing each filename, decision, and findings. Validation continues across selected files after an individual file fails. Denials, unresolved approval requirements, and evaluation errors return a nonzero exit status. File checks never execute hooks or resolve approvals. Other input kinds are compiled but not evaluated by check; their normal lifecycle gates remain responsible for collecting inputs.

--syntax-only checks definitions and compiles their resources without expanding file globs or reading input documents. Bundles without file rules retain the original definition-only behavior.

Diagnostic messages and Rego details can contain input values. File-check JSON deliberately reports them. Operational gates continue rendering static rule reasons; detailed findings remain available to Go callers through decisions and policy.Violations(err). This feature does not add warning actions or automatic Markdown reports.