policy_ref. When you need rules specific to your threat model — a command denylist, an egress allowlist, a model allowlist — you can author them yourself in OPA Rego and attach them to a sandbox with CustomPolicyConfig.
Your rules are additive to declaw’s non-bypassable platform floor (which already blocks living-off-the-land commands, kernel-module loading, device/storage operations, and cloud-metadata/IMDS access). You write deny rules that tighten policy; you can never relax a platform default.
The six gates
Declaw evaluates Rego at six enforcement gates. Each gate is its own Rego package — write your rules in the package that matches the action you want to govern:| Gate | Package | Governs | When it runs |
|---|---|---|---|
cmd | declaw.platform.cmd | Every command the agent (or a sub-agent) runs | Before the command executes |
network | declaw.platform.network | Every outbound connection | Per egress connection, at the proxy |
content | declaw.platform.content | The request body on LLM egress (model, scan findings) | On intercepted LLM traffic (opt-in) |
lifecycle | declaw.platform.lifecycle | Sandbox provisioning (tier, template, requested features) | At sandbox create |
pty | declaw.platform.pty | Interactive PTY sessions | At PTY creation |
volume | declaw.platform.volume | Volume mounts | Once per attached volume, at create |
cmddeny → the command is rejected before it runs (surfaces as an HTTP 403 / raised exception, not a non-zero exit code).networkdeny → the command runs, but the outbound connection is dropped at the proxy (e.g.curlexits non-zero with a reset/timeout — there is no 403).
The deny-only model
You only ever writedeny rules:
deny is a partial set, and partial-set rules are additive across modules — so your denials compose with the platform defaults (and with each other) without ever being able to remove a platform denial.
CustomPolicyConfig
| Field | Type | Description |
|---|---|---|
enabled | bool | Activates custom-policy evaluation for the sandbox. |
inline_rego | str | A single Rego module string (one package). Simplest option when you only need one gate. |
inline_modules | list[str] | A list of independent Rego module strings — one per package. Use this to govern multiple gates at once (you cannot put two packages in one inline_rego string). |
policy_ref | str | Reference a built-in pack or a published bundle. One of name@version (e.g. owasp-llm-top10@v1), sha256:<hex>, or blob:<key>. |
default_deny | bool | Fail behavior when the evaluator itself errors. true = an evaluator error denies the action (fail-closed — recommended for hard security gates). false = the action is allowed on evaluator error (fail-open — for advisory rules). |
inline_rego, inline_modules, and policy_ref can be combined — all sources are concatenated with the platform defaults before evaluation. Use inline_rego for the single-package case and inline_modules when your policy spans gates.Worked examples
Allowlists with a deny-only engine
Since you can only writedeny rules, an allowlist is expressed as “deny anything not in the approved set”. Combine it with default_deny=True so an evaluator fault also blocks:
glob.match for wildcard domains:
Input schema per gate
Each gate evaluates your rules against aninput document. The key fields:
Gate (package) | Key input fields |
|---|---|
cmd | input.action.command (the binary, e.g. "curl" — not the full shell line), input.action.args (argument list) |
network | input.action.destination (target host or IP; may include a :port suffix — strip it with split(..., ":")[0] if matching the host) |
content | input.attributes.model (LLM model from the JSON body), input.attributes.destination (request hostname), input.attributes.scan_results.injection.is_injection, input.attributes.scan_results.toxicity.is_toxic, input.attributes.scan_results.pii, input.attributes.scan_results.code_security, input.attributes.scan_results.invisible_text |
lifecycle | input.attributes.policy (the requested SecurityPolicy, e.g. input.attributes.policy.toxicity.enabled) |
pty | input.context.tier (and the shared context below) |
volume | input.action.mode ("copy" | "mount" | "mount-ro"), input.action.mount_path (absolute in-sandbox path) |
input.context.tier, input.context.account_id, input.context.sandbox_id, and input.context.template. Use it to scope a rule, e.g. forbid interactive shells on a locked-down tier:
Content gate
The content gate (declaw.platform.content) sees the decrypted LLM request body — the model name and the ML scanner’s findings — so you can enforce a model allowlist or write cross-signal rules that combine multiple scan results.
This gate runs inside the body-scanning path, so by default it only fires when a scanner is already intercepting that domain. To run a model-allowlist rule without enabling an ML scanner, opt the sandbox in with ContentGateConfig and list the LLM API hosts to intercept:
input.attributes.model. Rule A and Rule B are independent — either firing blocks the request before it reaches the upstream LLM.
Content-gate caveats. The gate is only evaluated when the connection is intercepted — either by an active ML scanner on the domain or by an explicit
ContentGateConfig entry for that host (per-destination network rules are unaffected; that gate always runs). Also, scan_results.pii.count reflects whole-body (non-JSON) text; for JSON LLM bodies, value-level PII is scanned and redacted separately, so don’t gate on pii.count for JSON traffic.Testing and fail-closed behavior
Your inline Rego is compile-checked at sandbox create. If a module fails to parse or compile (a syntax error, a redeclareddefault allow, an unknown built-in), the create request fails with an error describing the problem — invalid policy never reaches a running sandbox.
At runtime, default_deny controls what happens if the evaluator itself errors (engine unreachable, evaluation timeout):
default_deny=True(fail-closed) — an evaluator error denies the action. Recommended for any hard security gate (denylists, allowlists, model gating).default_deny=False(fail-open) — the action is allowed on evaluator error. Acceptable only for advisory rules where availability outweighs the missed check.
cmd-gate denial surfaces as a 403 / raised exception, while a network-gate denial lets the command run but drops the connection (use a resolvable host so the test exercises the gate and not a DNS failure).
Next steps
- Governance packs — curated, framework-aligned policy (OWASP, NIST, EU AI Act, and more) you enable with a single
policy_ref, with per-denial compliance evidence. - Policy bundles — publish your own modules as a versioned, reusable bundle and reference it by
name@version,sha256:, orblob:instead of inlining the Rego on every sandbox. - Network policies — the domain-allowlist / IP-CIDR layer that runs alongside the
networkgate.