# Writing Rego Policies \[Write Rego policies for blockchain transaction authorization with Newton Protocol. Examples for spend limits, allowlists, sanctions screening, and fraud prevention rules.]

A Newton policy is a Rego program that evaluates whether an Intent should be approved. Policies reference data from two sources: configuration parameters (`data.params`) and runtime data from WASM oracles (`data.wasm`).

## What Makes Up a Policy

Every policy deployment requires these files:

| File | Purpose |
|------|---------|
| `policy.rego` | Rego policy logic — the core evaluation rules |
| `policy.wasm` | Compiled WASM data oracle (see [Writing Data Oracles](/developers/guides/writing-data-oracles)) |
| `params_schema.json` | JSON Schema defining configurable parameters |
| `policy_metadata.json` | Human-readable policy metadata |
| `policy_data_metadata.json` | Human-readable oracle metadata |

## Data References

Your Rego policy can access three data namespaces:

| Path | Source | Description |
|------|--------|-------------|
| `input` | Intent | The transaction intent being evaluated (from, to, value, data, chain\_id, function\_signature) |
| `data.params` | PolicyClient | Configuration parameters set by the contract owner (thresholds, allowlists) |
| `data.wasm` | PolicyData WASM | Runtime data returned by your WASM oracle(s) (prices, KYC status). With multiple oracles, each is namespaced under its pack id — see [Consuming Multiple Oracles](#consuming-multiple-oracles) |

## Your First Policy

Create `policy.rego`:

```rego
package sanctions_check

default allow := false

# Allow the transaction if the oracle reports no sanctions match
allow if {
    data.wasm.is_sanctioned == false
}

# Also allow if the sender is on the explicit allowlist
allow if {
    input.from == data.params.admin
}
```

This policy:

1. Defaults to **deny** (`allow := false`)
2. Allows transactions where the oracle reports no sanctions match
3. Always allows transactions from the configured admin address

## Using Intent Fields

The `input` object contains the Intent fields:

```rego
package spend_limit

default allow := false

# Allow if transfer value is under the configured limit
allow if {
    input.value <= data.params.max_value
}

# Block transfers to specific addresses
deny if {
    input.to == data.params.blocked_address
}

allow if {
    not deny
    input.chain_id == 11155111
}
```

## Using Oracle Data

The `data.wasm` path contains whatever your WASM oracle returned:

```rego
package price_check

default allow := false

# Only allow trades when the price is below the configured maximum
allow if {
    data.wasm.price < data.params.max_price
    data.wasm.symbol == "BTC"
}
```

:::note
A single oracle's fields appear directly under `data.wasm` (e.g. `data.wasm.price`). When a policy reads multiple oracles, each oracle namespaces its output under a pack id (`data.wasm.<pack-id>.price`) — see [Consuming Multiple Oracles](#consuming-multiple-oracles).
:::

## Consuming Multiple Oracles

A policy can read from more than one PolicyData oracle. When it does, each oracle namespaces its output under a unique **pack id**, and the network merges them into a single `data.wasm` object. Your Rego then reads `data.wasm.<pack-id>.*` and `data.params.<pack-id>.*`:

```rego
package vault_gate

import rego.v1

default allow := false

allow if count(deny) == 0

deny contains "risk floor" if {
    data.wasm.vaultsfyi.risk_score < data.params.vaultsfyi.risk_score_floor
}

deny contains "token collapsed" if {
    data.wasm.webacy.is_collapsed
}
```

See [Chaining Multiple Data Oracles](/developers/guides/chaining-data-oracles) for the full merge model and the deny-set pattern, and [Policy Packs](/developers/guides/policy-packs) for prebuilt oracles you can compose.

## Parameter Schema

Create `params_schema.json` to define which parameters contract owners can configure:

```json
{
  "type": "object",
  "description": "Sanctions check policy parameters",
  "properties": {
    "admin": {
      "type": "string",
      "description": "Admin address that bypasses sanctions check"
    },
    "max_value": {
      "type": "number",
      "description": "Maximum transfer value in wei"
    }
  }
}
```

Leave properties empty if your policy has no configurable parameters:

```json
{
  "type": "object",
  "description": "",
  "properties": {}
}
```

## Metadata Files

Create `policy_metadata.json`:

```json
{
  "name": "Sanctions Check Policy",
  "version": "0.0.1",
  "author": "Your Name",
  "link": "https://github.com/your-org/your-policy",
  "description": "Checks transaction counterparties against sanctions lists"
}
```

Create `policy_data_metadata.json`:

```json
{
  "name": "Sanctions Oracle",
  "version": "0.0.1",
  "author": "Your Name",
  "link": "",
  "description": "Fetches sanctions data from screening API"
}
```

## Directory Structure

Organize all files into a `policy-files/` directory:

```bash
policy-files/
├── policy.rego
├── policy.wasm
├── params_schema.json
├── policy_metadata.json
└── policy_data_metadata.json
```

```bash
cp policy.wasm policy.rego params_schema.json \
   policy_data_metadata.json policy_metadata.json policy-files/
```

## Testing Locally

Test your policy with the CLI before deploying:

```bash
newton-cli policy simulate \
  --wasm-file policy-files/policy.wasm \
  --rego-file policy-files/policy.rego \
  --intent-json intent.json \
  --entrypoint "sanctions_check.allow" \
  --policy-params-data policy_params.json
```

:::note
The `--entrypoint` value must match your Rego package name + rule name. For `package sanctions_check` with rule `allow`, use `sanctions_check.allow`.
:::

For a full reference on supported Rego syntax, see the [Rego Syntax Guide](/developers/advanced/rego-syntax-guide).

## Using Identity Data in Policies

Newton provides built-in functions for checking user identity data (KYC status, age, location) within your policies. Identity data is injected by operators from the on-chain IdentityRegistry — you only see boolean check results, never raw personal data.

```rego
package kyc_gated_transfer

default allow = false

allow if {
    newton.identity.kyc.check_approved()
    newton.identity.kyc.age_gte(18)
    newton.identity.kyc.address_in_countries(["US", "CA"])
    newton.identity.kyc.not_expired()
    input.value <= data.params.max_value
}
```

Identity built-ins are domain-namespaced (e.g., `newton.identity.kyc.*`). A generic `newton.identity.get("field_name")` accessor is also available for ad-hoc field access across any domain. See the [Rego Syntax Guide](/developers/advanced/rego-syntax-guide#newton-identity-extensions) for the full reference.

## Next Steps

<Card icon="rocket" to="/developers/guides/deploying-with-cli" title="Deploying with CLI">
  Deploy your policy to IPFS and register it on-chain
</Card>

<Card icon="shield" to="/developers/guides/smart-contract-integration" title="Smart Contract Integration">
  Integrate the policy into your smart contract
</Card>
