platformengineering/policyascode devops
Open Policy Agent (OPA) Intro & Deep Dive
Speaker: Charlie Egan & Anders Eknert (Styra)
[!video]- Open Policy Agent (OPA) Intro & Deep Dive - Charlie Egan & Anders Eknert, Styra
Tldr
Policy Decoupling: OPA acts as a centralized “judge” that decouples logical authorization rules from application code using a standard “JSON-in, JSON-out” architecture.
Performance Breakthrough (OPA 1.0+): Real-world evaluations are now up to 2x faster (~50% reduction in evaluation times) largely driven by aggressive memory allocation optimizations on hot paths.
Dogfooding with Regal: Regal (the Rego linter and Language Server) was built entirely in Rego to lint itself, exposing performance bottlenecks and bugs that helped harden the core OPA engine.
String Interpolation & Streamed Tests: Upcoming features on the OPA roadmap seek to solve common user “footguns” (like rule failures on undefined references in
sprintf) and deliver real-time feedback to developers.Gatekeeper Upgrades: The sibling Kubernetes-native engine now supports OPA 1.0, adds Gator CLI improvements for local pre-rollout validation, and is graduating validating admission policies to beta.
Core OPA Architecture: The “Judge” Analogy
"OPA is your judge... It takes a JSON message, runs some Rego policy, and returns a JSON value. It's JSON-in and JSON-out."
— Charlie Egan
OPA operates on a strictly decoupled model. Instead of hardcoding authorization and rules inside application logic, services query OPA dynamically.
graph TD
Client[Client Request] -->|Triggers Action| Service[Application Service]
Service -->|Query: JSON Context| OPA[OPA Engine / 'The Judge']
OPA -->|Decision: JSON Payload| Service
subgraph Control Loop
BundleServer[Bundle Server] -.->|Async Pull: Policies & Data| OPA
OPA -.->|Async Push: Logs| LogStore[Decision Log Store]
end
classDef default fill:#1e1e2e,stroke:#cdd6f4,stroke-width:1px,color:#cdd6f4;
classDef highlight fill:#f5c2e7,stroke:#f5c2e7,stroke-width:2px,color:#11111b;
class OPA highlight;Key Pillars of the Decoupled Model
- Decoupling Access Control: Standardizes and abstracts policies away from the underlying business code. Developers can focus purely on business value without rebuilding policy controls.
- Declarative Policies: Written in Rego, making policies easy to read, version control, collaborate on, and run through standard GitOps pull-request workflows.
- The Bundle Mechanism: OPA caches policies and data in memory. It asynchronously pulls down updated policy bundles (
.tar.gzcontaining Rego files and JSON data) from a central server to ensure instant local evaluations. - Decision Logging: All authorization decisions are logged and pushed to a remote log store for compliance auditing, debugging, and system monitoring.
OPA Gatekeeper Updates
As OPA’s sibling project specifically designed for Kubernetes admission control, Gatekeeper has introduced several key enhancements:
- OPA 1.0 Alignment: Gatekeeper now fully integrates OPA 1.0 (users must set the
source-versionfield to target the updated engine). - Gator CLI Improvements: Enhances local validation capabilities, allowing developers to test and check resources against policies before applying them to live clusters.
- Pub/Sub and Violation Management: Improvements to the pub/sub architecture and new capabilities to export violation reports directly to disk.
- Validating Admission Policy: Integration is officially graduating to Beta.
Future Roadmap: Developer Experience & Quality of Life
The OPA maintainers are tackling prominent developer friction points and “footguns” with three core roadmap features:
1. Streaming Tests
- The Problem: Currently, running
opa testwaits for the entire suite to finish before outputting results. - The Solution: A streaming model (similar to
go test) where test results are printed to the console in real-time. This provides instant editor feedback when integrated with language tooling (LSP).
2. Logical OR (or) and Alternative (??) Operators
- The Problem: Rego classically lacks an explicit
oroperator. Users find expressing simple alternative logic verbose and confusing when starting out. - The Solution: Introduction of an alternative operator (resembling a ternary or null-coalescing operator) to attempt evaluating a value and gracefully falling back to a default if it resolves to
undefined.
3. String Interpolation
The
sprintfFootgunIn current OPA versions, a common trap is writing
sprintf("Warning: user %v unauthorized", [user.name]). Ifuser.nameis undefined, the entire rule body fails to evaluate, even if the missing variable was non-essential to the core rule logic.
- The Solution: Native string interpolation will safely handle undefined variables—evaluating them as a placeholder (like
unknownor?) instead of completely breaking policy evaluation.
Technical Deep Dive: OPA Performance & Regal
While OPA has always been performant for average workloads, building Regal (the Rego Linter and Language Server) pushed OPA evaluations to a scale never seen before.
The “Dogfooding” Story
"We built a linter in Rego to lint the Rego in the linter that was built in Rego. It was a learning journey, and it paid off."
— Anders Eknert
- The Challenge: Regal contains around 100 lint rules totaling 15,000 lines of Rego.
- The Scale: When linting its own codebase (approx. 200 files) on every single keypress in the editor, Regal must evaluate ~3,000,000 lines of Rego.
- The Problem: CPU-bound environments (like single/dual CPU GitHub Actions runners or rapid local keypresses) suffered multi-second latencies, rendering real-time LSP autocomplete and linting unusable.
Key Optimization Strategies (Core OPA Changes)
To speed up the linter, the team had to speed up the OPA engine itself. They focused extensively on hot path profiling and Go memory allocation reduction.
1. Number Interning (Count Function Optimization)
- Old Behavior: The heavily-used
countfunction requested a new memory allocation from the Go runtime every time it was called. Evaluated 1 million times, it made 1 million allocations. - New Behavior: Implemented number interning. OPA now caches and reuses the same memory references for common numbers, drastically shrinking Go’s garbage collector overhead.
- Additional Fixes: Redesigned basic data structures, optimized built-in function runtimes, and resolved underlying OPA compiler bugs discovered during development.
Benchmarks: OPA 0.70 vs. OPA 1.0+
On a standard local MacBook Pro testing Regal’s codebase:
| Metric | OPA 0.70 (Pre-optimizations) | OPA 1.0+ (Post-optimizations) | Improvement |
| Evaluation Time | ~2.0 seconds | ~1.0 second | ~50% faster (nearly cuts latency in half) |
| CI Pipelines | Several Minutes | Under a Minute | Multi-fold speedups in resource-constrained environments |
Key Q&A Insights
- Why build a linter in Rego? Why not Go?
Writing it in Go would have been initially faster to execute and easier to tweak. However, writing Regal in Rego forced the maintainers to push OPA to its absolute limits. This intense dogfooding revealed critical bugs, drove major performance enhancements to the core OPA evaluator, and significantly strengthened the ecosystem for everyone in the long run.