Skip to main content
Governance packs give you curated, framework-aligned policy with one 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: The gates also enforce differently, which matters when you test:
  • cmd deny → the command is rejected before it runs (surfaces as an HTTP 403 / raised exception, not a non-zero exit code).
  • network deny → the command runs, but the outbound connection is dropped at the proxy (e.g. curl exits non-zero with a reset/timeout — there is no 403).
Because the agent runs inside the declaw microVM, these gates are not advisory — they are the only path the agent’s actions can take.

The deny-only model

You only ever write deny rules:
Do not redeclare default allow. Each platform package already derives allow in Go purely from “zero denials” — an allow rule you write is ignored, and redeclaring default allow collides with the platform module your rules are compiled alongside. Express everything as deny contains msg if { ... }. An allowlist is just “deny anything not in the approved set” (see the allowlist pattern).
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

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 write deny 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:
The same pattern at the network gate — an egress allowlist using glob.match for wildcard domains:

Input schema per gate

Each gate evaluates your rules against an input document. The key fields: Every gate also receives a shared context block: 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:
The model is read from the request JSON body and exposed as 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 redeclared default 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.
A quick way to validate the gate behavior end-to-end is to run a command that should be blocked and confirm the failure mode matches the gate: a 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:, or blob: instead of inlining the Rego on every sandbox.
  • Network policies — the domain-allowlist / IP-CIDR layer that runs alongside the network gate.