Skip to content

Arete documentation

Govern steps with your Squads multisig

For the complete documentation index optimized for AI agents, see llms.txt or llms-full.txt. A markdown version of this page is available by appending .md to the URL or sending Accept: text/markdown.

For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /building-protocols/squads.md.

Some steps should never run on one person’s say-so: moving a treasury, withdrawing a raise, changing a fee. Give those steps the squadsProposer authority and they are proposed to your team’s Squads v4 multisig. Arete’s proposer only proposes. Your members approve and execute in Squads, as they would any other transaction.

Open app.arete.run/dashboard/squads. It works with any standard browser wallet (Phantom, Solflare, Backpack), including a Ledger connected through one of them, so members who sign with hardware wallets can do everything there. The page asks one question:

  • You already have a Squads multisig. Enter its address. The page shows its members and status, and gives two ways to add Arete’s proposer:
    • Here: connect a member wallet that can initiate, sign the add-proposer proposal, then approve and execute it (each member signs), here or in the Squads app.
    • In the Squads app: open the multisig, go to Members → Add member, paste the proposer, give it the Initiate permission only, then approve and execute the proposal. The page updates when the member appears.
  • You don’t have one. Create it on the page: choose a label, the members and the threshold, and connect the wallet that pays. Arete’s proposer is a member from the start, so there is nothing else to add.

Once the multisig is active, the page shows its address and the arete.toml snippet to put in your protocol’s deployment config. Attach a multisig once; every protocol you deploy can use it.

  • Your account has one proposer: a public key Arete signs proposals with. Every protocol you deploy uses the same proposer.
  • The proposer is a member of your multisig with the Initiate permission only. It can create proposals; it can never vote or execute, and it does not count toward the threshold.
  • Your account attaches each multisig it uses. An attachment is pending until the proposer is an Initiate-only member, active while it is, and archived if the proposer is removed or its permissions change (and active again once it is added back). The dashboard and a4 squads status show the state.
  • A production deployment of a protocol with Squads steps is refused unless the multisig in its deployment config is an active attachment of your account. A multisig that already has the proposer is attached automatically when you deploy.
  • The proposer pays the fee and rent of each proposal, so it needs a little SOL. The dashboard shows its balance.
  • When a reaction fires proposeSquads, Arete wraps the step’s instructions in a vault transaction and a proposal on your multisig. Your members approve and execute it. The protocol’s derive rules observe the result.

Declare the multisig as a resource and read it in the step’s authority:

const vault = 'pda("squads", "vault", resources.treasury.multisig, 0)';
export default defineProtocol({
// ...
programs: { system, squads },
resources: {
treasury: {
schema: {
type: "object",
properties: { multisig: { type: "string", format: "pubkey" } },
required: ["multisig"],
additionalProperties: false,
},
description: "The team's Squads v4 multisig.",
},
},
steps: {
pay: {
scope: "instance",
title: "Pay the recipient from the team vault",
authority: { kind: "squadsProposer", multisig: "resources.treasury.multisig" },
guards: { not_requested: 'instance.state == "requested"' },
transitions: [{ from: ["requested"], to: "proposed" }],
build: [
ix(system, "transfer", {
args: { lamports: "params.amount" },
accounts: { fundingAccount: vault, recipientAccount: "instance.roles.recipient" },
}),
],
},
},
reactions: [{
id: "proposePayout",
trigger: { kind: "onTransition", machine: "instance", from: [], to: ["requested"] },
action: { kind: "proposeSquads", step: "pay" },
}],
});

squads is the Squads v4 program (a4 install program squads-multisig-program --as squads --ts). Set the resource’s value for each deployment in arete.toml:

[protocol.resources]
treasury = "treasury.json" # { "multisig": "<your multisig address>" }

Put the address of a multisig attached on the dashboard there. The page shows the snippet for each active multisig.

The multisig must come from the deployment config: the step’s multisig reads a resource (resources.<name>) and nothing else. A protocol never creates a multisig, and a step that reads it from the instance or its arguments is refused when the protocol is checked. Because the multisig is fixed for the deployment, Arete checks it when the deployment is created and when it activates.

The step acts from vault 0 unless it sets vaultIndex. Use different vault indexes to keep protocols that share a multisig apart. The vault the step’s instructions use must match, so change the vault expression too:

const vault = 'pda("squads", "vault", resources.treasury.multisig, 1)';
// in the step
authority: { kind: "squadsProposer", multisig: "resources.treasury.multisig", vaultIndex: 1 },

If the multisig in the deployment config is not set up, a4 protocol deploy (deployment create or activation) fails with protocol-squads-setup-required. The error names the step, the multisig and why (not_member, permissions_invalid, …), and links to the multisig’s dashboard page, where the multisig is already waiting as pending:

Step 'finalize' proposes to Squads multisig <M>, which is not set up for your account (not_member). ...
Set it up: https://app.arete.run/dashboard/squads/<M>

Add the proposer there (or in the Squads app), then deploy again.

a4 squads reads your account’s setup and attaches a multisig by address. Each command prints the multisig’s dashboard link first and, while it is pending, the Squads-app steps to add the proposer. It builds no transactions.

Terminal window
a4 squads status # setup, proposer, attached multisigs
a4 squads status <M> # one multisig: members, deployments, proposals
a4 squads list
a4 squads attach <M> --label Treasury # attach an existing multisig

Agents use the MCP tools squads_status and squads_attach. They point the user to the dashboard or the Squads app and never sign for a member.

Arete builds no Squads transactions. To create a multisig with the proposer already a member, or to add the proposer to an existing one, from code, install the Squads v4 program SDK and use its Arete setup helpers:

Terminal window
a4 install program squads-v4

The member’s own wallet signs, as for any other transaction; Arete never asks for or holds a member key. Then read the multisig again (a4 squads status <M> or the dashboard) to see it turn active.

The proposer is per account, so once a multisig is attached and active, every protocol you deploy can point its resource at that multisig with no further setup. Proposals from all of them land in the same Squads queue. Give each protocol its own vaultIndex if their funds should stay apart.

Dev environments are separate: they have their own proposer, are never checked against your account’s attachments, and the dashboard does not apply. The Squads app cannot see a dev environment either, so a4 dev squads stands in for it with your dev wallets:

Terminal window
a4 protocol squads proposer --dev # the proposer <P>
a4 dev squads create --members alice,bob --threshold 1 --proposer <P> # proposer already a member
# or add it to an existing dev multisig:
a4 dev squads add-proposer --multisig <M> --proposer <P> --wallet alice # prints the index <n>
a4 dev squads approve --multisig <M> --index <n> --wallet alice
a4 dev squads execute --multisig <M> --index <n> --wallet alice
a4 dev fund <P> 1
a4 protocol squads status --dev --multisig <M>

When a step is proposed, approve it the same way and run it with a4 dev squads vault-execute --multisig <M> --index <n> --wallet alice.

If a step is proposed while the proposer is not a valid member (for example, someone removed it after the deployment activated), nothing is signed. The proposal waits and retries for up to 24 hours, and the instance’s event stream reports arete.squads.proposerMissing with the multisig, the proposer and the problem. The attachment turns archived and the dashboard lists the deployments affected. Add the proposer back (on the dashboard or in the Squads app) and it goes through on the next retry; after 24 hours it is dropped.