This plugin provides an LRU cache backed by NATS Key-Value store for OPA. It's not a full storage implementation - just a smart caching layer that overrides the runtime store for specific paths.
This project follows the golang-standards/project-layout convention:
├── cmd/ # Applications
│ └── opa-nats/ # Custom OPA binary with NATS plugin
├── pkg/ # Public library code
│ └── natsstore/ # Core NATS store plugin package
├── examples/ # Usage examples
│ └── opa-nats/ # Complete working example with Docker Compose
├── Dockerfile # Multi-platform container build
├── go.mod, go.sum # Go modules
└── .github/ # CI/CD configuration
The main public library code is in pkg/natsstore
. The cmd/opa-nats
directory contains a ready-to-use OPA binary with the NATS plugin pre-registered.
- Simple LRU Cache: Just an LRU cache with NATS K/V backing - not a full store implementation
- Smart Path Routing: Only caches specific paths (schemas, resource_types, etc.) in NATS
- Automatic Updates: Watches NATS K/V for changes and automatically updates the cache
- TTL Support: Configurable time-to-live for cached entries
- Struct Embedding: Uses Go struct embedding for clean runtime store override
- Production Ready: Includes connection management, error handling, and monitoring
The plugin can be configured through OPA's configuration system. Here's an example configuration:
plugins:
nats:
server_url: "nats://localhost:4222"
ttl: "10m"
refresh_interval: "30s"
max_reconnect_attempts: 10
reconnect_wait: "2s"
max_bucket_watchers: 10
root_bucket: "" # Optional - leave empty for multi-bucket mode
# Authentication (choose one)
credentials: "/path/to/nats.creds"
# OR
token: "your-token"
# OR
username: "user"
password: "pass"
# TLS (optional)
tls_cert: "/path/to/cert.pem"
tls_key: "/path/to/key.pem"
tls_ca_cert: "/path/to/ca.pem"
tls_insecure: false
services:
authz:
url: http://localhost:8181
Option | Type | Default | Description |
---|---|---|---|
server_url |
string | "nats://localhost:4222" |
NATS server URL |
ttl |
duration | "10m" |
TTL for cache entries |
refresh_interval |
duration | "30s" |
How often to refresh cache from NATS |
max_reconnect_attempts |
int | 10 |
Maximum reconnection attempts |
reconnect_wait |
duration | "2s" |
Wait time between reconnection attempts |
max_bucket_watchers |
int | 10 |
Maximum number of bucket watchers in LRU cache |
root_bucket |
string | "" |
Root bucket name for default data (optional) |
credentials |
string | "" |
Path to NATS credentials file |
token |
string | "" |
NATS token for authentication |
username |
string | "" |
NATS username |
password |
string | "" |
NATS password |
tls_cert |
string | "" |
Path to TLS certificate |
tls_key |
string | "" |
Path to TLS private key |
tls_ca_cert |
string | "" |
Path to TLS CA certificate |
tls_insecure |
bool | false |
Skip TLS certificate verification |
The plugin provides custom Rego built-in functions for interacting with NATS K/V:
Watches a NATS K/V bucket and returns all its data. This function automatically manages bucket watchers with LRU caching.
# Watch a specific bucket
group_data := nats.kv.watch_bucket("550e8400-e29b-41d4-a716-446655440000")
# Access nested data
members := group_data.members
permissions := group_data.permissions
Retrieves a specific key from a NATS K/V bucket.
# Get specific data from a bucket
user_data := nats.kv.get_data("users", "123e4567-e89b-12d3-a456-426614174000")
See examples/opa-nats
for a complete example with:
- Docker Compose setup with NATS server and UI
- Example Rego policies using the built-in functions
- Test data setup and configuration
- OPA configuration files
The custom OPA binary is located in cmd/opa-nats
.
import "github.com/permitio/opa-nats/pkg/natsstore"
The simplest way to use this plugin is with the pre-built custom OPA binary:
package main
import (
"log"
"github.com/open-policy-agent/opa/cmd"
"github.com/open-policy-agent/opa/v1/runtime"
natsstore "github.com/permitio/opa-nats/pkg/natsstore"
)
func main() {
// Register the plugin
pluginFactory := natsstore.NewPluginFactory()
runtime.RegisterPlugin(natsstore.PluginName, pluginFactory)
// Run OPA with the registered plugin
if err := cmd.RootCommand.Execute(); err != nil {
log.Fatalf("Failed to execute OPA runtime: %v", err)
}
}
For custom integrations, you can use the plugin as a library. The cmd/opa-nats/main.go
shows the simplest integration approach.
Create an OPA configuration file that includes the plugin (see above for YAML or JSON example).
./opa run --server --config-file opa-config.yaml
docker run -p 8181:8181 -v ./config.yaml:/config.yaml opa-nats \
run --server --config-file=/config.yaml --addr=0.0.0.0:8181
See examples/opa-nats/docker-compose.yaml
for a complete setup with NATS server and example data.
- Struct Embedding: Uses Go struct embedding to cleanly override the runtime store
- Simple Cache: Creates an LRU cache backed by NATS K/V (not a full store implementation)
- Path-Based Routing: Only specific paths (schemas, resource_types, etc.) use the NATS cache
- Auto-Updates: Watches for K/V changes and automatically updates the cache
- TTL Management: Cache entries expire based on the configured TTL
- Transparent Operation: All other OPA functionality (policies, etc.) works normally through the embedded store
The plugin uses a Composite Store pattern that properly overrides the OPA runtime store:
┌─────────────────────────────────────┐
│ SimpleCompositeStore │
│ (Struct Embedding) │
├─────────────────────────────────────┤
│ storage.Store (embedded) │
│ ├─ All methods inherited │
│ └─ Only overrides Read/Write │
│ │
│ Custom routing: │
│ • Read/Write → path-based routing │
│ • Everything else → embedded store │
└─────────────────────────────────────┘
↙ ↘
┌──────────────────────────────┐ ┌─────────────────┐
│ NATS-backed Cache Layer │ │ Original OPA │
│ (LRU Cache + NATS K/V) │ │ Store │
│ ┌───────────────┐ │ │ (embedded) │
│ │ LRU Cache │ │ │ │
│ └───────────────┘ │ │ • Policies │
│ ↓ │ │ • Transactions │
│ NATS K/V (source of truth) │ │ • Triggers │
└──────────────────────────────┘ └─────────────────┘
How it works:
- The LRU cache is a local, in-memory cache to avoid NATS IO for repeated accesses.
- NATS K/V is the single source of truth for the handled paths.
- On a cache miss, data is fetched from NATS and stored in the LRU cache.
- All writes go to NATS and update the cache.
- The rest of OPA's data (policies, etc.) uses the original store.
There are no hardcoded default handled paths. The set of paths routed to NATS K/V is determined entirely by the handled_paths_regex
configuration option. If handled_paths_regex
is empty or not set, no paths will be routed to NATS K/V.
All other paths (policies, etc.) continue using the original OPA store.
The plugin maps OPA storage paths to NATS K/V keys using dot notation:
- OPA path:
["data", "schemas", "user"]
- NATS K/V key:
data.schemas.user
The plugin provides statistics through the OPA status API:
curl http://localhost:8181/v1/status
Statistics include:
- Cache size and capacity
- NATS connection status
- Number of registered triggers
- Policy count
- NATS Server 2.3.0+ with JetStream enabled
- K/V store functionality enabled
jetstream: enabled
accounts: {
APP: {
jetstream: enabled
users: [
{user: "opa", pass: "secret", permissions: {
subscribe: ["$JS.API.>", "$KV.>"]
publish: ["$JS.API.>", "$KV.>"]
}}
]
}
}
This is a standalone Go library, maintained in the opa-nats repository.
go build -o opa ./cmd/opa-nats
docker build -t opa-nats .
docker buildx build --platform linux/amd64,linux/arm64 -t opa-nats .
go test ./... -cover
This library uses GitHub Actions for CI, including linting, build, security checks, and test coverage reporting. See .github/workflows/pr_checks.yml.
- Authentication: Always use proper authentication (credentials, tokens, or user/pass)
- TLS: Enable TLS for production deployments
- Network Security: Ensure NATS server is properly secured and accessible only to authorized clients
- Bucket Permissions: Configure appropriate K/V bucket permissions
- Connection Failed: Check NATS server URL and authentication
- Bucket Not Found: Ensure the K/V bucket exists or the plugin has permissions to create it
- Cache Misses: Check TTL settings and NATS connectivity
- Performance Issues: Adjust cache size and refresh intervals
The plugin uses structured logging. Enable debug logging to see detailed operation:
plugins:
nats:
# ... other config
log_level: debug
This project is licensed under the Apache License, Version 2.0. See the LICENSE file for details.
Contributions are welcome! Please open issues or pull requests on the opa-nats GitHub repository for bug reports, feature requests, or improvements related to the nats-store
library.
This repository uses pre-commit to enforce Go best practices and code quality. Hooks include:
golangci-lint
(linting)go fmt
(formatting)go vet
(static analysis)go mod tidy
(module tidiness)- Trailing whitespace and end-of-file checks
- Install pre-commit (requires Python):
pip install pre-commit
- Install the git hook scripts:
pre-commit install
- Run all hooks on all files (before pushing):
pre-commit run --all-files
All CI checks will run these hooks. Please ensure your code passes before submitting a PR.