---
title: "Rules"
description: "The document you sign that decides which programs a sandbox may use, which instructions, how much it may spend and where funds may go. Rules v3 reference."
editUrl: true
head: []
template: "doc"
sidebar: {"hidden":false,"attrs":{}}
pagefind: true
draft: false
---

> For the complete documentation index optimized for AI agents, see [llms.txt](https://docs.arete.run/llms.txt) or [llms-full.txt](https://docs.arete.run/llms-full.txt). A markdown version of this page is available at [/sandboxes/rules.md](https://docs.arete.run/sandboxes/rules.md) or by sending `Accept: text/markdown`.
A sandbox's rules are one JSON document you sign. They decide which programs
the agent may use, which of their instructions, how much it may spend through
each, and where funds may go. The same programs define what the agent can
read; see [Read scope](/sandboxes/read-scope/).

## Build and sign

The rules builder on the sandbox's page in the
[dashboard](https://app.arete.run/dashboard/sandboxes) lists the supported
programs. Turn on the ones the agent needs, set options for each, add any
extra destinations and sign once with your wallet.

Change the rules the same way: edit, then sign again. An agent whose sandbox
has no signed rules is refused with `sandbox-rules-missing` or
`sandbox-rules-unsigned`. The CLI and the MCP server only show and check
rules; they never build or sign them.

## Rules v3

Version 3 is the only rules format.

```json
{
  "version": 3,
  "programs": [
    {
      "programId": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
      "name": "Pump.fun",
      "enabled": true,
      "instructions": ["buy", "sell"],
      "spendLimit": { "lamports": "1000000000", "windowSeconds": 86400 }
    }
  ],
  "transferAllowlist": ["<an address you control>"]
}
```

| Field               | Meaning                                                                                                                |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `version`           | `3`                                                                                                                    |
| `programs[]`        | The supported programs, each turned on or off with `enabled`                                                           |
| `programId`         | The program's base58 address                                                                                           |
| `name`              | The program's name, as the registry lists it                                                                           |
| `instructions`      | Optional. The instructions allowed for that program; left out, the registry's safe set for it                          |
| `spendLimit`        | Optional. The most SOL, in lamports (a decimal string), the sandbox may spend through that program per `windowSeconds` |
| `transferAllowlist` | Addresses funds may be sent to besides your own wallet (always allowed) and the sandbox's own accounts                 |

### Instructions

Leaving `instructions` out allows the registry's safe set for that program.
Listing instructions narrows the program to exactly those.

Every sandbox also has a small baseline of plumbing, such as creating an
associated token account, that it needs to hold tokens at all.
`sandbox_rules` lists the baseline with the programs you enabled.

### Spend limits

A spend limit caps the SOL that may leave the sandbox through one program in
each window of `windowSeconds`. In the example, the agent may spend at most 1 SOL per day
through Pump.fun. An action over the limit is refused with what remains in
the window, so the agent can retry with a smaller amount.

There are no sandbox-wide per-action or per-window caps; set a spend limit
on each program instead.

### Destinations

SOL and tokens may leave the enabled programs only to your wallet, an address
on `transferAllowlist`, or the sandbox's own accounts. A transfer anywhere
else is refused.

An address on the allowlist can receive any amount. Allow only addresses you
control.

## Exits and rent

Opening a position can create accounts that the sandbox's wallet pays rent
for. Some of that rent comes back when the agent exits the position. Some of
it never does.

| Program        | Account                                    | Rent                                   | Comes back?                                                                                                                                                        |
| -------------- | ------------------------------------------ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Kamino Lending | Obligation (`initObligation`)              | About 0.0176 SOL (17,637,760 lamports) | Yes. A withdraw that leaves the obligation with no deposits and no borrows closes it, and the rent goes back to the wallet. There is no separate close instruction |
| Kamino Lending | User metadata (`initUserMetadata`)         | About 0.0059 SOL (5,892,800 lamports)  | **No.** Kamino Lending has no instruction that closes user metadata                                                                                                |
| Pump.fun       | Volume tracker (`user_volume_accumulator`) | Set by the account's size              | Yes. `close_user_volume_accumulator` closes it and sends the rent back to the wallet                                                                               |

Rent is the network's rent-exempt minimum for the account's size. The
amounts above are for the obligation's 3,344 bytes and user metadata's 1,032
bytes and can change if the network's rent rate changes.

Treat Kamino's user-metadata rent as a one-time cost of using Kamino Lending
from the sandbox. A wallet has one user-metadata account, made by its first
deposit. Later deposits reuse it, including after the obligation has closed.
An agent should not plan to get that rent back.

### Allow the way out

Exiting a position takes the program's own exit instruction:

| Program        | Way in                                                                     | Way out                                                                                                                          |
| -------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Kamino Lending | Deposit (the first one also needs `initUserMetadata` and `initObligation`) | Withdraw: `withdrawObligationCollateralAndRedeemReserveCollateral` or `withdrawObligationCollateralAndRedeemReserveCollateralV2` |
| Pump.fun       | `buy` (the first one opens the volume tracker)                             | `sell` for the tokens, then `close_user_volume_accumulator` for the tracker's rent                                               |

The rules builder turns each program's way out on with its way in. If you list
a program's `instructions` yourself, list the way out as well. An instruction
left off the list is refused with `sandbox-instruction-not-allowlisted`, so the
agent cannot unwind the position or get its rent back until you sign new
rules.

## Validate before you sign

`a4 sandbox rules validate` checks a rules file locally. Nothing is sent
anywhere:

```bash
a4 sandbox rules validate rules.json
a4 sandbox rules validate - < rules.json   # from stdin
```

```text
rules.json: valid Rules v3 (enabled: Pump.fun)
```

Misspelt keys are errors (`spend_limit` would otherwise be signed and
ignored), as are program ids and addresses that are not base58 Solana
addresses and limits of zero. Passing locally does not guarantee the API
accepts every program: it must also be one the registry supports.

A document in an earlier format is refused with one line, by
`rules validate` and by `rules show` / `sandbox_rules`
(`sandbox-rules-version-unsupported`):

```text
rules.json: Rules version 2 is no longer supported. Build and sign v3 rules in the dashboard (Sandboxes → Rules).
```

## How the agent sees the rules

`sandbox_rules` (or `a4 sandbox rules show`) returns the rules as the agent
uses them: each allowed program, its instructions with their argument types
and accounts, the spend limits, the allowlist and the baseline. An account
with a `fill` (`fixed`, `wallet` or `derived`) can be left out of an action:
Arete fills it in.

That answer is the agent's view (`"view": "agent"`), not the document the
owner signed. Two numbers in it are easy to mix up:

- `rulesFormat`: the rules format, always `3`.
- `revision`: which signing of the sandbox's rules is active (`1` after the
  first signing, `2` after the owner changes them once, and so on).

The guard pipeline a preview, an action result, a `sandbox_action` lookup or
a history item names (`guardPipeline`, for example `execute-v1`; null or
absent on older actions and on actions that never reached the guard) is
neither: it is how Arete simulates and checks
the transaction. The view carries the signed document as `signedRules`, so
`a4 sandbox rules show --json | a4 sandbox rules validate -` checks the
signed rules. A view without `signedRules` is refused rather than checked as
a document. The only spend limits are the per-program `spendLimit`s: a
sandbox has no global caps.
