Introduction
The Policy Engine is your platform's source of truth for access control. Declare rules once; the engine enforces them everywhere — across transactions, key operations, and any action your platform exposes.
Capabilities
- Multi-chain support: Allow for setting rules to secure digital assets held across multiple chains using a single, unified policy language model.
- Offchain operations: Enforces policies for key export, key refresh, quorum change and other off-chain operations.
- Transfer Limits: Cap the amount of value that can be transferred in a single transaction.
- Fine-grained Access Control: Enables distributed authority across organizational boundaries where signature authority is split among multiple parties/levels.
- Specific Architecture: Designed for transactions signed by Multi-Party Computation (MPC) nodes.
- Key-Bound: Policies are bound to a specific Key ID.
- Stateless & Deterministic: Evaluation is based solely on the request payload and the policy itself.
- Auditable: Policy evaluation results are logged for every action.
Concepts
A Policy is a JSON document that defines the complete set of constraints for a key. Policy contains multiple Rule. A Rule might contain single Condition or multiple ones, called ConditionGroup.
- Rule: A composite statement about if the transaction/request satisfies all the condition groups or conditions to execute the action.
- Condition: A boolean statement that evaluates a specific attribute of the transaction (e.g., checking if
amountis less than 100). - ConditionGroup: A combination of boolean statements about the transaction/request.
Validation: Policies are stored on external storage. Their integrity is ensured by using a Message Authentication Code (MAC).
Policy Structure
A Policy object follows this structure:
| Field | Type | Description |
|---|---|---|
version | string | Must be "1.0". |
description | string | Optional description (max 512 chars). |
rules | Rule[] | List of rules to evaluate. |
Rule Structure
| Field | Type | Description |
|---|---|---|
description | string | Optional description (max 512 chars). |
issuer | Issuer[] | Entities authorized to issue the request that will be evaluated by the engine. |
action | string | "allow", "deny". The action to take if the conditions evaluate to true. |
logic | string | The logic operator to use while combining result of the conditions. Can be "or", or "and". Defaults to "and" if omitted. |
chain_type | string | "off", "ethereum", "solana". |
conditions | Condition[] | List of conditions or condition groups. |
Issuer
Defines who is making the request which is defined by the authentication payload in the request.
type: "UserId", "SessionKeyId", or "*" (any).id: Depending on thetypefrom above.- For
UserIdis the specific ID of the credential (for example: Passkey ID, or EOA address, or in case of no-auth architecture, the name of the registered Backend). - For
SessionKeyIdis the Ephemeral Key ID.
- For
Conditions
Conditions are the building blocks of rules. They compare transaction attributes against expected values. They are logic statements that evaluate to true or false.
Condition Fields
| Field | Type | Description |
|---|---|---|
transaction_type | string | The type of transaction (see below). |
transaction_attr | string | The specific attribute to check (see below). |
operator | string | Comparison operator. |
value | any | The static value to compare against. |
abi | object | (Optional) ABI/IDL for decoding data. |
Transaction Types
Used to deserialize the transaction payload correctly.
eip712: Typed Data signing.eip191: Personal Sign.erc20: Fungible Token interaction.erc721: NFT interaction.nativeTransfer: Pure ETH/SOL transfer.solanaTransaction: General Solana transaction.
Transaction Attributes
The specific field within the transaction to evaluate.
| Name | Description | Example |
|---|---|---|
sender | Transaction sender. EIP-1559 "from" field in erc20, erc721 and ETH transfer transactions | "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" |
receiver | Transaction destination. EIP-1559 "to" field in erc20, erc721 and ETH transfer transactions. Or, sender account key in SOL transfer transaction | "0x538f44e32925a3b844Bc454e4438f44e742d35Cc" |
nativeValue | Amount of native token (ETH/SOL). | "1000000000000000000" (1 ETH) |
chainId | Blockchain Network ID. | 1 |
functionSelector | Ethereum function signature (4 bytes). | "0xa9059cbb" |
message | EIP-191 message content. | "Sign this message" |
verifyingContract | EIP-712 contract address. | "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" |
primaryType | EIP-712 primary type. | "Mail" |
domainName | EIP-712 domain name. | "Ether Mail" |
splTransferAmount | Solana SPL token amount. | 1000000 |
splTokenMint | Solana SPL token mint address. | "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" |
solanaAccountKeys | List of accounts in a Solana transaction. | ["Account1...", "Account2..."] |
If an attribute is not found in the list above, the engine will try to resolve it as a parameter from the provided ABI.
To resolve naming collisions between built-in attributes and ABI parameters, you can use the abi: prefix (e.g. abi:sender).
This forces the engine to look up the attribute in the ABI parameters instead of using the built-in value.
Operators
The operands to evaluate Transaction Attributes against the Policy’s expected values.
eq: Equalneq: Not Equallt: Less Thanlte: Less Than or Equalgt: Greater Thangte: Greater Than or Equalin: Value is in a list.all: All values match (for arrays).
Policy evaluation flow
Policy
A Policy evaluation is based on the results of Rule evaluations:
- The Policy is evaluated rule-by-rule, in order as they are defined.
- Precedence: If any rule evaluates to
DENY, whole policy evaluates toDENY. - Default Behavior: If the request does not match to any of the rules in the policy, the whole policy evaluates to
DENY. - If there's no Policy attached to the key, policy evaluation will never happen - that results in ALLOW ALL behavior for given key.
Rule
A Rule defines an action (ALLOW or DENY) that is triggered when:
- The request Issuer and the Transaction Type matches the Policy configuration. If these 2 attributes aren't matched, the evaluation will be skipped for that Rule.
- Conditions (or Condition Groups) are satisfied.
Example
Giving following example of the Policy:
{
"version": "1.0",
"description": "Example policy",
"rules": [
{
"issuer": [{"type": "UserId", "id": "alice"}],
"description": "Rule 1",
"action": "allow",
"logic": "and",
"conditions": [
{
"logic": "and"
"group": [
{ ...Condition1... },
{ ...Condition2... }
]
},
{
"logic": "and"
"group": [
{ ...Condition3... },
{ ...Condition4... }
]
},
]
}, // End of Rule 1
{
"issuer": [{"type": "UserId", "id": "alice"}],
"description": "Rule 2",
"action": "allow",
"logic": "or",
"conditions": [
{
"logic": "and"
"group": [
{ ...Condition5... },
{ ...Condition6... }
]
},
{
"logic": "or"
"group": [
{ ...Condition7... },
{ ...Condition8... }
]
},
]
} // End or Rule 2
]
}
It consists two Rules.
Rule1has 2Groupsand logic operatorANDGroup1has 2Conditionsand logic operatorANDGroup2has 2Conditionsand logic operatorAND
Rule2has 2Groupsand logic operatorORGroup1has 2Conditionsand logic operatorANDGroup2has 2Conditionsand logic operatorOR
- The whole policy is for issuer
Alice
This example Policy can be seen as following logic statement:
( (C1 AND C2) AND (C3 AND C4) ) AND ( (C5 AND C6) OR (C7 OR C8) )
As a tree:
[AND] - At Rules level, all rules are evaluated using AND.
/ \ That means, all must return `Allow` for policy to pass.
/ \ If any rule returns `Deny`, then whole policy returns `Deny`.
/ \
[AND] [OR] - Logic operators on Rule level
/ \ / \
[AND] [AND] [AND] [OR] - Logic operators on Condition group level
/ \ / \ / \ / \
C1 C2 C3 C4 C5 C6 C7 C8 - Conditions
Limitations
- Stateless: No access to historical data (e.g., "Daily Limit" is not supported).
- No External State: Cannot reference external price feeds or databases.
- No Time Awareness: Cannot restrict based on time of day.