Skip to main content
The hexgate policy subcommands author, inspect, and compile policies without a running platform. See WASM bundles for what build produces and signing for keygen.

Prerequisite — opa

The WASM compile step shells out to the Open Policy Agent binary. Install it once:
Without opa on PATH, hexgate policy build --no-wasm still emits the yaml + rego (no .wasm), and the pydantic engine keeps working. Override the binary location with HEXGATE_OPA_BIN.

Reading a denied test

On a denied decision, test prints the reason; the wasm engine additionally lists each violated constraint string verbatim:

Multi-role dry-runs (--roles)

--role evaluates one role; --roles a,b evaluates a whole set exactly as the runtime does — every role is evaluated and the most permissive outcome wins, so the output also names the role that granted the call:
The two flags differ on undefined names, deliberately:
  • --role nope fails — a single undefined role is almost always a typo.
  • --roles support,nope warns and continues, evaluating nope against the default policy. That is what happens at runtime, and a dry-run that refused would disagree with production.
  • --roles "" fails — an empty set names no roles. Scripting this as --roles "$ROLES" with an unset variable would otherwise dry-run the default policy and exit 0, passing CI while asserting nothing. Pass --roles default if the fallback policy is what you meant to test.
At most 32 distinct roles are evaluated, matching the enforcer; past that the dry-run warns on stderr and evaluates the first 32, as production would. That fallback is also why default should stay least-privilege — see request context. hexgate policy validate warns when the default role grants something no named role grants, and when the document declares roles but never names one default (so one of them silently becomes the fallback):

Testing ctx.* attribute rules

Constraints can filter on caller attributes via ctx.* (see constraints). Pass them to test with --attributes, a JSON object so numbers and booleans keep their type:
A ctx.* key you don’t pass is treated as missing and fails closed, exactly as at runtime when the request scope omits it.