scobleclaw/scoble-policy/opa/README.md
hermes 39b21dcda5 feat(opa): Layered judge L0 — OPA deployment + 4 Rego policies (eng#40)
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.
2026-06-08 16:36:37 -07:00

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.local
  • opa-policies-configmap.yaml — ConfigMap containing the 4 .rego files
  • policies/ — 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:
    { "result": true }
    
    or { "result": false } if deny is 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)