Skip to content

Signing policies

A signing policy controls recurring transaction requests from agents. It does not grant an agent access to private keys or owner-only settings.

New accounts begin with an empty policy, so transactions ordinarily wait for owner review. Policy rules are evaluated from top to bottom, and the first matching rule decides each call:

  • Allow lets a matching transaction proceed without asking the owner again.
  • Deny rejects a matching transaction without an approval override.
  • No match sends the transaction to the owner for native review.

Every call in a batch must be allowed before the batch can proceed automatically.

Open Policies in Ekubo Wallet to use the guided editor. A rule can constrain the network, destination, native value, and calldata. Conditions in one rule are combined; leaving a field unrestricted broadens what the rule matches.

For contract calls, prefer a full function signature whose top-level parameters are all named, such as approve(address spender, uint256 amount), and constrain arguments by those names. Put narrow exceptions before broader rules because order is part of the policy’s authority. The wallet rejects rules that are provably unreachable behind an earlier rule.

Tuple arguments use a positional tuple predicate. Its array must have exactly the tuple’s ABI arity; a predicate constrains that position and null leaves only that position unrestricted. Tuple predicates can nest, and an array of tuples uses {"each":{"tuple":[...]}}. For example, {"tuple":[{"eq":"1"},null,{"eq":"true"}]} constrains the first and third values of a three-element tuple.

The editor below uses the same generated JSON Schema offered for download. It provides JSON syntax highlighting, schema-driven property and value completion, inline structural validation, and automatic formatting when focus leaves valid JSON. Choose an example to replace the editor contents with formatted JSON, then change it to explore the policy language. Examples containing addresses use obvious placeholders and are not ready to propose unchanged. Choose Review every transaction (Default) to restore the default behavior of asking the owner about every transaction.

Policy JSON editor

Press Ctrl+Space to see properties and values from the schema. Errors are underlined and listed in the diagnostics panel.

An empty policy leaves every transaction for owner review.

Loading the policy schema…

Download the Ekubo Wallet policy JSON Schema

View the complete JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Ekubo Wallet policy",
  "type": "object",
  "properties": {
    "$schema": { "type": ["string", "null"] },
    "version": { "type": "integer", "minimum": 1, "maximum": 1 },
    "rules": { "type": "array", "maxItems": 256, "items": { "$ref": "#/$defs/Rule" } }
  },
  "additionalProperties": false,
  "required": ["version", "rules"],
  "$defs": {
    "Rule": {
      "type": "object",
      "properties": {
        "effect": { "$ref": "#/$defs/Effect" },
        "label": { "type": ["string", "null"] },
        "chain_id": { "anyOf": [{ "$ref": "#/$defs/Predicate" }, { "type": "null" }] },
        "to": { "anyOf": [{ "$ref": "#/$defs/Predicate" }, { "type": "null" }] },
        "native_value": { "anyOf": [{ "$ref": "#/$defs/Predicate" }, { "type": "null" }] },
        "calldata": { "anyOf": [{ "$ref": "#/$defs/Predicate" }, { "type": "null" }] }
      },
      "additionalProperties": false,
      "required": ["effect"]
    },
    "Effect": {
      "oneOf": [
        { "type": "string", "const": "allow" },
        { "type": "string", "const": "deny" }
      ]
    },
    "Predicate": {
      "oneOf": [
        { "type": "string", "const": "any_value" },
        { "$ref": "#/$defs/Eq" },
        { "$ref": "#/$defs/In" },
        { "$ref": "#/$defs/Lt" },
        { "$ref": "#/$defs/Lte" },
        { "$ref": "#/$defs/Gt" },
        { "$ref": "#/$defs/Gte" },
        { "$ref": "#/$defs/Selector" },
        { "$ref": "#/$defs/Each" },
        { "$ref": "#/$defs/Tuple" },
        { "$ref": "#/$defs/Any" },
        { "$ref": "#/$defs/All" },
        { "$ref": "#/$defs/Not" },
        { "$ref": "#/$defs/Length" }
      ]
    },
    "Eq": { "type": "object", "properties": { "eq": { "type": "string" } }, "required": ["eq"], "additionalProperties": false },
    "In": { "type": "object", "properties": { "in": { "type": "array", "uniqueItems": true, "items": { "type": "string" } } }, "required": ["in"], "additionalProperties": false },
    "Lt": { "type": "object", "properties": { "lt": { "type": "string" } }, "required": ["lt"], "additionalProperties": false },
    "Lte": { "type": "object", "properties": { "lte": { "type": "string" } }, "required": ["lte"], "additionalProperties": false },
    "Gt": { "type": "object", "properties": { "gt": { "type": "string" } }, "required": ["gt"], "additionalProperties": false },
    "Gte": { "type": "object", "properties": { "gte": { "type": "string" } }, "required": ["gte"], "additionalProperties": false },
    "Selector": { "type": "object", "properties": { "selector": { "$ref": "#/$defs/SelectorPredicate" } }, "required": ["selector"], "additionalProperties": false },
    "SelectorPredicate": {
      "type": "object",
      "properties": {
        "abi": { "type": "string" },
        "args": { "type": "object", "additionalProperties": { "$ref": "#/$defs/Predicate" } }
      },
      "additionalProperties": false,
      "required": ["abi"]
    },
    "Each": { "type": "object", "properties": { "each": { "$ref": "#/$defs/Predicate" } }, "required": ["each"], "additionalProperties": false },
    "Tuple": { "type": "object", "properties": { "tuple": { "type": "array", "items": { "anyOf": [{ "$ref": "#/$defs/Predicate" }, { "type": "null" }] } } }, "required": ["tuple"], "additionalProperties": false },
    "Any": { "type": "object", "properties": { "any": { "type": "array", "items": { "$ref": "#/$defs/Predicate" } } }, "required": ["any"], "additionalProperties": false },
    "All": { "type": "object", "properties": { "all": { "type": "array", "items": { "$ref": "#/$defs/Predicate" } } }, "required": ["all"], "additionalProperties": false },
    "Not": { "type": "object", "properties": { "not": { "$ref": "#/$defs/Predicate" } }, "required": ["not"], "additionalProperties": false },
    "Length": { "type": "object", "properties": { "length": { "$ref": "#/$defs/Predicate" } }, "required": ["length"], "additionalProperties": false }
  },
  "description": "Ordered stateless per-call signing policy. The first matching rule decides each call: allow signs automatically, deny rejects without queuing, and reaching the end requires explicit owner approval. Omitted matchers mean anything and present matchers are ANDed. Native-value comparisons are per-call conditions, not cumulative spending budgets."
}

Schema validation checks document shape, required properties, allowed values, and predicate structure. The wallet remains the final authority: it also checks values in context, validates ABI signatures and predicate applicability, and rejects unreachable rules. A document that passes this browser editor is not installed and grants no permission.

The optional $schema property is only an editor hint. Ekubo Wallet does not fetch or trust content from that URL when it evaluates a policy.

An authorized agent can read the active policy and propose a complete replacement based on its current revision. A proposal does not change permissions. The owner must inspect the permission diff in the native wallet and authenticate before installation.

Do not broaden policy merely to finish the request currently waiting. A one-time action can use the ordinary native review path without changing future permissions.

Display labels, token symbols, RPC simulation output, and other network-provided facts are review context, not policy inputs. Policy matching uses exact transaction fields.