> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hexgate.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# hexgate policy

> validate, show-rego, test, build, keygen — policy authoring CLI.

The `hexgate policy` subcommands author, inspect, and compile policies without a
running platform. See [WASM bundles](/policy/wasm-bundles) for what `build`
produces and [signing](/policy/signing) for `keygen`.

```bash theme={null}
# Validate a policy.yaml without the network — parse + check every constraint
hexgate policy validate policy.yaml

# See the Rego your YAML compiles to (stdout)
hexgate policy show-rego policy.yaml

# Dry-run a single decision. --engine wasm compiles + evaluates in wasmtime
# (matching production); the default pydantic engine needs no opa.
hexgate policy test policy.yaml --role billing --tool refund_order \
    --args '{"amount": 200, "currency": "USD"}' --engine wasm

# Compile a bundle: writes {stem}.yaml + .rego + .wasm + .bundle.json
hexgate policy build policy.yaml --out ./bundle

# Generate an Ed25519 keypair for signing bundles
hexgate policy keygen --out ./keys/dev          # → dev.private (0600) + dev.public
```

## Prerequisite — `opa`

The WASM compile step shells out to the [Open Policy
Agent](https://www.openpolicyagent.org/) binary. Install it once:

```bash theme={null}
brew install opa            # macOS
# or see https://www.openpolicyagent.org/docs/latest/#running-opa
```

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:

```text theme={null}
✗ DENY · billing → refund_order({"amount": 700})
  reason: Policy denied tool "refund_order": args.amount <= 500
  violations:
    • args.amount <= 500
```

## Testing `ctx.*` attribute rules

Constraints can filter on caller attributes via `ctx.*` (see
[constraints](/policy/constraints)). Pass them to `test` with `--attributes`, a
JSON object so numbers and booleans keep their type:

```bash theme={null}
hexgate policy test policy.yaml --role billing --tool refund_order \
    --args '{"amount": 30}' \
    --attributes '{"department": "finance", "clearance_level": 3}'
```

A `ctx.*` key you don't pass is treated as missing and fails closed, exactly as
at runtime when the request scope omits it.
