Arete documentation
Define a protocol
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.mdto the URL or sendingAccept: 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/definition.md.
Write protocol.ts to describe your workflow. Start with the
first protocol tutorial so you have installed
programs, configuration, and an environment in which to run it.
Use installed programs
Section titled “Use installed programs”Install the TypeScript SDK for each program your protocol uses:
a4 install program <package> --tsa4 install program ./idl/my_program.json --as my-program --tsThe installer maps @programs/* in tsconfig.json. Import the installed
module and register it under a stable alias in programs. The module carries
its program identity and instruction shapes; you do not need to copy hashes
or restate the IDL.
This is a complete minimal definition using the scaffold’s System Program:
import system from "@programs/system";import { defineProtocol, ix } from "@usearete/protocol";
export default defineProtocol({ name: "payment", version: "0.1.0", programs: { system }, params: { amount: "u64" }, instanceVisibility: "account", machines: { instance: { initial: "paid", states: { paid: { kind: "recorded", terminal: true } }, }, }, steps: { pay: { scope: "create", authority: "creator", args: { recipient: "pubkey", amount: "u64" }, guards: { amount_zero: "args.amount > 0" }, build: [ ix(system, "transfer", { args: { lamports: "args.amount" }, accounts: { fundingAccount: "wallet", recipientAccount: "args.recipient", }, }), ], }, },});args describes what a caller supplies to a step. params describes values
retained by an instance; create-step arguments with those names supply the
instance parameters. Use decimal strings for integer values at API boundaries.
Instruction templates
Section titled “Instruction templates”ix takes an installed program, its IDL instruction name, and expressions for
arguments and accounts. Expressions such as args.amount, wallet, and
instance.accounts.vault are resolved when preparing the step. The
expression reference lists every
function, operator and name, where each name is in scope, and every check
rejection with its fix.
Instruction, argument, and account names come from the installed IDL. TypeScript catches unknown names and missing required values. Derivable or optional accounts can be omitted when the generated instruction shape permits it. The build checks templates again, including plain JavaScript input.
A template can contain several instructions. For IDLs with trailing accounts,
add remainingAccounts in the required order:
remainingAccounts: [ { pubkey: "instance.accounts.quoteVault", isWritable: true }, { pubkey: "params.quoteMint" },],Remaining accounts cannot be signers. Use a handler when dynamic construction cannot be expressed with templates.
State-only steps
Section titled “State-only steps”A step that only moves the state machine, such as close, pause or reopen, has an empty template. Don’t add a placeholder instruction:
close: { scope: "instance", authority: { kind: "role", role: "operator" }, guards: { not_operator: "wallet == instance.roles.operator", not_open: 'instance.state == "open"', }, transitions: [{ machine: "instance", from: ["open"], to: "closed" }], build: [],},Its transaction carries only the attested memo, plus any fee or glue
instructions the step declares. The wallet signs and submits it like any other
step, and the transition applies when it confirms.
A squadsProposer step can’t be state-only: it proposes its instructions.
Bind accounts and roles
Section titled “Bind accounts and roles”The create step’s bind records identities that later steps use. The scaffold
keeps the creator and names a recipient role:
bind: { accounts: { creator: "wallet" }, roles: { recipient: "args.recipient" },},Later expressions use instance.accounts.creator and
instance.roles.recipient. Declare the role under roles as the scaffold does.
An ephemeral declaration names fresh accounts needed during construction;
protocol code does not return or store their private keys.
Include the data your app needs
Section titled “Include the data your app needs”The protocol’s generated stack provides instance and participant views and can
track the activity declared in stack.activity. The scaffold demonstrates
mapping its open and close transfers into activity with an instance account,
wallet account, and amount argument.
Choose those mappings to match the program’s real account relationships. A shared wallet is not an instance-owned account: the tip-jar scaffold tracks its Arete executions rather than treating every transfer to that wallet as protocol activity. Do not assume every direct program interaction can be attributed to an instance.
For additional joined or computed data, author custom live views and run them alongside your protocol.
Check and build
Section titled “Check and build”npx arete-protocol checknpx arete-protocol buildThe build writes the canonical spec, optional handler bundle, and generated
stack source to .arete/protocol/. Installed program identities are checked
against arete.lock; after changing a dependency, run a4 install or update
it, then rebuild. Do not edit the generated artifacts.
Continue with steps and lifecycle and automation and data.