scobleclaw/scoble-policy/opa/policies/demo-concierge.rego

53 lines
1.9 KiB
Rego

package scobleclaw.demo_concierge
# demo-concierge.rego — policy for the demo concierge agent.
#
# Per scobleclaw#42 refactor: this file now lives at
# `package scobleclaw.demo_concierge` (was `package scobleclaw` in the
# package-root layout). API path is now
# `data.scobleclaw.demo_concierge.allow`.
#
# Scope: read-only Forgejo + OB1 read access. No writes anywhere. This
# is a demo / smoke-test agent; its allowlist is intentionally small.
import data.scobleclaw.lib
demo_concierge_allowed_kamelets = {
"kamelet:source/forgejo-search",
"kamelet:source/forgejo-read",
"kamelet:source/ob1-search",
"kamelet:source/kamelet-dispatch",
}
# ---------------------------------------------------------------------------
# Deny set — re-emit base_deny from lib, plus per-agent rules.
# ---------------------------------------------------------------------------
deny[reason] {
lib.base_deny[reason]
}
# Per-agent deny rule: demo-concierge is read-only. Any kamelet whose
# name suggests a Forgejo write (starts with `forgejo-create`) is
# denied at L0. Note: this is in addition to the lib base_deny rules
# (external email, forgejo writes outside engineering, payment kamelets).
deny[reason] {
input.agent_id == "demo-concierge"
startswith(input.kamelet_ref, "kamelet:source/forgejo-create")
reason := sprintf("demo-concierge is read-only; %v not permitted", [input.kamelet_ref])
}
# Allow only if kamelet is on the allowlist AND no shared deny rule fires.
allow {
input.agent_id == "demo-concierge"
demo_concierge_allowed_kamelets[input.kamelet_ref]
count(deny) == 0
}
# Demo-concierge never needs L1 — if a kamelet isn't on its allowlist, it
# is a misconfiguration and should be denied at L0 so the operator notices.
needs_l1 {
input.agent_id == "demo-concierge"
not demo_concierge_allowed_kamelets[input.kamelet_ref]
not lib.payment_kamelets[input.kamelet_ref]
}