The decision API & bundles

OPA as a service; distributing policy.

Advanced12 min · lesson 10 of 12

Beyond the CLI, OPA runs as a server exposing a REST decision API: your service POSTs input to /v1/data/<policy path> and gets the decision back as JSON. This is how applications offload authorization — the app asks “can this user do this?” and enforces the answer, with all the logic in OPA. Running OPA as a sidecar next to the service keeps latency low and the policy engine local.

terminal
$ opa run --server policy.rego
$ curl -s localhost:8181/v1/data/authz/allow \
-d '{"input": {"user": {"roles": ["editor"]}, "action": "write", "resource": "docs/x"}}'
{ "result": true }

Bundles: distributing policy

Bundles are how you deliver and update policy to running OPAs at scale: a bundle is a tarball of policies and data that OPA periodically pulls from a server (an HTTP endpoint, an S3 bucket, an OCI registry). Change the bundle centrally and every OPA picks it up — so a fleet of sidecars stays in sync without redeploying the apps. Bundles support signing, so an OPA can verify a bundle before loading it.

opa config
services:
acme: { url: https://policies.acme.internal }
bundles:
authz:
resource: bundles/authz.tar.gz
polling: { min_delay_seconds: 30, max_delay_seconds: 60 }
signing: { keyid: acme-policy-key } # verify the bundle signature before loading
A poisoned bundle changes every decision — sign it
If OPA pulls policy from a bundle server, whoever controls that server controls your authorization and admission decisions across the fleet. Serve bundles over TLS from infrastructure you trust, enable bundle signing so OPA verifies each bundle before loading, and restrict who can publish. An unsigned bundle from an untrusted endpoint is remote control of your policy engine.