- Updated: March 18, 2026
- 5 min read
Embedding the OpenClaw Rating API Edge Token‑Bucket Limiter with Istio and OPA
Embedding the OpenClaw Rating API Edge Token‑Bucket Limiter with Istio and OPA
This guide walks senior engineers through a step‑by‑step, publish‑ready integration of the OpenClaw Rating API Edge token‑bucket limiter using Istio and Open Policy Agent (OPA) policies. It builds on the existing OPA integration described in Host OpenClaw on a Dedicated Server and shows how to extend that work with Istio traffic management and token‑bucket rate limiting.
Prerequisites
- Kubernetes cluster (v1.24+ recommended)
- Istio installed (v1.18+)
- OPA sidecar injector enabled
- OpenClaw Rating API Edge endpoint reachable from the cluster
- kubectl configured for the target cluster
1. Deploy the OpenClaw Rating API Edge Service
First, create a Kubernetes Service and Deployment for the OpenClaw Rating API Edge. Use the official Docker image (e.g., ubos/openclaw-rating:latest) and expose it on port 8080. Ensure the service name is openclaw-rating so that Istio can reference it in VirtualService definitions.
apiVersion: apps/v1
kind: Deployment
metadata:
name: openclaw-rating
spec:
replicas: 2
selector:
matchLabels:
app: openclaw-rating
template:
metadata:
labels:
app: openclaw-rating
spec:
containers:
- name: openclaw-rating
image: ubos/openclaw-rating:latest
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: openclaw-rating
spec:
selector:
app: openclaw-rating
ports:
- protocol: TCP
port: 80
targetPort: 8080
2. Add OPA Policy for Token‑Bucket Limiting
Reuse the OPA policy template from the existing integration (Host OpenClaw) and adapt it to enforce a token‑bucket algorithm per API key. Store the policy in a ConfigMap and mount it into the OPA sidecar.
apiVersion: v1
kind: ConfigMap
metadata:
name: openclaw-opa-policy
namespace: default
labels:
app: openclaw-rating
annotations:
opa/allow: "true"
opa/policy: |
package openclaw.limiter
default allow = false
# Token bucket state (stored in OPA's in‑memory data store)
token_bucket[api_key] = {"capacity": 100, "tokens": tokens, "refill_rate": 10, "last_refill": last}
# Refill logic
refill(api_key) = new_state {
bucket := token_bucket[api_key]
now := time.now_ns() / 1000000
elapsed := now - bucket.last_refill
added := int(elapsed * bucket.refill_rate / 1000)
new_tokens := min(bucket.capacity, bucket.tokens + added)
new_state := {"capacity": bucket.capacity, "tokens": new_tokens, "refill_rate": bucket.refill_rate, "last_refill": now}
}
# Main allow rule
allow {
input.method == "GET"
api_key := input.headers["x-api-key"]
bucket := token_bucket[api_key]
bucket.tokens > 0
# Consume a token
new_state := refill(api_key)
new_state.tokens = new_state.tokens - 1
# Update state (OPA will persist this in its data store)
data.openclaw.limiter.token_bucket[api_key] = new_state
}
3. Configure Istio EnvoyFilter to Forward Headers to OPA
Istio must forward the x-api-key header to the sidecar so OPA can evaluate it. Add an EnvoyFilter that adds the header to the request metadata.
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: openclaw-header-forward
spec:
workloadSelector:
labels:
app: openclaw-rating
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: envoy.http_connection_manager
patch:
operation: MERGE
value:
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inlineCode: |
function envoy_on_request(request_handle)
local api_key = request_handle:headers():get("x-api-key")
if api_key then
request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.rbac", "api_key", api_key)
end
end
4. Create an Istio AuthorizationPolicy that Calls OPA
Link the OPA policy to Istio using an AuthorizationPolicy that references the OPA sidecar via custom action.
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: openclaw-opa-authz
spec:
selector:
matchLabels:
app: openclaw-rating
action: CUSTOM
provider:
name: opa
kind: opa
rules:
- to:
- operation:
methods: ["GET", "POST"]
when:
- key: request.headers[x-api-key]
values: ["*"]
5. Deploy the OPA Sidecar Injector
Enable the OPA sidecar injector for the openclaw-rating deployment. The injector will mount the policy ConfigMap and start OPA with the --watch flag so policy updates are hot‑reloaded.
apiVersion: apps/v1
kind: Deployment
metadata:
name: openclaw-rating
spec:
template:
metadata:
annotations:
sidecar.istio.io/inject: "true"
opa.sidecar.istio.io/config: |
policies:
- name: openclaw-limiter
path: /etc/opa/policy.rego
services:
- name: openclaw-rating
url: http://localhost:8181/v1/data/openclaw/limiter/allow
spec:
containers:
- name: openclaw-rating
...
- name: opa
image: openpolicyagent/opa:0.64.0
args:
- "run"
- "--server"
- "--watch"
- "/etc/opa/policy.rego"
volumeMounts:
- name: policy
mountPath: /etc/opa
volumes:
- name: policy
configMap:
name: openclaw-opa-policy
6. Verify the Rate‑Limiting Behaviour
Use curl or a load‑testing tool (e.g., hey) to send requests with a valid x-api-key. Observe that after the bucket is exhausted, the API returns 403 Forbidden (or the status you configure in OPA). Refill occurs at the configured rate (10 tokens per second in the example).
# Exhaust the bucket (100 requests)
for i in $(seq 1 100); do curl -s -o /dev/null -w "%{http_code}\n" -H "x-api-key: demo-key" https://api.yourdomain.com/rate; done
# Subsequent request should be blocked
curl -I -H "x-api-key: demo-key" https://api.yourdomain.com/rate
Conclusion
This guide demonstrates how to extend the existing OPA integration for OpenClaw with Istio’s traffic management capabilities, delivering a robust token‑bucket rate‑limiting solution. By following the steps above, senior engineers can quickly embed the OpenClaw Rating API Edge limiter into their service mesh, ensuring fair usage and protecting downstream services.
For more details on the base OPA integration, see the Host OpenClaw on a Dedicated Server article.