Deploys openpolicyagent/opa:0.68.0 as a single-replica ClusterIP
service in the scobleclaw namespace, with a ConfigMap-mounted
policy bundle under /policies.
Policies (package scobleclaw):
_lib.rego shared deny rules (external email, forgejo
write outside marcus/engineering, payment
kamelets) + helpers (is_external_email,
is_payment_kamelet, is_forgejo_write_outside_allowed)
main-agent.rego 10-kamelet allowlist for main-agent
demo-concierge.rego read-only 4-kamelet allowlist
demo-watchdog.rego 4-kamelet allowlist (ob1 + discord)
L0 contract:
POST /v1/data/scobleclaw/allow -> {allow: bool}
POST /v1/data/scobleclaw/needs_l1 -> {needs_l1: bool}
POST /v1/data/scobleclaw/deny -> [reason, ...]
Verified end-to-end against the running OPA pod:
allow=true for main-agent + kamelet:source/forgejo-search
deny=[reason] for external email, forgejo write to stranger/not-mine,
payment kamelet (stripe-charge), demo-concierge forgejo-create
needs_l1=true for unknown kamelet (e.g. some-new-kamelet) on main-agent
needs_l1=false for payment kamelet (L1 cannot override L0 deny)
Out of scope (separate tickets): judge-integration Camel-K binding,
llm-classifier-integration (L1), discord-escalation-integration (L2),
audit-sink OPA log tailing.
3 KiB
3 KiB
opa/
OPA deployment + Rego policy bundle for the L0 (static policy) layer of the layered judge stack (eng#40).
Layout
opa-deployment.yaml— ServiceAccount + Deployment (1 replica, 64Mi mem, 8181)opa-service.yaml— ClusterIP service on :8181,opa.scobleclaw.svc.cluster.localopa-policies-configmap.yaml— ConfigMap containing the 4 .rego filespolicies/— source-of-truth copy of the .rego files (GitOps reference; the ConfigMap is the runtime mount)
Policies
All policies share the scobleclaw package. _lib.rego defines the base
deny rules and helpers (is_external_email, is_payment_kamelet,
is_forgejo_write_outside_allowed); the per-agent files add their own
allowlist + agent-specific deny rules.
| Agent | File | Allowlist size | Per-agent denies |
|---|---|---|---|
| main-agent | main-agent.rego |
10 | none beyond _lib |
| demo-concierge | demo-concierge.rego |
4 | read-only |
| demo-watchdog | demo-watchdog.rego |
4 | none beyond _lib |
L0 contract
- Input (
POST /v1/data/scobleclaw/allow):{ "input": { "agent_id": "main-agent", "kamelet_ref": "kamelet:source/forgejo-search", "params": {}, "request_id": "...", "timestamp": "..." } } - Output:
or{ "result": true }{ "result": false }ifdenyis non-empty or the kamelet is not on the agent's allowlist. The judge-integration Camel-K binding translates(allow, deny, needs_l1)into a single{decision, reasons, decision_id}envelope for the audit-sink.
Apply
kubectl apply -f scoble-policy/opa/
Verify
# deployment health
kubectl -n scobleclaw get deploy opa # 1/1 Ready
# service exists, ClusterIP
kubectl -n scobleclaw get svc opa # ClusterIP 8181
# policies loaded
kubectl -n scobleclaw exec deploy/opa -- \
wget -qO- http://localhost:8181/v1/policies | jq
# smoke test (allow)
kubectl -n scobleclaw exec deploy/opa -- \
wget -qO- --post-data='{"input":{"agent_id":"main-agent","kamelet_ref":"kamelet:source/forgejo-search","params":{}}}' \
--header='Content-Type: application/json' \
http://localhost:8181/v1/data/scobleclaw/allow
# expected: {"result": true}
# smoke test (deny: external email)
kubectl -n scobleclaw exec deploy/opa -- \
wget -qO- --post-data='{"input":{"agent_id":"main-agent","kamelet_ref":"kamelet:source/send-email","params":{"to":"bob@evil.com","domain":"evil.com"}}}' \
--header='Content-Type: application/json' \
http://localhost:8181/v1/data/scobleclaw/allow
# expected: {"result": false}
Out of scope
- judge-integration Camel-K binding (separate ticket — calls OPA at
/v1/data/scobleclaw/allow) - llm-classifier-integration (L1)
- discord-escalation-integration (L2)
- audit-sink OPA log tailing (separate ticket, blocked on CDI/CRD scope creep — see eng#46/eng#93/eng#94)