PDP Integration with EnforceAuth
A Policy Decision Point (PDP) runs OPA or Enterprise OPA and evaluates Rego. EnforceAuth is the control plane: it builds bundles from Git, publishes them to your bundle destination, and ingests decision logs for audit and coverage.
This guide covers the PDP side — pulling bundles and reporting decisions back.
Architecture
Git (policy source) ──► EnforceAuth deploy ──► S3 / GCS / Azure (bundle)
│
▼ pull bundle
OPA / EOPA (PDP)
│
▼ decision logs + status
EnforceAuth API (API key)
Your PEP (application or gateway) sends input to the PDP; the PDP returns allow/deny. EnforceAuth does not sit in the request path — it governs policy lifecycle and evidence.
1. Pull bundles from EnforceAuth
After a successful deployment, the bundle object lives at the path configured in Bundle Destination (e.g. policies/bundle.tar.gz on S3).
Configure OPA to poll that location using management bundles. Example for S3 (illustrative — substitute your bucket, region, and path):
services:
enforceauth-bundle:
url: https://YOUR_BUCKET.s3.YOUR_REGION.amazonaws.com
credentials:
s3_signing:
environment_credentials: {}
bundles:
authz:
service: enforceauth-bundle
resource: policies/bundle.tar.gz
polling:
min_delay_seconds: 10
max_delay_seconds: 20
For GCS or Azure, use the credential plugin appropriate to your cloud — match the same destination you configured in the console. See Bundle destinations.
Verify activation:
opa inspect /path/to/downloaded/bundle.tar.gz
# or on a running OPA with bundle mode:
curl -s localhost:8181/v1/data/authz/allow | jq .
Drift tip: If the console shows Success but decisions reference an old bundle revision, the PDP is likely still pointed at a stale URL or path.
Bundle polling — how often, and what happens when things break
This is the most common operations question after initial setup. Bundle polling (OPA → object storage) is separate from decision log upload (OPA → EnforceAuth API) and separate from authorization queries (PEP → OPA).
EnforceAuth deploy ──writes──► S3 / GCS / Azure (bundle)
▲
│ poll every min–max seconds
OPA / EOPA
▲
│ every request
App (PEP)
How often does OPA check for a new bundle?
With management bundle polling:
bundles:
authz:
service: enforceauth-bundle
resource: policies/bundle.tar.gz
polling:
min_delay_seconds: 10
max_delay_seconds: 20
| Field | Meaning |
|---|---|
min_delay_seconds | Shortest wait between poll attempts after the previous attempt finished |
max_delay_seconds | Longest wait between poll attempts |
OPA picks a random delay between min and max for each cycle — not a fixed interval. With 10 / 20, expect a new check roughly every 10–20 seconds on average after the prior download attempt completes.
This is not the same as decision log reporting (decision_logs.reporting uses its own min/max, typically 30–60s). Bundle polling controls policy freshness; decision log reporting controls audit latency.
Recommended starting points
| Environment | Bundle min / max | Notes |
|---|---|---|
| Dev | 10 / 20 | See policy changes quickly |
| Staging | 30 / 60 | Balance freshness and storage API load |
| Production | 60 / 120 (or wider) | Stagger across pods — avoid thundering herd on deploy |
| Large fleet (100+ sidecars) | 90 / 180 + jitter | Consider EOPA delta bundles if downloads are heavy |
Widen intervals in production unless you have a hard real-time promotion SLO — EnforceAuth deploy completes in seconds; PDP pickup time is dominated by your poll window.
What happens on startup?
| Scenario | Behavior |
|---|---|
| Polling config, bundle reachable | OPA downloads, activates bundle, then serves /v1/data/... |
| Polling config, bundle unreachable on first boot | OPA retries on the poll schedule; no policy to evaluate until first successful activation — PEP requests fail or return undefined until then |
Local bundle (opa run -b bundle.tar.gz) | No polling; static bundle until restart |
For Kubernetes, use readiness probes on /health and confirm bundle activation (status plugin or logs) before marking the pod ready.
What if the bundle server is unreachable?
After a bundle has been successfully activated at least once, OPA keeps serving the last good bundle in memory. Authorization continues — it does not automatically deny all traffic when S3/GCS/Azure is down.
| State | What the PDP does | Risk |
|---|---|---|
| Bundle server down, prior bundle loaded | Keeps evaluating stale policy | False confidence — you think new deny rules are live but PDP has not picked them up |
| Bundle server down, never activated | No policy loaded | Deny/undefined behavior depending on Rego defaults |
| Bundle server recovers | Next successful poll downloads and activates new revision | Lag = up to max_delay_seconds plus download time |
This is fail operational with stale policy, not fail closed. For security-sensitive promotions, verify bundle revision in decision logs or via the status plugin — do not assume deploy Success in the console means every PDP has activated.
Enable status reporting (section 4) so EnforceAuth receives OPA's native bundle activation success/failure payloads. Failed downloads surface in fleet monitoring when status:write is configured.
What if EnforceAuth API is unreachable?
Decision log and status plugins use a different URL (ENFORCEAUTH_URL) than bundle storage. If the EnforceAuth API is down or blocked:
| Component | Impact |
|---|---|
| Authorization (PEP → PDP) | Unaffected — OPA evaluates locally with the active bundle |
| Decision logs | OPA buffers and retries per plugin config; sustained outage → gaps in console Decisions / coverage |
| Status | Fleet health reports may stop updating |
| Bundle polling | Unaffected — bundles live in your object storage, not the EnforceAuth API |
Design alerts on decision log gaps separately from bundle activation failures.
What if the app cannot reach the PDP?
That is a PEP → PDP problem (sidecar down, wrong OPA_URL, network policy). OPA cannot evaluate if the PEP cannot connect. See Application integration — fail open vs fail closed.
This is independent of bundle polling — the PDP may have a fresh bundle but be unreachable from the app.
Verifying bundle revision at runtime
# OPA exposes bundle state on the status API (when server is running)
curl -s localhost:8181/v1/status | jq '.bundles'
Compare active_revision / etag against the bundle version in your latest EnforceAuth deployment run.
With status:write configured, the same information flows to EnforceAuth for fleet views.
Thundering herd on deploy
When EnforceAuth publishes a new bundle, every PDP polling that path will discover the change on its own schedule. If all sidecars share identical min_delay / max_delay, they may hit object storage simultaneously.
Mitigations:
- Stagger min/max per deployment zone or use a spread of values in Helm values
- Widen production poll windows
- EOPA delta bundles for large artifacts (OPA vs EOPA)
2. Create an API key for telemetry
Decision log ingestion requires an entity-scoped API key with decisions:write. See API keys for creation and rotation.
Use View Config in the console to copy the PDP YAML template.
3. Send decision logs
EnforceAuth expects OPA's decision log plugin pointed at the EnforceAuth API. The console-generated template (from decisions:write scope):
services:
enforceauth:
url: ${ENFORCEAUTH_URL}/v1
headers:
X-API-Key: ${ENFORCEAUTH_API_KEY}
decision_logs:
service: enforceauth
reporting:
min_delay_seconds: 30
max_delay_seconds: 60
Environment variables:
| Variable | Value |
|---|---|
ENFORCEAUTH_URL | https://api.enforceauth.com or https://api.enforceauth.dev |
ENFORCEAUTH_API_KEY | Secret from key creation (shown once) |
The /v1 prefix belongs on the service URL — OPA plugins append /logs and /status paths relative to that base.
After traffic flows, verify:
- Decisions in the console shows entries for the entity
- Policy Coverage moves from Never evaluated to Evaluated
- Bundle revision in logs matches your latest deployment
4. Optional: status reporting
Keys with status:write can add:
status:
service: enforceauth
Status reports help fleet health views. Read scope status:read allows fetching status via API.
5. Enterprise OPA (EOPA)
→ OPA vs EOPA — when to choose EOPA, industry context, masking/delta/partitioning depth.
Styra DAS™ was decommissioned April 30, 2026. EnforceAuth is the enterprise authorization platform for policy lifecycle, promotion, audit, and fleet operations. EOPA was donated to the CNCF Open Policy Agent project (open-policy-agent/eopa) as a PDP subproject. DAS™ may be a trademark of its respective owner. EnforceAuth is not affiliated with or endorsed by Apple Inc.
Enterprise OPA (EOPA) is an extended OPA distribution for data-heavy and enterprise PDP workloads. It runs the same Rego and bundle format EnforceAuth publishes — swap the container image and add EOPA-specific plugins where needed.
When teams choose EOPA
| Capability | One-line benefit |
|---|---|
| Decision log masking | Redact sensitive input fields before logs leave your network |
| Delta bundles | Smaller updates when only part of the bundle changes |
| Partitioning | Large fleets with isolated policy partitions |
| Datasource integrations | SQL, DynamoDB, Kafka, LDAP, S3, Vault — without custom plugins |
| Fleet scale | Delta bundles + partitioning + data-heavy evaluation tuning |
Expand each capability below for why it matters and when to use it.
Decision log masking — redact PII before logs leave the PDP
The problem: Every authorization check sends the full input document to the decision log plugin — user emails, employee IDs, bearer tokens, account numbers, internal URLs. EnforceAuth stores these for audit and policy coverage. In regulated environments, shipping raw PII to any external endpoint (even your own control plane) triggers compliance review.
What OSS OPA requires: Mask in the PEP before calling OPA, run a log proxy between OPA and EnforceAuth, or accept the compliance exposure. PEP-side masking is error-prone — one new field in input and PII leaks until someone notices.
What EOPA provides: Native decision_logs.mask paths redact fields inside the PDP before the log payload leaves your network:
decision_logs:
service: enforceauth
mask:
- "/input/user/email"
- "/input/user/ssn"
- "/input/request/headers/authorization"
Why this is a benefit:
- Compliance — HIPAA, PCI-DSS, and GDPR often require minimization before data crosses zone boundaries; masking at the PDP is a clean audit story
- Defense in depth — even if EnforceAuth is trusted, logs transiting corporate proxies or SIEM pipelines carry less sensitive payload
- Operational safety — engineers reviewing decision logs in the console see authorization context without full PII
Choose masking when: input routinely carries identifiers you would not paste into a ticket, or legal has flagged decision log retention.
Delta bundles — smaller bundle updates at scale
The problem: OSS OPA downloads the entire bundle.tar.gz on each bundle activation. A one-line Rego change still pulls the full artifact — every sidecar, every poll cycle where the etag changed.
What EOPA provides: Delta bundles apply a patch containing only changed files instead of re-downloading the whole archive.
Why this is a benefit:
| Without deltas | With deltas |
|---|---|
| 50 MB bundle × 500 pods = 25 GB egress per deploy wave | Patch might be kilobytes — proportional to the diff |
| Longer activation time — parse and load full bundle | Faster activation — less CPU and memory spike on rollout |
| Higher object-storage egress cost | Lower cost at fleet scale |
| Wider window where pods run mixed revisions during download | Shorter convergence time after deploy |
Choose delta bundles when:
- You have dozens to thousands of PDP instances polling the same bundle destination
- Bundles are large (shared
data.json, many packages, WASM helpers) - You deploy policy multiple times per day and see S3 throttling or slow rollouts
At small scale (a handful of sidecars, sub-megabyte bundles), full bundles are fine — see Bundle polling for poll tuning.
Partitioning — large multi-tenant fleets
The problem: A single monorepo builds one fat bundle with policies for payments, catalog, admin, and internal tools. Every microservice PDP loads all of it — wasted memory, slower startup, and broader blast radius if one package fails compilation at activation.
What EOPA provides: Partitions let different PDP fleets subscribe to named slices of a release — e.g. payments vs catalog — from one overall build.
Why this is a benefit:
- Memory and startup — each PDP loads only the Rego and
datait needs - Isolation — a bad rule in one domain does not block activation of unrelated packages in another partition
- Multi-tenant operations — platform teams can align partition boundaries with team or system ownership
- Rollout control — promote or roll back one partition without touching others
EnforceAuth alternative: Configure separate bundle paths per system entity — each gets its own bundle.tar.gz from the same Git repo via path overrides. That works well when domains map cleanly to EnforceAuth entities. EOPA partitioning helps when you want one build artifact, many slices at the PDP without splitting storage paths.
Choose partitioning when: One repo feeds many services with distinct policy domains, or you are mirroring a legacy layout that already used partition names.
OSS OPA is sufficient for many workloads. EOPA is common when migrating from Styra DAS™ (deprecated April 30, 2026) or when masking, delta bundles, or partitioning are hard requirements.
EOPA config example
Combine bundle polling (section 1), EnforceAuth telemetry (sections 2–3), and optional masking:
services:
enforceauth-bundle:
url: https://YOUR_BUCKET.s3.YOUR_REGION.amazonaws.com
credentials:
s3_signing:
environment_credentials: {}
enforceauth:
url: ${ENFORCEAUTH_URL}/v1
headers:
X-API-Key: ${ENFORCEAUTH_API_KEY}
bundles:
authz:
service: enforceauth-bundle
resource: policies/bundle.tar.gz
polling:
min_delay_seconds: 10
max_delay_seconds: 20
decision_logs:
service: enforceauth
reporting:
min_delay_seconds: 30
max_delay_seconds: 60
mask:
- "/input/user/email"
- "/input/user/ssn"
- "/input/request/headers/authorization"
status:
service: enforceauth
labels:
id: ${OPA_INSTANCE_ID}
environment: production
Use an EOPA build from the CNCF project (open-policy-agent/eopa) with the same opa run --server --config-file=... entrypoint as OSS OPA. Legacy ghcr.io/styrainc/enterprise-opa images may still run in existing fleets during migration — plan to move to CNCF builds.
Sidecars and deployment patterns
EOPA uses the same deployment patterns as OSS OPA — sidecar, host daemon, centralized service, or gateway-backed. Configure bundle polling and plugins in the PDP config.yaml; mount it the same way in Kubernetes.
Migrating from DAS™? See Migration from Styra DAS™ / EOPA.
Local development without EnforceAuth
opa run --server --bundle ./build/bundle.tar.gz
opa eval -i input.json -d bundle.tar.gz 'data.authz.allow'
Use EnforceAuth dev (api.enforceauth.dev, docs.enforceauth.dev) for integration testing before production keys.
Troubleshooting
| Symptom | Check |
|---|---|
| No decision logs | API key scopes, ENFORCEAUTH_URL, network egress to API |
| 401 on ingest | Revoked or wrong key; create a new key |
| Policies unevaluated in console | PDP not receiving traffic, or logs pointed at wrong tenant entity |
| Stale bundle revision | Bundle polling config vs latest deployment path; see Bundle polling |
| Deploy succeeded but old policy in decisions | PDP serving last good bundle — poll lag or wrong bundle path; check /v1/status bundles |
| New pods deny everything | First bundle activation failed — credentials, path, or bucket policy |
| Intermittent 403s after deploy | Mixed fleet — some pods on new bundle, some on stale; rolling restart or wait for poll cycle |
| S3 throttling on deploy | Poll intervals too aggressive across fleet — stagger min/max |