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

# Policy Gates

> The deterministic policy engine that decides what an automated actor may sign, before the MPC cosigner produces its share.

Policy gates are how WalletSuite decides what an automated actor is allowed to sign. Every signing intent passes through a deterministic Policy Decision Point before the MPC cosigner produces its share. It evaluates the request against your rules and returns one outcome: allow or deny.

<Info>
  Signing runs through [MPC threshold signing](/security/overview#trust-model) — no share, no signature. A policy deny means no signature is produced, so a denied intent cannot be signed by anyone. See [Agents and Authority](/core-concepts/agents-and-authority) for how an automated actor proposes a transaction and why it can never authorize its own.
</Info>

## How rules work

Allowlist rules are your own rules for what may be signed. They are **opt-in and deny-by-default**: with no active rule, any signing intent on a chain you're entitled to is admitted; once at least one rule is active, an intent is allowed only if it matches an active rule, and denied otherwise. An intent on a chain you're not entitled to is always denied.

You write rules against typed operations (below), not raw transaction bytes.

## Rule Anatomy

A rule matches on the fields below. A field left unset matches any value for that field. All set fields must match for the rule to admit the operation.

| Field             | Required         | What it matches                                                                                                  |
| ----------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- |
| `chain`           | yes              | The chain the intent targets                                                                                     |
| `operationType`   | yes              | One of the typed operations classified from the calldata                                                         |
| `contractAddress` | For contract ops | The token or contract the operation targets. Absent on native transfers, required on every other operation type  |
| `methodSelector`  | For contract ops | The 4-byte selector as `0x` plus 8 hex chars. Absent on native transfers, required on every other operation type |
| `destination`     | no               | Pins the decoded recipient, spender, or operator. When set, only that destination matches                        |
| `amountCap`       | no               | Per-transaction cap in base units (wei or smallest token unit). An intent above the cap does not match           |
| `expiresAt`       | yes              | Mandatory expiry. Authority is never open-ended. A rule stops matching once it expires                           |

Match fields are normalized to lowercase before storage so they line up with the operation typing. `amountCap` is always base units. It is never a USD or decimal value.

## Typed Operations

WalletSuite recognizes the following operations. Each rule targets exactly one.

| Operation type                | Method selector             | Family      |
| ----------------------------- | --------------------------- | ----------- |
| `native_transfer`             | none                        | EVM native  |
| `erc20_transfer`              | `0xa9059cbb`                | ERC-20      |
| `erc20_approve`               | `0x095ea7b3`                | ERC-20      |
| `erc20_transfer_from`         | `0x23b872dd`                | ERC-20      |
| `erc721_transfer_from`        | `0x23b872dd`                | ERC-721     |
| `erc721_safe_transfer_from`   | `0x42842e0e` / `0xb88d4fde` | ERC-721     |
| `erc721_approve`              | `0x095ea7b3`                | ERC-721     |
| `erc721_set_approval_for_all` | `0xa22cb465`                | ERC-721     |
| `tron_native_transfer`        | none                        | Tron native |
| `tron_trc20_transfer`         | `0xa9059cbb`                | Tron TRC-20 |

An operation outside this set cannot be typed and is denied once your allowlist is armed.

## A Rule

A rule that permits USDC transfers on Ethereum to a single payout address, capped per transaction, with a mandatory expiry:

```json theme={null}
{
  "chain": "ethereum",
  "operationType": "erc20_transfer",
  "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
  "methodSelector": "0xa9059cbb",
  "destination": "0x1111111111111111111111111111111111111111",
  "amountCap": "5000000000",
  "expiresAt": "2027-01-01T00:00:00Z"
}
```

The `amountCap` here is `5000000000` base units, which is 5000 USDC at 6 decimals. Any transfer to a different destination, above the cap, of a different token, or on a different chain does not match this rule. With this as the only active rule, everything else is denied.

For creating, listing, and deactivating rules through the admin API, see [Managing Policy Rules](/core-concepts/managing-policy-rules).

## Off, Shadow, Enforce

The engine has three modes. They are the adoption motion. You watch the engine decide beside your rules before you let it enforce them.

| Mode      | What it does                                                                                                                                        |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `off`     | Evaluation is disabled                                                                                                                              |
| `shadow`  | Every intent is evaluated and every would-deny is recorded, but signing still proceeds. You see what the engine would deny without denying anything |
| `enforce` | A deny is a deny. The intent is recorded as denied and no signature is produced                                                                     |

The path is `shadow` first, then `enforce`. Arm your rules, run in `shadow`, read the would-deny records, confirm the engine denies exactly what you expect, then switch to `enforce`. Nothing you did not intend to deny gets denied by surprise.

## What a Deny Does

When an intent is denied:

1. The intent is persisted as `denied` with the deny reason.
2. No signature is produced — the cosigner never releases its share for a denied intent.
3. The decision is recorded in the signing-intents ledger — see [Policy Decisions](/core-concepts/policy-decisions#what-every-decision-records) for the record fields.

The decision plane decides. The cosigner enforces. A deny is not a warning that a compliant caller might ignore — it is a signature that is never produced.

## Decision Provenance

Every evaluation, allow or deny, produces a fresh `policyDecisionId` and a stable `reason` — the seed of your execution evidence. See [Policy Decisions → Decision reasons](/core-concepts/policy-decisions#decision-reasons) for the reason strings and the full decision record.

## Administration

Rules are org-scoped and administered by an admin; every mutation is audited. See [Managing Policy Rules](/core-concepts/managing-policy-rules) for the request shapes and audit snapshots, and [Structured Errors](/core-concepts/structured-errors) for how a policy denial surfaces to a caller.
