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 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. Parts of that floor are enforced below the policy layer and hold for anything running in the sandbox; parts are enforced at the API surface. Where each gate runs sets out which is which — worth reading before you rely on a rule.

The seven gates

Declaw evaluates Rego at seven enforcement gates. Each gate is its own Rego package — write your rules in the package that matches the action you want to govern: pty and stdio are two doors to the same capability — an interactive shell. Denying one does not deny the other, so a policy meant to forbid interactive sessions has to deny both packages. 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).

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.

Where each gate runs

The gates are not all enforced in the same place, and the difference decides what each one can see. network is the clearest case of the first kind: egress is intercepted in the host’s network namespace, so an egress rule holds for every process in the sandbox no matter how it started. cmd, pty and stdio are evaluated by envd, on its API surface. An agent running inside the sandbox that spawns a child directly — subprocess.run(), os.system(), a shell pipeline — never crosses that surface, so those gates do not see it. Commands your code sends through the SDK are gated; commands a running process invents for itself are not. This is worth internalising if you are running an autonomous agent: a cmd denylist constrains what your application asks declaw to run, not everything the agent’s own process may do once started. What still applies to those in-sandbox processes:
  • Egress and content rules, because they are enforced outside the guest.
  • The syscall floor, where enabled — init_module, mount, pivot_root, ptrace, bpf, kexec, memfd_create and the kernel keyring are refused by the kernel itself and inherited by every child process. It is a fixed platform control, not expressible in Rego, and it is enabled on Declaw Cloud; self-hosted operators turn it on per deployment. Note this overlaps the command floor deliberately, at two different levels. The cmd gate refuses the commands (insmod, modprobe, mount, mknod) and applies to every sandbox, but only on the API surface. The syscall floor refuses the syscalls those commands would make, no matter how they are invoked, and is inherited by children. Intent and effect, guarded separately.
  • The microVM, which is the actual isolation boundary. A process that already has execution inside the sandbox is contained by the VM — not by the cmd gate.
Audit records interactive session I/O (stdio_stdin) even where policy does not evaluate it, so in-sandbox activity remains observable when it is not blockable.

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:

What input.context contains, per gate

Gates also receive a context block — but how much of it is populated depends on where the gate runs, so a rule keyed on the wrong field silently never fires. input.context.tier is therefore usable only in the lifecycle and volume gates. A cmd, pty or stdio rule that tests input.context.tier == "free" compares "" against "free", never matches, and denies nothing — it looks like a working policy and is not.
Scope in-VM rules by which policy you attach, not by input.context. Policy is attached per sandbox, so a tier or template distinction is made when you build the policy for that sandbox; the rule inside it is then unconditional:

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.