Input, data & document model
Feeding facts to policy.
OPA evaluates over two documents. input is the thing being judged, provided per query (a request, a resource, a Terraform plan). data is external context loaded into OPA — allowlists, role mappings, exception lists — that policies reference as data.<path>. Separating the two matters: the policy logic stays stable while the facts (which registries are allowed, who is exempt) live in data you can update without touching rules.
// data.json — facts, loaded into OPA{ "allowed_registries": ["registry.internal", "gcr.io/acme"] }---package maindeny contains msg if {image := input.spec.containers[_].imagereg := split(image, "/")[0]not reg == data.allowed_registries[_] # reference external datamsg := sprintf("registry %v not allowed", [reg])}
The unified document model
OPA presents everything — your loaded data, your rules’ outputs, and system info — under one virtual document rooted at data, while the query input is separate. Your policies are themselves under data (data.main.deny). This uniform tree is why opa eval queries look like data.example.allow, and why loading external facts is just “put JSON under a data path.” Keeping mutable facts in data and logic in rules is the clean separation to aim for.