Skip to main content
A policy bundle is a set of OPA Rego modules you publish once to your account and then reference from any sandbox by a short policy_ref. Bundles are versioned and immutable: a published name@version always resolves to the exact same Rego, so a sandbox config can pin a policy that can never silently change underneath it. This page covers the admin side — publishing bundles, referencing them, managing the registry, and setting a non-bypassable account-wide floor. For writing the Rego itself, see Authoring custom policy. For curated, framework-aligned bundles you can enable without writing any Rego, see Governance Packs.
Custom policy bundles build on declaw’s non-bypassable platform floor (which already blocks living-off-the-land commands, kernel-module loading, and cloud-metadata/IMDS access). Every bundle is compile-checked against that floor at publish time. A bundle can only add denials — it can never relax the platform defaults.

Publish a bundle

POST /admin/accounts/<account_id>/policy-bundles with a name, a version, and the Rego modules that make up the bundle:
The response confirms the stored content and gives you both ways to reference it:

Validation rules

  • Compile-checked. The modules are compiled together with the platform floor at publish time, so a bundle that does not compile is rejected with 400 before it is stored — never at sandbox-create time.
  • Size limit. A bundle’s canonical bytes may not exceed 1 MiB. Larger bundles are rejected at publish (they would otherwise be unresolvable on the read path).
  • Versions are immutable. A name@version is write-once. Re-publishing the same content is idempotent (returns the same content_hash), but re-publishing a name@version with different content returns 409 Conflict — the existing sha256_ref is included in the error so you can see what is already pinned there. To ship a change, publish a new version (acme-baseline@v4).
Because versions are immutable, treat @vN like a Git tag, not a branch. Bump the version for every change; never try to “update” a published version in place.

Reference a bundle from a sandbox

A sandbox references a bundle through custom_policy.policy_ref. There are three reference forms, all resolving against declaw-controlled storage only (there is no arbitrary URL fetch):

Resolution precedence

A name@version ref is resolved at sandbox-create time as follows:
  1. Look up (your account, name, version) in the registry.
  2. If not found, fall back to the shared platform account (this is how built-in governance packs resolve, and how a platform team can publish org-wide baselines once).
  3. If still not found, the create fails with 400 (a genuinely missing ref is a client error, not a server error).
The resolved content_hash is then fetched from the content-addressed store and the Rego is appended to the sandbox’s policy. sha256: and blob: refs skip the registry entirely and read the object directly.
Versioned name vs. sha256 — which to use. Use name@version for production: it is account-scoped, readable, and the natural unit you bump and roll out. Use sha256:<hex> when you need an absolute immutable pin — the content address can never resolve to anything but those exact bytes, even if a registry pointer is later deleted or repointed. (The sha256: and blob: forms are content-addressed-global by design: they read the dedicated, non-tenant-writable policy bucket and ignore the account, so confidentiality of policy text under these forms rests on hash opacity, not access control — hashes are not secrets. For per-account isolation, use name@version.)

Manage published bundles

List and fetch return the same fields as publish — name, version, content_hash, policy_ref, sha256_ref, and created_at. A missing bundle returns 404.
Delete removes the pointer, not the content. Deleting a bundle removes only its name@version registry entry. The content-addressed object survives (it may be shared across versions and is verified by hash on every read), so any sha256:<hex> ref to the same bytes keeps resolving. To make a policy permanently unreferenceable you must stop referencing its content hash, not just delete the named pointer.

Set an account-wide policy floor

To enforce a policy on every sandbox an account creates — with no per-sandbox change and no way for a sandbox to opt out — set the account floor:
You can set policy_ref, inline_rego, or both — an enabled floor needs at least one. Inline Rego is compile-validated synchronously (a broken module returns 400 here); a policy_ref is validated lazily at sandbox-create time, since the referenced bundle may be published independently.
The floor is non-bypassable. The account floor is merged into every sandbox’s policy as an additional module at create time. A sandbox’s own custom_policy can add rules on top, but it cannot remove or override the account floor — bundle denials are additive and tighten-only.
The floor is resolved and frozen at sandbox-create time. Enabling or tightening the account policy does not retroactively apply to already-running sandboxes, and resume / snapshot-restore reuse the create-time policy rather than re-merging the current floor. Roll out a tightened floor with the expectation that it takes effect on newly created sandboxes.
  • Authoring custom policy — writing the Rego gates (cmd, net, content, lifecycle) that go into a bundle.
  • Governance Packs — curated, framework-aligned bundles (OWASP, NIST, EU AI Act, …) you can enable without writing Rego.
  • Compliance reporting — per-account denial evidence grouped by control, framework, and gate.