# The policy model

The vault has two distinct policy systems. Both share the same principles and the same workflow, but they govern different things.

## Administrative Policy

Administrative Policy answers: **who can perform which actions?**

It replaces role-based access control with a more flexible model. Instead of assigning named roles, you write rules that map groups of users to specific actions.

Every action in the vault is covered: creating users, importing keys, freezing keys, editing policies, exporting the audit log, and more. The full list of actions is in the [Administrative Policy guide](/guide/admin-policy/understanding).

Administrative Policy is **deny by default**. If no rule permits an action, nobody can perform it. The only pre-configured exception is `modify_admin_policy`, which is set to allow the initial Admins group during bootstrap. From there, the first admins configure everything else.

**Example:** To let the Risk team freeze keys in an emergency but keep unfreeze to a smaller senior group, you write two rules:

* Action `freeze_assets`: allow group "Risk Team".
* Action `unfreeze_assets`: allow group "Senior Admins" only.

## Signing Policy

Signing Policy answers: **how may a key be used to sign?**

Rules are evaluated top to bottom; the first matching rule wins. Each rule specifies:

* Which users (by group) are making the request.
* Which keys or wallets (by security group) are being signed with.
* Optional conditions that narrow when the rule matches.
* The result: **Approve**, **Deny**, or **External Rule Server** (delegate to your own decision service).

Security groups are named collections of keys or wallets. A group is homogeneous: its members are off-chain keys or a vault's wallets, never a mix. A key must belong to a security group to be signed through that group's Signing Policy.

Signing Policy is also deny by default. A signing request that matches no rule is blocked by the implicit final "deny all" rule.

**Rule conditions.** Conditions come in two disjoint families. A `raw_signing` condition marks a raw-signing rule: it covers off-chain key signing and wallet raw-signing in a vault's Signing Policy. The `network`, `asset`, and `destination` conditions together form an on-chain transfer rule, and an optional `usd_value` condition bounds the transfer's value in US dollars; asset entries can carry per-asset amount caps. The two families are disjoint: a transfer rule never approves a raw-sign, and a `raw_signing` rule never approves a transfer. Conditions do not change how rules are evaluated: evaluation stays ordered, the first match wins, and the default is deny.

:::warning\[Raw signing grants transaction-equivalent power]
In a vault's Signing Policy a `raw_signing` rule grants transaction-equivalent signing power (an EVM wallet raw-sign covers keccak256(message), which is exactly a transaction's signing hash; an Ed25519 wallet signs the bytes verbatim), so transfer conditions such as `destination` and `usd_value` do not bound what it can sign. Scope such rules tightly via user groups and security groups.
:::

For details on each rule type, including the External Rule Server, see the [Signing Policy guide](/guide/signing-policy/understanding).

## Shared principles

Both policy systems share the same foundational properties:

**Deny by default.** An empty policy blocks everything. You explicitly grant what is allowed.

**Passkey-signed edits.** Changing a policy requires a WebAuthn passkey assertion. This ensures that a stolen browser session cannot silently modify policy.

**One pending draft at a time.** You cannot have two simultaneous in-flight changes to the same policy type. This prevents conflicts and makes changes auditable.

**2-of-3 enforcement.** Policy decisions are evaluated independently by each of the three Policy Server nodes, and Key Stores verify the 2-of-3 signatures before signing. No single node can override policy.

## The draft lifecycle

Policy changes are staged in a draft, then submitted as a single atomic change:

![Policy draft lifecycle state diagram: No Draft, Draft, Active](/img/diagrams/policy-draft-lifecycle.svg)

```mmd
stateDiagram-v2
    [*] --> NoDraft : initial state
    NoDraft --> Draft : Create draft
    Draft --> Draft : Edit rules (passkey-signed)
    Draft --> Active : Submit (atomic activation)
    Active --> Draft : Create new draft (current policy stays active)

    note right of Draft
        Edits are staged in the draft;
        the active policy keeps applying.
    end note

    note right of Active
        New rules replace old ones
        in a single transaction.
    end note
```

*Editing happens in a draft; submitting activates it atomically, with the new rules replacing the old in one transaction.*

1. **Create.** Enter edit mode. The current active policy keeps applying while you work.
2. **Edit.** Add, remove, or reorder rules. Every edit requires a passkey assertion.
3. **Submit.** Sign and submit the draft. The new policy activates atomically, replacing the previous one in a single transaction. Optimistic locking (a `previous_version` field) rejects the submit if the active policy changed under you, so you re-open the draft and reapply.

## Where to configure policies

**Web app:** Administrative Policy lives at `/admin-policy`. Signing Policy lives at `/signing-policy`. Both show the current active rules and an "Edit Policy" button to enter draft mode.

**API:** Administrative Policy is at `PUT /v1/admin-policies`. Signing Policy is at `PUT /v1/signing-policies/{policy_id}`. Both use optimistic locking via a `previous_version` field to prevent concurrent overwrites. A vault's Signing Policy is referenced from the vault object itself: the `signing_policy` field names the policy that governs the vault's wallets.
