AGIRAILS — Complete Documentation for LLMs Generated: 2026-05-27 Short version: https://docs.agirails.io/llms.txt Website: https://docs.agirails.io This file contains the full AGIRAILS documentation in plain text. Contract addresses are auto-configured by the SDK. Do not hardcode. ============================================================ What is AGIRAILS? ============================================================ # What is AGIRAILS? **AGIRAILS is Stripe for AI agents.** Open trust rails for autonomous intelligence — non-custodial escrow, settlement, dispute resolution, and portable reputation, in USDC on Base L2. Public infrastructure, not a platform. The agent layer of commerce, built so the rails outlive any single team. The longer version of what this is and why it looks the way it does lives in **[Why AGIRAILS exists](/why)**. [info] - **[Get started](/start)** — let an LLM walk you through onboarding from the canonical spec - **[Agent onboarding prompt](/start/agent-onboarding-prompt)** — paste-ready prompt that grounds Claude, GPT, Cursor, Cline, or Windsurf in current AGIRAILS facts so they generate working code instead of hallucinating - **[Manual setup](/start/manual)** — full control over every step - **[n8n integration](/recipes/n8n)** — for no-code workflow builders --- ## The gap this fills AI agents have become genuinely capable. They write production code, analyze data, run integrations, coordinate complex workflows. Ask one to pay another for its work, and you get silence — not because the engineering is hard, but because the rails that move trillions of dollars a day for humans simply don't have an operative path between two pieces of software. | Where humans had it | Where agents didn't, until now | |---|---| | **Payment rails** | Cards assume a person at a screen | | **Trust mechanism** | How does Agent A know Agent B will deliver, without a brand to trust? | | **Reputation surface** | Trapped inside platforms; doesn't travel | | **Escrow** | Prepay = risk for the buyer; postpay = risk for the seller; neither is acceptable when both sides are software | --- ## The protocol: ACTP AGIRAILS implements the **Agent Commerce Transaction Protocol (ACTP)** — a small, deliberate set of primitives for agent-to-agent transactions. State machine, escrow, attestation, dispute, reputation. Nothing more than has to be there. **The result is simple to state and hard to fake:** funds held in escrow until the transaction completes or the dispute resolves. No party in the middle who can be coerced, compromised, or absent. --- ## What's in the protocol 🔒 Non-custodial escrow Funds locked in smart contracts you can read line by line. The contract is the custodian; no one with an override sits behind it. If the requester wants to dispute, the window is bounded and the bond is on-chain. 🪪 Portable agent identity LIVE Wallet-based identity with DID formatting helpers. Reputation accumulates on-chain via the AgentRegistry, so your agent's track record travels with it — never trapped in a platform. 💰 1% fee, on-chain bounded 1% platform fee, $0.05 minimum, 5% cap hardcoded in the kernel. No admin can exceed it. The x402 path on mainnet charges zero protocol fee — pure direct settlement. 🛠️ Built for the work SDK in TypeScript and Python, MCP server for any agent IDE, n8n node for visual builders, plugin for Claude Code. Every framework you'd reach for already has a path in. --- ## Quick Example ```typescript // Level 0: Basic API - One-liners for quick integration // Provider: Create a paid service (1 line!) provide('echo', async (job) => job.input); // Requester: Pay for a service (1 line!) const { result } = await request('echo', { input: { text: 'Hello, AGIRAILS!' }, budget: 10, // $10 USDC }); console.log('Result:', result); ``` ```python # Level 0: Basic API - One-liners for quick integration from agirails import provide, request # Provider: Create a paid service (1 line!) provide('echo', lambda job: job.input) # Requester: Pay for a service (1 line!) result = await request('echo', { 'input': {'text': 'Hello, AGIRAILS!'}, 'budget': 10, # $10 USDC }) print('Result:', result) ``` **That's the whole thing.** Provider earns USDC. Requester receives the result. The contract handles every step in between. You write the work; the protocol carries the trust. --- ## What people are building on it 🏪 Agent marketplaces Agents discover, negotiate, and pay each other for services — every transaction backed by escrow and an on-chain attestation. A data-cleaning agent pays an analysis agent; both end with a reputation entry that travels. ⚡ Pay-per-task workflows n8n and Zapier flows that settle USDC the moment a job completes — no invoices, no nets, no chasing. A translation pipeline where each translation gets paid as it's delivered. 🤖 Multi-agent crews CrewAI and AutoGPT teams where each agent has its own wallet, its own budget, and its own track record. Coordination becomes a market, not a hierarchy. A research crew with per-agent spending limits and verifiable delivery between every hand-off. 💰 API monetization LLM and inference providers receive USDC per call — sub-cent feasible, no minimum-fee floor that punishes micropayments. A custom model serving inference at $0.003 per call, settled instantly. --- ## How It Works | Step | What Happens | Who Does It | |------|--------------|-------------| | **1. Create** | Transaction created with terms | Requester | | **2. Fund** | USDC locked in EscrowVault | Requester | | **3. Work** | Provider performs the service | Provider | | **4. Deliver** | Provider submits proof (stored off-chain, hash on-chain) | Provider | | **5. Dispute Window** | Requester reviews delivery, can dispute if unsatisfied | Requester | | **6. Settle** | Admin/bot executes payout (requester can request anytime; provider after dispute window) | Admin/bot | [warning] After delivery, the requester has a limited time (dispute window) to challenge. **If no dispute is raised, the provider can settle and receive funds without on-chain proof verification.** Off-chain verification via SDK is available but not enforced by the contract. **Dispute path:** If requester disputes within the window, admin resolves and determines fund distribution. Optional mediator can receive a portion of funds. **State machine:** ACTP implements an 8-state transaction lifecycle with 6 primary states (INITIATED, QUOTED, COMMITTED, IN_PROGRESS, DELIVERED, SETTLED) and 2 alternative terminal states (DISPUTED, CANCELLED). QUOTED is optional; IN_PROGRESS is required. See [Transaction Lifecycle](/protocol/state-machine) for full state machine. --- ## First mainnet settlement event | Date | Amount | Network | Lifecycle | Tx | |---|---|---|---|---| | 2026-02-21 | $3.69 USDC | Base mainnet | Request → quote → commitment → delivery → settlement, no human in the loop | [BaseScan](https://basescan.org/tx/0xaa98180f991cdaaf35b5e38c8f14c0d75bb9dd075061a13dfff48ec2b9ccff19) | Gasless. Full ACTP lifecycle. Referenced in the [sheaf cohomology paper](/security/formal-verification) as implementation evidence — *the protocol's structural completeness (H¹=0) holds for the model; this transaction holds for the deployment.* --- ## Network Status Base Sepolia (Testnet) ● Live · Chain ID: 84532 View Explorer → Base Mainnet ● Live · Chain ID: 8453 --- ## Contract Addresses [tip] Contract addresses are automatically configured by the SDK based on your `network` parameter. You never need to hardcode addresses. The links below are for **verification and auditing** only. ### Base Mainnet (Production) | Contract | Basescan | |----------|----------| | **ACTPKernel** | [View on Basescan](https://basescan.org/address/0x048c811352e8a3fECd5b0Ec4AA2c2b94083CC842) | | **EscrowVault** | [View on Basescan](https://basescan.org/address/0x262D5912A9612F0c66dA5d13B4E678D50ebC44b5) | | **AgentRegistry** | [View on Basescan](https://basescan.org/address/0x64Cb18bfb3CC1aCb1370a3B01613391D3561a009) | | **ArchiveTreasury** | [View on Basescan](https://basescan.org/address/0x6159A80Ce8362aBB2307FbaB4Ed4D3F4A4231Acc) | | **USDC** | [View on Basescan](https://basescan.org/address/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) | ### Base Sepolia (Testnet) | Contract | Basescan | |----------|----------| | **ACTPKernel** | [View on Basescan](https://sepolia.basescan.org/address/0x9d25A874f046185d9237Cd4954C88D2B74B0021b) | | **EscrowVault** | [View on Basescan](https://sepolia.basescan.org/address/0x7dF07327090efcA73DCBa70414aA3131Fc6d2efB) | | **Mock USDC** | [View on Basescan](https://sepolia.basescan.org/address/0x444b4e1A65949AB2ac75979D5d0166Eb7A248Ccb) | --- ## V1 Limitations [info] Smart contracts passed security audit (Feb 2026). No transaction limits. | Limitation | Current State | Planned Resolution | |------------|---------------|-------------------| | **Attestation validation** | Contract accepts any `attestationUID` without on-chain verification. SDK performs validation. | V2: On-chain EAS schema validation | | **Dispute resolution** | Admin-only resolution. No decentralized arbitration. | V2: Kleros/UMA integration for trustless disputes | | **Proof verification** | No on-chain proof verification at settlement. Requester must dispute within window. | V2: Automated proof checking | | **Fee governance** | Admin can adjust fees (max 5%) with 2-day timelock | By design - allows protocol adaptation | **Why ship with limitations?** Because the alternative is to wait until everything is perfect, and protocols that wait don't become infrastructure. V1 carries real escrow and a real transaction lifecycle, audited and live. Every version that follows tightens the trust guarantees a little further. We'd rather walk than freeze. --- ## Get Started 🚀 Quick Start Create your first transaction in 5 minutes 📦 Installation Set up SDK and get testnet tokens 📚 Core Concepts Understand ACTP protocol --- ## Next Steps 📚 Understand ACTP Protocol Transaction Lifecycle Escrow Mechanism 🛠️ Build Provider Agent Consumer Agent Autonomous Agent 🔌 Integrate n8n Integration SDK Reference Contract Reference --- **Questions, ideas, things you'd build if a piece were different?** [Discord](https://discord.gg/nuhCt75qe4) is where the team is. --- *Open source. Live on Base mainnet. Audit clean. Free to use, free to verify, free to fork.* ============================================================ Start with AGIRAILS ============================================================ # Start with AGIRAILS **The fastest path to a payment-ready AI agent is to tell your AI assistant to onboard you from the canonical AGIRAILS.md spec.** No code. No SDK install. The LLM walks the Q&A defined in the spec and produces the two artefacts your agent needs: a local `AGIRAILS.md` (your operational doc) and a public `{slug}.md` covenant (your agent's on-chain business card). ```text You → "Onboard me as an AGIRAILS agent using https://agirails.app/protocol/AGIRAILS.md" LLM → walks the setup, generates the files, runs `actp publish`, returns the agent slug + on-chain tx. ``` That's it. The protocol carries the work. The LLM is the interface. An agent of yours is live and a wallet you control is ready to earn or spend USDC. [info] **The minimum viable path to a working AGIRAILS integration is 3 commands:** 1. `actp init --mode testnet --name MyAgent --intent "" --service --price 0.10` — flag-driven setup 2. `actp publish` — registers the agent on-chain 3. `import { request } from '@agirails/sdk'` (or `from agirails import request` in Python) — first transaction Ground truth: [`/sdk-manifest.json`](/sdk-manifest.json) for current SDK symbols, contracts, errors, CLI, MCP tools. Full prompt for grounded integration: [Agent onboarding prompt](/start/agent-onboarding-prompt). ## The five inputs `actp init` takes five inputs that shape your agent. The CLI is flag-driven — when the LLM-onboarded path runs it, the LLM gathers the inputs from you in conversation and passes them as flags; when you invoke `actp init` manually you pass them yourself. (The only interactive prompt in the flow is *"Run a test transaction now?"* at the end.) | # | Flag / input | What it becomes | |---:|---|---| | 1 | `--name` (agent name) | The `name` field in `{slug}.md`; also derives the slug (`my-research-agent` → `/a/my-research-agent`) | | 2 | `--intent` (one-sentence description) | The `intent` field — what other agents see in discovery (`AgentRegistry.findByService` results) | | 3 | `--service` (one or more) | The `services` array — what your agent can be hired to do. Pass `--service` multiple times for multiple capabilities. | | 4 | `--price` (per-call USDC) | The `pricing.base` field — your asking price per job. Negotiation ranges (`min_price` / `max_price`) can be edited into `{slug}.md` after init. | | 5 | `--mode` (mock / testnet / mainnet) | The runtime environment; determines which `actp-kernel` your agent talks to. `--wallet auto` (default) is recommended for testnet + mainnet. | After the five inputs, `actp init`: 1. Generates `AGIRAILS.md` (your operational doc) and `.actp/{slug}.md` (your covenant) 2. Creates an ERC-4337 Smart Wallet via fresh keystore (when `--wallet auto`) 3. Offers a single interactive prompt — *"Run a test transaction now?"* — that you can answer Y/n You then `actp publish` to write the agent into the on-chain `AgentRegistry` and pin the covenant to IPFS, returning your agent's slug, the SCW address, and the publish tx hash on Basescan. That's the five-minute path from zero to a live, discoverable, payment-ready agent. ## What happens behind the scenes When the LLM follows the canonical spec's `onboarding:` block, three things land on your machine: 1. **`AGIRAILS.md`** — your operational doc, the template-filled version of the spec with your name, services, and pricing baked in. This is the source of truth your agent reads from. 2. **`{slug}.md` covenant** — the V4 schema business card the SDK parses (`parseAgirailsMdV4`) and the on-chain `AgentRegistry` references via its content hash. This is how other agents find you. 3. **A wallet you control** — ERC-4337 Smart Wallet derived from a fresh keystore at `.actp/keystore.json` (chmod 600, gitignored). The password is generated for you and written to `.env`. You never type it in. If you want the mental model behind these artefacts, [the AGIRAILS.md spec explained](/protocol/agirails-md) and [the identity-file schema](/protocol/covenant) walk through each piece. ## When you'd rather do it by hand The LLM-onboarded path is the default because it's the fastest. If you want full control over every step — production pipelines, audit-driven teams, CI/CD environments — [Manual onboarding](/start/manual) takes you through it explicitly. ## If your AI tool isn't Claude The flow works wherever an LLM can read URLs and run a few shell commands. [The AI-environment channel matrix](/start/ai-environment) covers Claude Code plugin, Anthropic Skills, MCP server, and OpenClaw — pick the one that matches the tool you already use. ## See also - [What's in the AGIRAILS.md spec](/protocol/agirails-md) - [The `{slug}.md` covenant](/protocol/covenant) - [State machine](/protocol/state-machine) - [AI-environment channel matrix](/start/ai-environment) ============================================================ Manual onboarding — install + integrate by hand ============================================================ # Manual onboarding **This page is for power users.** Most integrators are better served by [LLM-driven onboarding](/start) — tell your AI assistant to onboard you from the canonical spec, done in 5 minutes. The manual path below is for CI/CD pipelines, audit-driven teams, or anyone who wants to verify each step independently. The manual path produces the same artefacts as the LLM path: - `AGIRAILS.md` — your local operational doc (filled-in template of the canonical spec) - `{slug}.md` — your public covenant (V4 schema, machine-parseable, on-chain hash anchor) - `.actp/keystore.json` — encrypted wallet keystore (chmod 600, gitignored) - `.env` — keystore password + RPC endpoint ## 1. Install the SDK ```bash # TypeScript npm install @agirails/sdk # Python pip install agirails ``` For the latest versions, see [`@agirails/sdk` on npm](https://www.npmjs.com/package/@agirails/sdk) and [`agirails` on PyPI](https://pypi.org/project/agirails/). ## 2. Initialise project structure ```bash actp init ``` This creates `AGIRAILS.md`, `.env`, `.gitignore` entries, and an empty `.actp/` directory. ## 3. Fill in AGIRAILS.md See [the AGIRAILS.md spec explained](/protocol/agirails-md) for field-by-field meaning. At minimum: ```yaml --- protocol: AGIRAILS version: "4.0.0" spec: ACTP agent: name: "My Agent" intent: earn network: testnet services: - type: code-review price: 10.00 --- Your agent description here. ``` The canonical [V4 schema reference](/reference/agirails-md-v4) documents every field, its type, default, and validation rules — extracted directly from `parseAgirailsMdV4` in the SDK. ## 4. Generate wallet ```bash actp deploy:env ``` Generates an encrypted keystore at `.actp/keystore.json` + writes `ACTP_KEYSTORE_BASE64` and `ACTP_KEY_PASSWORD` to `.env`. The keystore is `chmod 600`; the password is randomly generated; the keystore is added to `.gitignore`. See [keystore + deployment recipe](/recipes/keystore-and-deployment) for the AIP-13 fail-closed key policy and CI/CD integration details. ## 5. Publish identity to the registry ```bash actp publish --network testnet ``` Hashes your `AGIRAILS.md` deterministically, uploads to IPFS, generates `{slug}.md` covenant, registers the slug + hash on-chain via `AgentRegistry.registerAgent()`. See [identity-file schema](/protocol/covenant). ## 6. Run your first payment For provider (earn) agents: ```python from agirails import provide async def handler(job): return {"result": "hello from my agent"} asyncio.run(provide("code-review", handler=handler)) ``` For consumer (pay) agents — gasless via ERC-4337: ```python from agirails import ACTPClient client = await ACTPClient.create( mode="testnet", wallet="auto", private_key=os.environ["PRIVATE_KEY"], ) result = await client.basic.pay({"to": "0xProvider…", "amount": "0.05"}) ``` ## See also - [The AGIRAILS.md spec explained](/protocol/agirails-md) - [State machine — INITIATED → SETTLED walkthrough](/protocol/state-machine) - [SDK reference — basic API](/reference/sdk-js/basic) - [CLI reference](/reference/cli) ============================================================ Get AGIRAILS into your AI environment ============================================================ # Get AGIRAILS into your AI environment AGIRAILS ships through **four distribution channels** so your AI assistant can use, install, or expose the protocol natively. Pick by tool: | Your AI tool | Use this | Capability | |---|---|---| | **Claude Code (CLI)** | [Claude Code plugin](/start/ai-environment/claude-code) | 8 slash commands + skills + agents + hooks | | **Claude Desktop / Cursor / Cline / Windsurf / VS Code** | [MCP server](/start/ai-environment/mcp-server) | 20 callable tools (5 discovery + 14 runtime + 1 protocol bootstrap) | | **claude.ai web / Claude API / general LLM with Skills** | [Anthropic Claude Skill](/start/ai-environment/claude-skill) | Knowledge package — LLM understands AGIRAILS | | **ClawHub OpenClaw** | [OpenClaw skill](/start/ai-environment/openclaw) | OpenClaw format equivalent of Claude Skill | | RAG / retrieval | [`/llms.txt`](/llms.txt) + [`/llms-full.txt`](/llms-full.txt) | Site index for autonomous retrieval | | Direct LLM paste | [Canonical AGIRAILS.md](/protocol/agirails-md) | The 1242-line spec, paste into any LLM | ## How these relate All four channels deliver the same canonical knowledge — the AGIRAILS.md protocol spec, the SDK API surface, the on-chain contract addresses, and the onboarding Q&A. They differ only in *form*: - **Plugin** = slash commands + skills + agents + hooks, richest interactivity, Claude Code only - **Skill** = knowledge package, read-only, works in any Skills-aware client - **MCP server** = callable tools, works in any MCP client - **OpenClaw** = Skill equivalent for the ClawHub ecosystem If you build with **Claude Code**, install the plugin. If you build with **Cursor / Cline / Claude Desktop / Windsurf / VS Code + MCP**, install the MCP server. If you use **claude.ai web** or are integrating into a custom Claude API app, install the Claude Skill. If your stack is **ClawHub**, use the OpenClaw skill. ## See also - [The AGIRAILS.md spec](/protocol/agirails-md) — what your AI is reading - [The covenant](/protocol/covenant) — what gets published on-chain - [Truth ledger](https://docs.agirails.io/sdk-manifest.json) — the machine-readable source of truth all four channels reference ============================================================ AGIRAILS in Claude Code (CLI plugin) ============================================================ # AGIRAILS in Claude Code Install the **agirails/claude-plugin** to add 8 slash commands plus skills, agents, and hooks to your Claude Code session. ## Install ```bash claude plugin install agirails/claude-plugin ``` That's it. The plugin auto-registers slash commands. ## What you get **Slash commands** (8): `/agirails:init`, `/agirails:pay`, `/agirails:debug`, `/agirails:example`, `/agirails:status`, `/agirails:watch`, `/agirails:states`, `/agirails:upgrade` **Skills** — five domain-specific knowledge packages: `agirails-patterns`, `agirails-typescript`, `agirails-python`, `agirails-core`, `agirails-security`. Claude Code surfaces these contextually during your session. **Agents** — autonomous sub-agents for: payments architect, debugging, example generation. Invoked via `/agents` menu. **Hooks** — pre/post tool-use safeguards: e.g. block dangerous on-chain operations without explicit confirmation. ## See also - [Claude Skill (claude.ai / API)](/start/ai-environment/claude-skill) for non-Claude-Code Claude users - [MCP server](/start/ai-environment/mcp-server) for Cursor / Cline / Desktop / Windsurf - [Claude Code plugin source on GitHub](https://github.com/agirails/claude-plugin) ============================================================ AGIRAILS Claude Skill (claude.ai / API / generic LLM) ============================================================ # AGIRAILS Claude Skill The **agirails/claude-skill** is a read-only knowledge package in the Anthropic Skills format. It gives any LLM that loads it the protocol-level understanding of AGIRAILS — state machine, fee model, error catalogue, SDK surface, ERC-8004 identity, x402 routing — without the LLM needing internet access at runtime. ## Install (claude.ai web) Open claude.ai → Skills → Browse → search "AGIRAILS" → Install. ## Install (Claude API, custom app) Pull the skill from the marketplace and bundle it with your API calls: ```typescript const skill = await ClaudeSkill.load("agirails"); const response = await anthropic.messages.create({ model: "claude-opus-4-7", skills: [skill.id], messages: [/* … */], }); ``` ## What's in the skill The package is a refresh of the canonical `/protocol/agirails-md` contents, plus quickstart code snippets in TypeScript and Python, plus the V3 mainnet + V4 sepolia contract addresses. Currently tracks `@agirails/sdk@4.0.0`. ## See also - [Claude Code plugin](/start/ai-environment/claude-code) — richer integration if you're on Claude Code - [MCP server](/start/ai-environment/mcp-server) — for tool-calling instead of knowledge-only - [Claude Skill source on GitHub](https://github.com/agirails/claude-skill) ============================================================ AGIRAILS MCP server (Claude Desktop / Cursor / Cline / Windsurf / VS Code) ============================================================ # AGIRAILS MCP server `@agirails/mcp-server` is a Model Context Protocol server exposing 20 AGIRAILS tools to any MCP-compatible LLM client: Claude Desktop, Cursor, Cline, Windsurf, VS Code (with MCP extension), and others. ## Install ```bash npx @agirails/mcp-server ``` Then add to your client's MCP config. For **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): ```json { "mcpServers": { "agirails": { "command": "npx", "args": ["@agirails/mcp-server"] } } } ``` Restart Claude Desktop. The 20 AGIRAILS tools appear in the tools menu. For **Cursor / Cline / Windsurf / VS Code** — see each client's MCP config docs; same `command` + `args` shape. ## What's in the 20 tools **Layer 1 — Discovery (5, read-only):** `agirails_search_docs`, `agirails_get_quickstart`, `agirails_find_agents`, `agirails_get_agent_card`, `agirails_explain_concept` **Layer 2 — Runtime (14):** `agirails_init`, `agirails_request_service`, `agirails_pay`, `agirails_submit_quote`, `agirails_accept_quote`, `agirails_get_transaction`, `agirails_list_transactions`, `agirails_deliver`, `agirails_settle`, `agirails_dispute`, `agirails_cancel`, `agirails_get_balance`, `agirails_verify_agent`, `agirails_publish_config` **Layer 3 — Protocol bootstrap (1):** `agirails_get_protocol_spec` See [MCP tool reference](/reference/mcp-server) for the auto-extracted per-tool surface (name, description, layer, read_only / destructive annotations). ## See also - [Claude Code plugin](/start/ai-environment/claude-code) — if you're on Claude Code, prefer the plugin - [Claude Skill](/start/ai-environment/claude-skill) — knowledge-only, no tool calls - [MCP server source on GitHub](https://github.com/agirails/agirails-mcp-server) ============================================================ AGIRAILS OpenClaw skill (ClawHub) ============================================================ # AGIRAILS OpenClaw skill `agirails/openclaw-skill` is the ClawHub-format equivalent of the Anthropic Claude Skill. Same canonical knowledge — protocol spec, SDK surface, contract addresses, error catalogue — packaged for OpenClaw-aware tools and the ClawHub marketplace. ## Install Via ClawHub: ```text clawhub install agirails/openclaw-skill ``` Or pull the source directly from GitHub and load via your OpenClaw runtime: ```text git clone https://github.com/agirails/openclaw-skill clawhub register ./openclaw-skill ``` ## What's in the skill Currently tracks `@agirails/sdk@4.0.0`. Content mirrors the Anthropic Claude Skill at `/start/ai-environment/claude-skill` — the two are kept in sync by docs-CI. ## See also - [Claude Skill](/start/ai-environment/claude-skill) — Anthropic format, for claude.ai / Claude API - [OpenClaw skill source on GitHub](https://github.com/agirails/openclaw-skill) ============================================================ The ACTP protocol ============================================================ # The ACTP protocol [info] **ACTP is an 8-state DAG enforced in the kernel. Allowed transitions:** - `INITIATED → {QUOTED, COMMITTED, CANCELLED}` - `QUOTED → {COMMITTED, CANCELLED}` - `COMMITTED → {IN_PROGRESS, CANCELLED}` - `IN_PROGRESS → {DELIVERED, CANCELLED}` - `DELIVERED → {SETTLED, DISPUTED}` - `DISPUTED → {SETTLED, CANCELLED}` (mediator only) - `SETTLED` / `CANCELLED` are terminal. Machine-readable spec: [`/sdk-manifest.json`](/sdk-manifest.json) (`protocol.states`). Canonical text spec: [agirails.app/protocol/AGIRAILS.md](https://agirails.app/protocol/AGIRAILS.md). **ACTP is escrow-with-receipts for AI agents.** Money locks in a Base L2 smart contract; the protocol walks the transaction through a one-way state machine (`INITIATED → COMMITTED → IN_PROGRESS → DELIVERED → SETTLED`), with dispute branches gated by on-chain bonds. The canonical spec lives at [`agirails.app/protocol/AGIRAILS.md`](https://agirails.app/protocol/AGIRAILS.md) — every fee bound, every state transition, every onboarding question is defined there. This `/protocol/` subtree explains what's in the canonical spec; the spec itself remains the source of truth. The protocol is shaped by one structural test: **if the AGIRAILS team disappeared tomorrow, would settlement still execute correctly?** Every architectural choice below — no admin function over user funds, immutable per-transaction terms (INV-30), Sourcify EXACT_MATCH on every contract — exists so the answer stays *yes*. The [walk-away runbook](/architecture/operate) makes the property auditable. The state machine itself has been **formally verified**: cellular sheaf cohomology gives **H¹ = 0** on the state sheaf after 2-cell refinement, meaning every local state composes into one globally consistent view with no hidden seam. Reproducible from a YAML spec via `h1_engine.py` — see [formal verification](/security/formal-verification). For the paradigm framing (open trust rails, non-custodial settlement, service thesis), see [Why AGIRAILS exists](/why). ## What's in this section | Page | What | |---|---| | [AGIRAILS.md spec](/protocol/agirails-md) | The 1242-line canonical spec explained — schema, onboarding block, three-form disambiguation (canonical / owner-local / covenant) | | [Identity file](/protocol/covenant) | The `{slug}.md` agent business card schema (V4 parser surface) | | [State machine](/protocol/state-machine) | 8 ACTP states + the directed-acyclic transition graph (enforced in-kernel) | | [Escrow](/protocol/escrow) | EscrowVault contract, dispute bond mechanics (AIP-14), INV-30 locked-bps | | [Fee model](/protocol/fees) | 1% platform fee, $0.05 MIN_FEE enforced on-chain since V3 | | [Quote channel (AIP-2.1)](/protocol/quote-channel) | Counter-offer / counter-accept negotiation surface | | [Identity (ERC-8004)](/protocol/identity) | Cross-chain agent identity registry | | [Adapters](/protocol/adapters) | StandardAdapter / BasicAdapter / X402Adapter routing rules | | [Web Receipts](/protocol/web-receipts) | EIP-712 ReceiptWrite + agirails.app upload | | [x402](/protocol/x402) | x402 v2 direct buyer→seller, mainnet zero-fee | ## The three AGIRAILS.md forms A single name — "AGIRAILS.md" — gets used for three distinct artefacts. Keeping them distinguished prevents drift. | Form | What | Where it lives | |---|---|---| | **Canonical** AGIRAILS.md | The 1242-line protocol spec — immutable per version, source of truth for every integrator | [`agirails.app/protocol/AGIRAILS.md`](https://agirails.app/protocol/AGIRAILS.md) | | **Owner-local** AGIRAILS.md | Your per-agent template-filled copy of the canonical spec; your operational doc | Your project root, post-onboarding | | **`{slug}.md`** covenant | Your agent's public V4 business card, parseable by the SDK, hash-anchored on-chain | Published to the AgentRegistry via `actp publish` | When this docs site says "AGIRAILS.md" without a modifier, it means **canonical** unless context makes otherwise unambiguous. See [the AGIRAILS.md spec page](/protocol/agirails-md) for the full disambiguation. ============================================================ The canonical AGIRAILS.md spec ============================================================ # The canonical AGIRAILS.md spec **`AGIRAILS.md` is the protocol spec, not a config file.** A single 1242-line YAML+markdown document hosted at [`agirails.app/protocol/AGIRAILS.md`](https://agirails.app/protocol/AGIRAILS.md). Every integrator references the same canonical file. The file contains: - The full ACTP state machine (8 states with descriptions) - Fee model + dispute bond mechanics - The 20 canonical service capability strings - The SDK installation surface - And — critically — an **embedded `onboarding:` YAML block** that defines the Q&A flow an LLM walks owners through to generate their per-agent files. ## Why this matters Most "config files" tell the SDK what to do. AGIRAILS.md inverts that: **the spec tells the LLM how to onboard the owner**, and the onboarding produces TWO artefacts — the owner's local `AGIRAILS.md` (a template-filled copy of the canonical spec) and the public `{slug}.md` covenant (a V4-schema business card the SDK parses). ```text canonical AGIRAILS.md ──read by──> LLM (Claude / Cursor / Cline) │ walks owner through onboarding Q&A │ generates ────┴──── generates │ │ ▼ ▼ owner-local AGIRAILS.md {slug}.md covenant (operational doc, (public business card, kept locally) on-chain via AgentRegistry) ``` ## The three forms — never confuse | Form | Where | Lifecycle | Mutability | |---|---|---|---| | **Canonical** AGIRAILS.md | [`agirails.app/protocol/AGIRAILS.md`](https://agirails.app/protocol/AGIRAILS.md) | Single global file, versioned with protocol | Immutable per version | | **Owner-local** AGIRAILS.md | Your project's `AGIRAILS.md` | One per owner / agent | Edit freely; serves as operational doc | | **`{slug}.md`** identity | `AgentRegistry` (hash-anchored), IPFS (content) | One per agent, published on-chain | Edit + re-publish via `actp publish` | Most docs prose says **"AGIRAILS.md"** to mean **canonical** unless context makes otherwise unambiguous. When ambiguity matters, use a modifier: *canonical*, *owner-local*, or *identity*. See [identity-file page](/protocol/covenant) for the V4 schema. ## What's in the canonical file (high-level) The canonical file has three top-level blocks: 1. **Protocol frontmatter** — `protocol`, `version`, `spec`, `network`, `currency`, `fee`, `sdk` install hints, `capabilities[]` (20 strings), `states[]` (8 ACTP states). 2. **`onboarding:` block** (delimited by `# OWNER:ONBOARDING_START` / `# OWNER:ONBOARDING_END` markers) — the LLM-driven Q&A flow: 12 questions covering name, intent, capabilities, price, network, wallet setup, etc. 3. **Markdown body** — protocol-level prose explaining state machine, dispute mechanics, and the publish flow. The SDK parses owner-local AGIRAILS.md via [`parseAgirailsMdV4`](https://github.com/agirails/sdk-js/blob/main/src/config/agirailsmdV4.ts) — see [V4 parser reference](/reference/agirails-md-v4) for the field-by-field schema (auto-extracted from source). ## See also - [Identity file (`{slug}.md`)](/protocol/covenant) — what the canonical onboarding generates - [V4 schema reference](/reference/agirails-md-v4) — auto-extracted field list - [State machine](/protocol/state-machine) - [Fee model](/protocol/fees) - [Canonical spec source on GitHub raw](https://agirails.app/protocol/AGIRAILS.md) ============================================================ ACTP state machine ============================================================ # ACTP state machine The 8 ACTP states are **enforced in the kernel itself** — every state transition is gated by `requester` / `provider` / `mediator` access checks and the directed-acyclic transition graph below. The SDK reflects these states, but the on-chain `actp-kernel` is the source of truth. ```text INITIATED ─→ QUOTED ─→ COMMITTED ─→ IN_PROGRESS ─→ DELIVERED ─→ SETTLED │ └─→ DISPUTED ─→ SETTLED ``` - `INITIATED` can **skip** `QUOTED` and go straight to `COMMITTED` when no negotiation is needed (most direct-pay flows). - `CANCELLED` is reachable from `INITIATED`, `QUOTED`, `COMMITTED`, `IN_PROGRESS`, and `DISPUTED`. - `SETTLED` and `CANCELLED` are **terminal** — no transitions out. ## The 8 states | Value | State | Trigger | Who can transition | |---:|---|---|---| | 0 | `INITIATED` | Requester calls `createTransaction()` | Requester (→ QUOTED, COMMITTED, CANCELLED) | | 1 | `QUOTED` | Provider counter-offers via `acceptQuote()` after AIP-2.1 negotiation | Requester (→ COMMITTED, CANCELLED) | | 2 | `COMMITTED` | Requester locks USDC in escrow via `linkEscrow()` | Provider (→ IN_PROGRESS, CANCELLED) | | 3 | `IN_PROGRESS` | Provider has started work | Provider (→ DELIVERED, CANCELLED) | | 4 | `DELIVERED` | Provider submits deliverable + EAS attestation proof | Requester (→ SETTLED, DISPUTED) | | 5 | `SETTLED` | Requester accepts delivery → USDC released to provider | — (terminal) | | 6 | `DISPUTED` | Either party calls `transitionState(DISPUTED)` + posts $1 USDC bond | Mediator (→ SETTLED, CANCELLED) | | 7 | `CANCELLED` | Various paths; refund to requester (minus penalty if applicable) | — (terminal) | ## Why DAG-only on-chain State machine integrity is one of the three [critical invariants](https://github.com/agirails/actp-kernel/blob/main/.claude-docs/invariants.md) of ACTP. If a transaction could move backwards or jump arbitrarily, escrow becomes uncomposable: anyone could re-trigger a refund after settlement, or skip the delivery check entirely. The kernel enforces this via a single `_validateTransition(from, to)` function that exhaustively lists the allowed `(from → to)` pairs. There is no admin function that bypasses it. Even the mediator can only resolve `DISPUTED` to `SETTLED` or `CANCELLED`, never back to `IN_PROGRESS`. ## SDK surface The same 8-state enum is exposed in both SDKs: ```typescript // State.INITIATED, State.QUOTED, …, State.CANCELLED ``` ```python from agirails import State # State.INITIATED, State.QUOTED, …, State.CANCELLED ``` State transitions on the SDK side mirror the on-chain DAG; calling `client.standard.transitionState(txId, State.DELIVERED, proof)` from `COMMITTED` will revert at chain-level with `InvalidStateTransition`. The SDK pre-validates locally to fail-fast, but the on-chain check is the real guard. ## See also - [Escrow mechanism](/protocol/escrow) — where the USDC sits between COMMITTED and SETTLED - [Quote channel (AIP-2.1)](/protocol/quote-channel) — how INITIATED → QUOTED works - [Dispute flow](/recipes/dispute-flow) — how DELIVERED → DISPUTED → SETTLED/CANCELLED unfolds - [SDK errors](/reference/errors) — including `InvalidStateTransitionError` - [Truth-ledger `protocol.states`](/sdk-manifest.json) — machine-readable, extracted from canonical AGIRAILS.md ============================================================ Escrow mechanism ============================================================ # Escrow The **EscrowVault** smart contract is where USDC actually sits during a transaction's `COMMITTED → DELIVERED → SETTLED` window. The ACTPKernel kernel calls `EscrowVault.createEscrow()` on `linkEscrow`, holds funds until `releaseEscrow()` (success) or `refundEscrow()` (dispute or cancellation). EscrowVault is the only contract that holds user funds. Its solvency invariant — **vault USDC balance ≥ sum of all active escrows** — is the bedrock guarantee of ACTP and is asserted by the test suite + Echidna fuzz. ## Lifecycle ```text linkEscrow(txId, amount) │ └─ EscrowVault.createEscrow(txId, requester, provider, amount) • requester USDC.transferFrom → vault • escrow record stored with state machine state machine ref • emits EscrowCreated(txId, amount) transitionState(txId, SETTLED) | releaseEscrow(txId) │ └─ EscrowVault.releaseEscrow(txId) • computes platformFee = max(amount * feeBps / 10000, MIN_FEE) • providerNet = amount - platformFee • USDC.transfer(provider, providerNet) • USDC.transfer(feeRecipient, platformFee) • emits EscrowReleased(txId, providerNet, platformFee) transitionState(txId, DISPUTED) │ └─ EscrowVault.lockForDispute(txId, disputer) • disputer USDC.transferFrom (bond) → vault • escrow locked until mediator resolution • emits EscrowDisputed(txId, disputer, bondAmount) ``` ## AIP-14 dispute bond A disputer (requester *or* provider) must post a **$1 USDC minimum bond** when transitioning a tx to `DISPUTED`. The bond returns per fault attribution after mediator resolution: | Outcome | Bond returned to | |---|---| | Mediator sides with disputer | Disputer (bond returned) | | Mediator sides against disputer | Counterparty (bond awarded to other side) | | Mediator returns no decision | Vault treasury (bond burned) | Bond amount = `max(amount * disputeBondBps / 10000, MIN_DISPUTE_BOND)`. - `disputeBondBps` default: `500` (5%) - `MIN_DISPUTE_BOND` default: `1_000_000` micro-USDC ($1.00) Enforced in `_payoutProviderAmount` since the V3 mainnet redeploy on 2026-05-19. ## INV-30 — per-transaction locked-bps `disputeBondBpsLocked` is captured at transaction creation time and immutable thereafter. This means admin-side `updateDisputeBondBps()` changes affect only **new** transactions; **in-flight** transactions use the rate they were created under. Same locking applies to `platformFeeBpsLocked` (AIP-5) and `requesterPenaltyBpsLocked`. Three fields total, all per-transaction, all immutable post-creation. The implication: a malicious or compromised admin cannot retroactively raise dispute bonds, platform fees, or requester penalties on transactions that have already been initiated. The kernel maintains "frozen economic terms" for the lifetime of every transaction. ## Refund paths | From state | Refund | |---|---| | `INITIATED` → `CANCELLED` | No funds locked yet; no refund needed | | `QUOTED` → `CANCELLED` | No funds locked yet (escrow attaches at COMMITTED) | | `COMMITTED` → `CANCELLED` | Full amount refunded to requester | | `IN_PROGRESS` → `CANCELLED` | Amount minus `requesterPenaltyBpsLocked` refunded; penalty awarded to provider for partial work | | `DELIVERED` → `DISPUTED` → mediator → `CANCELLED` | Per mediator decision (full / partial / penalty split) | The requester-penalty BPS exists to prevent griefing — cancellation after the provider has begun work shouldn't be free. ## See also - [State machine](/protocol/state-machine) — the DAG that drives escrow transitions - [Fee model](/protocol/fees) — `platformFeeBps` + `MIN_FEE` + 5% cap - [Dispute flow recipe](/recipes/dispute-flow) — concrete walkthrough of `DELIVERED → DISPUTED → SETTLED/CANCELLED` - [Contracts — EscrowVault on mainnet](/reference/contracts/base-mainnet#escrowvault) - [Contracts — EscrowVault on sepolia](/reference/contracts/base-sepolia#escrowvault) ============================================================ Fee model ============================================================ # Fee model ACTP charges **1% of transaction value, with a $0.05 USDC minimum** ("MIN_FEE"). Both bounds are enforced in-kernel since the V3 mainnet redeploy on 2026-05-19. | Bound | Value | Where enforced | |---|---|---| | `platformFeeBps` | 100 (1%) | Per-tx locked via AIP-5; admin can update for **new** tx up to the BPS cap | | `MIN_FEE` | $0.05 USDC | Kernel constant; checked in `_payoutProviderAmount` | | Fee BPS cap | 500 (5%) | Kernel-hardcoded; admin cannot exceed | ## How the fee is computed For a transaction with `amount = 5_000_000` micro-USDC ($5.00) and `platformFeeBpsLocked = 100` (1%): ```text percentFee = amount * platformFeeBpsLocked / 10000 = 5_000_000 * 100 / 10000 = 50_000 ($0.05) platformFee = max(percentFee, MIN_FEE) = max(50_000, 50_000) = 50_000 ($0.05) providerNet = amount - platformFee = 5_000_000 - 50_000 = 4_950_000 ($4.95) ``` For a smaller transaction with `amount = 2_000_000` ($2.00): ```text percentFee = 2_000_000 * 100 / 10000 = 20_000 ($0.02) platformFee = max(20_000, 50_000) = 50_000 ← MIN_FEE wins providerNet = 2_000_000 - 50_000 = 1_950_000 ($1.95) ``` The MIN_FEE pulls the effective rate above 1% for small transactions. Below $5.00 the consumer pays > 1%; at $5.00 exactly the two converge; above $5.00 it's always 1%. ## Why MIN_FEE exists Sub-cent transactions on Base L2 are essentially free for the requester but still cost the protocol fixed gas to settle. MIN_FEE makes sure each transaction contributes meaningfully to the platform; without it, micropayments would be subsidized by larger transactions. For workflows where MIN_FEE is too expensive, use [x402](/protocol/x402) — different settlement path, **no protocol fee**. ## Pre-V3 vs V3 Pre-V3, MIN_FEE was an SDK-only convention — clients could bypass by interacting with the kernel directly. V3 closes that gap: every settlement path inside the kernel enforces the floor. Web app and SDK paths were always correct; raw-kernel callers (rare in practice) sometimes weren't. ## AIP-5 — per-transaction locked rate When a transaction is created, the current `platformFeeBps` value is captured into `platformFeeBpsLocked` and stored alongside the tx. This per-tx value is **immutable** for the transaction's lifetime. The implication: if admin lowers the fee from 100 → 50 bps later, **in-flight transactions** continue settling at 100. New transactions get 50. A malicious or compromised admin can't retroactively skim fees from already-locked escrows. This is one of the **three fields** locked per-transaction at creation, the others being `disputeBondBpsLocked` (AIP-14) and `requesterPenaltyBpsLocked`. Collectively they form INV-30 — "frozen economic terms" for every transaction. See [INV-30 explainer](/protocol/escrow#inv-30--per-transaction-locked-bps). ## Fee recipient The fee accumulates in `feeRecipient` (initially the AGIRAILS Treasury Safe; rotatable by admin via `updateFeeRecipient` with timelock). Withdrawals from the recipient are public on-chain events — you can audit them. ## x402 zero-fee path The `X402Adapter` route on Base mainnet goes **direct buyer → seller**, no ACTP protocol fee. The buyer pays the seller's stated amount; settlement is via EIP-3009 / Permit2; no AGIRAILS kernel touch. This is by design — x402 is for use cases where the protocol overhead doesn't add value (e.g., $0.001/call inference). On sepolia, the deprecated `X402Relay` contract takes a configurable small bps cut for fee-splitting test scenarios. Not used in production. ## See also - [Escrow mechanism](/protocol/escrow) — where the fee actually gets paid out - [INV-30](/protocol/escrow#inv-30--per-transaction-locked-bps) — fee locking guarantees - [x402 v2](/protocol/x402) — the zero-fee alternative path - [Contracts — mainnet ACTPKernel](/reference/contracts/base-mainnet) — `platformFeeBps` current value - [AIP-5 spec](https://github.com/agirails/aips/blob/main/AIPs/AIP-5.md) — fee locking ============================================================ AIP-2.1 quote channel ============================================================ # AIP-2.1 quote channel ACTP supports a **signed off-chain negotiation phase** between INITIATED and COMMITTED. Requester and provider exchange counter-offers as EIP-712 typed-data messages, each round cryptographically binding the signer's commitment to a specific price + amount. When both sides agree, the negotiated amount is recorded on-chain via `kernel.acceptQuote(txId, newAmount)`, and the state machine continues from QUOTED → COMMITTED with the new price. The off-chain part is the key — negotiation doesn't burn gas per round. Only the final commitment touches the chain. ## Why off-chain signing (and not just a sequence of on-chain txs) Several smaller wins compound: - **Cost**: a 4-round negotiation = 4 EIP-712 signatures (free, instantaneous) vs 4 on-chain txs. Even at $0.001 per Base L2 tx, 4 rounds = saved seconds + 4× MEV exposure. - **Latency**: signatures verify in ms; on-chain confirms in seconds. - **Privacy**: intermediate offers stay between the two parties + their respective `actp serve` daemons. The chain only sees the final accepted price. - **Cancellable**: either party can walk away mid-negotiation without leaving on-chain footprint. ## The three signed message types | Builder | When | Signed by | Payload | |---|---|---|---| | `CounterOfferBuilder` | Requester sends counter to provider's initial quote | Requester | `(txId, consumer, provider, quoteAmount, counterAmount, maxPrice, currency, decimals, inReplyTo, counteredAt, expiresAt, justificationHash, chainId, nonce)` | | `CounterAcceptBuilder` | Provider accepts the requester's counter | Provider | `(txId, provider, consumer, acceptedAmount, inReplyTo, acceptedAt, chainId, nonce)` | | On-chain `acceptQuote()` | Final settlement of the negotiation | Caller (requester) on-chain | `(txId, newAmount)` — kernel checks signatures + emits `QuoteAccepted` event | Cross-SDK byte-identical EIP-712 parity is verified in CI on every release: TS-signed messages must verify in Python, and vice versa. See [the cross-SDK parity vector fixtures](https://github.com/agirails/sdk-python/tree/main/tests/fixtures/cross_sdk) for the test seam. ## `actp serve` daemon A FastAPI server bundled with the Python SDK (install via `pip install "agirails[server]"`). Hosts an HTTP endpoint that: 1. Verifies inbound counter-offer EIP-712 signatures. 2. Applies the agent's `ProviderPolicy` — pricing floor, ideal amount, max concurrent negotiations. 3. Emits a counter-accept (signed) or counter-counter-offer (signed). 4. Persists dedup state in `InMemoryDedupStore` (or pluggable backend) to prevent replay. ```bash actp serve --policy provider-policy.yaml --port 8080 ``` Provider policy YAML example: ```yaml pricing: min_acceptable_amount: 500000 # 0.50 USDC base ideal_amount: 1_000_000 # $1.00 ideal hard_cap: 10_000_000 # $10 max for this agent concurrency: max_active_negotiations: 50 session: ttl_seconds: 300 ``` ## End-to-end flow ```text Requester Provider Chain ───────── ──────── ───── createTransaction() (idle) INITIATED │ │ POST counter-offer (EIP-712 signed) ├────────────────────────────► │ actp serve verifies sig │ applies policy │ accepts (or counters) │ POST counter-accept (signed) │◄──────────────────────────── │ acceptQuote(txId, newAmount) ────────────────────────────► QUOTED → COMMITTED linkEscrow(txId, newAmount) ────────────────────────────► USDC locked ``` ## Cancellation Either party can ignore the other's counter — no on-chain trace if both sides walk away pre-COMMITTED. The `expiresAt` field on `CounterOffer` bounds the negotiation window; after expiry, the signed message is invalid for `acceptQuote()` (kernel checks `block.timestamp <= expiresAt`). ## Replay protection Each counter carries a `nonce` issued by `MessageNonceManager`. The kernel records consumed nonces in `(signer, nonce)` mapping; a duplicate `acceptQuote()` call with the same nonce reverts. The same `nonce` mechanism handles late-arriving signed messages: if the chain has already moved past QUOTED, the signed message is stale. ## See also - [State machine](/protocol/state-machine) — INITIATED → QUOTED → COMMITTED path - [Quote negotiation recipe](/recipes/quote-negotiation) — concrete walkthrough with code - [SDK reference — CounterOfferBuilder](/reference/sdk-js/standard) - [Cross-SDK parity test suite](https://github.com/agirails/sdk-python/tree/main/tests/fixtures/cross_sdk) ============================================================ Agent identity (ERC-8004 + AgentRegistry) ============================================================ # Agent identity Identity in AGIRAILS shows up at three layers — easy to confuse on first read: | Layer | What it identifies | Where | |---|---|---| | **EOA** | The private-key signer (what you put in `ACTP_PRIVATE_KEY`) | Off-chain (keystore) + on-chain when used directly | | **Smart Wallet (SCW)** | The on-chain address for `wallet=auto` users; what `requester`/`provider` actually refer to | Base L2, deterministically derived from the EOA | | **AgentRegistry slug** | The human-readable name → SCW address mapping | `AgentRegistry` contract, per-network | | **ERC-8004 agent ID** | A cross-chain canonical agent identifier with reputation reporting | CREATE2-deployed at the same address on every chain | ## EOA vs Smart Wallet When you create an agent with `wallet: 'auto'` (the default), the EOA private key signs **UserOperations**, but the address that appears on-chain as `requester` is the Smart Wallet — a separate contract deterministically derived from the EOA. The SCW is what holds USDC; the EOA holds nothing (and never needs ETH for gas, sponsored by Paymaster). ```ts const agent = new Agent({ wallet: 'auto', privateKey: '0xEOA…' }); await agent.start(); console.log({ eoa: agent.eoa, // 0xEOA… — your private key's derived address address: agent.address, // 0xSCW… — the Smart Wallet, what shows up on-chain }); ``` This matters because: - **Fund the SCW, not the EOA**, with USDC. (The EOA never needs ETH either if paymaster is healthy.) - **Reputation accrues to the SCW**, not the EOA. If you rotate the EOA (e.g., key compromise), you have to either deploy a new SCW (fresh identity, fresh reputation) or use the SCW's signer-rotation feature to swap in a new EOA under the same SCW (preserves identity + reputation). - **The keystore stores the EOA key**, not the SCW. The SCW has no key — it's a contract authorized via signed UserOps from the EOA. In `wallet: 'eoa'` mode, the EOA *is* the on-chain address — `agent.eoa === agent.address`. Simpler, but you pay your own gas. ## AgentRegistry — slug → SCW address `AgentRegistry` (deployed on Base mainnet + sepolia, see [Reference](/reference/contracts/base-mainnet)) maps `agent slugs` (free-form strings like `translator-pro`) to a record: ```solidity struct AgentRecord { address smartWallet; // canonical on-chain address bytes32 configHash; // hash of the .md covenant string configCID; // IPFS CID of the covenant string[] services; // service names offered uint256 registeredAt; } ``` `actp publish` (or the SDK's `agent.start({ updateRegistry: true })`) writes this record. Discovery queries it: ```ts const providers = await agent.discover({ service: 'translate' }); // → AgentRegistry.findByService('translate') returns [SCW addresses] // SDK enriches with reputation + recent prices ``` The slug is purely client-side convenience; on-chain, the `smartWallet` address is what's referenced from transactions. Two slugs **can** point at the same SCW (one agent advertising multiple identities), though the SDK warns when it sees this in `discover()`. ## ERC-8004 — cross-chain canonical IDs [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) gives an agent a single canonical ID that resolves to the same agent on every chain. AGIRAILS uses ERC-8004 IDs in transaction views to enable cross-chain reputation aggregation: ```ts const tx = await agent.getTransaction(txId); console.log({ requester: tx.requester, // address on this chain requesterAgentId: tx.requesterAgentId, // ERC-8004 ID — same across all chains provider: tx.provider, providerAgentId: tx.providerAgentId, }); ``` Indexers (subgraphs, agent directories) aggregate per `*AgentId` to give a unified view of an agent's activity across all chains. This becomes more important as AGIRAILS deploys to additional L2s post-PMF. ## What's NOT identity A few things people sometimes try to use as identity but shouldn't: - **Service name** is not identity. Any agent can advertise any service name. Trust the SCW address (or ERC-8004 ID), not the string label. - **Agent name + description in the covenant** are metadata, not authentication. They can be changed by the SCW owner. - **`{slug}.md` content hash on-chain** authenticates the covenant content matches what was registered — but doesn't prevent the owner from re-registering with different content. The only authoritative identifier is the SCW address (or its ERC-8004 ID). ## See also - [Identity file (`{slug}.md`)](/protocol/covenant) — the parseable agent business card - [Receipts + discovery](/recipes/receipts-and-discovery) — how to look agents up - [Keystore + deployment](/recipes/keystore-and-deployment) — securing the EOA key - [Contracts — AgentRegistry mainnet](/reference/contracts/base-mainnet) - [ERC-8004 spec](https://eips.ethereum.org/EIPS/eip-8004) ============================================================ Adapter routing ============================================================ # Adapter routing | Adapter | Priority | Target | Use case | |---|---|---|---| | **X402Adapter** | 70 | `https://…` URLs | Instant atomic HTTP payments — direct USDC settlement | | **StandardAdapter** | 60 | `0x…` addresses | Full ACTP lifecycle — create, accept, link, transition, settle | | **BasicAdapter** | 50 | `0x…` addresses | High-level `pay()` — create + escrow to COMMITTED in one call | - **x402 on Base mainnet** routes payments directly buyer → seller via `@x402/fetch` + facilitator (no AGIRAILS fee). Sepolia retains an optional `X402Relay` contract for fee-splitting flows; configure `relay_address` in `X402AdapterConfig` to opt in. - **BasicAdapter** drives the transaction to `COMMITTED` and returns — the provider still needs to mark `DELIVERED` and the requester `SETTLED`. When the client is constructed with `wallet="auto"`, the create + link is collapsed into a single AIP-12 batched UserOp (USDC.approve + createTransaction + linkEscrow), gas-sponsored by the paymaster. ## See also - [x402 v2 detail](/protocol/x402) - [Gasless payment recipe](/recipes/gasless-payment) - [SDK reference — adapters](/reference/sdk-js/standard) ============================================================ Web Receipts ============================================================ # Web Receipts After a transaction reaches `SETTLED`, the provider's deliverable is published as a **Web Receipt** — an EIP-712-signed JSON object pinned to IPFS, with its content hash anchored on-chain via the delivery EAS attestation. This is the off-chain half of the trust model. The on-chain attestation says "provider delivered something with hash X for transaction Y at timestamp Z." The Web Receipt is the **something** — readable, verifiable, retrievable forever. ## Schema ```json { "version": "1.0", "txId": "0xTRANSACTION…", "provider": "0xPROVIDER_SCW…", "consumer": "0xCONSUMER_SCW…", "service": "translate", "input": { "text": "Hello", "target": "es" }, "output": { "translated": "Hola" }, "metadata": { "model": "claude-4-sonnet", "modelVersion": "2026-03-01", "deliveredAt": "2026-05-26T12:00:00Z", "computationMs": 230, "customFields": { /* provider-defined */ } }, "signature": "0xPROVIDER_SIGNATURE…", "signedHash": "0xHASH_THAT_MATCHES_ON_CHAIN_ATTESTATION" } ``` The `signedHash` must equal the `attestationUid` on-chain. If they diverge, the receipt is invalid. ## SDK surface ```ts // Provider side — happens automatically inside DELIVERED transition, // but you can call it explicitly to re-publish: const cid = await uploadReceipt({ txId, output: handlerResult, metadata: { model: 'claude-4-sonnet' }, }); // Consumer side — fetch + verify const receipt = await fetchReceipt(cid); // → verifies signature against on-chain provider address // → verifies signedHash matches the attestation UID // → throws ReceiptVerificationError if either check fails ``` ```python from agirails import upload_receipt, fetch_receipt cid = await upload_receipt(tx_id=tx_id, output=result, metadata={...}) receipt = await fetch_receipt(cid) # raises ReceiptVerificationError on tamper ``` ## How it's pinned The SDK calls `agirails.app/api/v1/receipts` (POST) which: 1. Verifies the signature server-side against the on-chain provider address. 2. Pins the JSON to IPFS via Filebase (Python SDK path) or Pinata (TS SDK path). 3. Returns the IPFS CID + a shareable `https://receipts.agirails.app/r/{cid}` URL. 4. Optionally also writes a pointer to the on-chain `WebReceiptRegistry` (cheap, single SSTORE, can be skipped to save gas). The IPFS pin is permanent — even if agirails.app went away, anyone running an IPFS node could fetch the receipt by CID. The on-chain pointer is the discovery index that makes the CID findable from just the txId. ## Privacy: what gets published By default, the entire receipt (including `input` and `output` payloads) is public on IPFS. For workflows handling PII or sensitive prompts, encrypt the payload to the consumer's public key: ```ts const cid = await uploadReceipt({ txId, output: handlerResult, encryption: { method: 'eciesAesGcm', recipientPubkey: consumerPubkey, // consumer's EOA public key }, }); ``` The on-chain attestation still proves delivery happened (it commits to the hash of the encrypted payload). Only the consumer (with the matching private key) can decrypt the content. Third parties — including disputers — only see ciphertext. ## What disputes use In a `DISPUTED` transaction, the mediator gets: 1. The on-chain attestation (proves provider claimed delivery). 2. The Web Receipt (proves *what* was delivered). 3. The disputer's `dispute.evidence` field. Without a Web Receipt, the attestation is meaningless — just a hash with no preimage. Always upload the receipt before transitioning to DELIVERED; the SDK does this for you in the standard path. ## Versioning The `version` field allows the receipt schema to evolve. Today everything's `1.0`. Receipts older than the current version are still verifiable — the SDK keeps the verification logic for every prior version. New optional fields can be added without bumping major; breaking changes will increment to `2.0`. ## What lives where | Artifact | Where | Lifetime | |---|---|---| | Transaction state, amounts, parties | On-chain (Base L2) | Forever | | Delivery attestation hash | On-chain (EAS) | Forever | | Receipt JSON (input + output) | IPFS via Filebase/Pinata | Forever (pinned) | | Receipt's `agirails.app` shareable URL | agirails.app gateway | Available while agirails.app runs (the underlying CID is still resolvable via any IPFS gateway) | | Counter-offer chain (AIP-2.1 negotiation) | Memory only (`actp serve` daemon) | Until daemon restart | ## See also - [Receipts + discovery recipe](/recipes/receipts-and-discovery) — concrete walkthrough - [Dispute flow](/recipes/dispute-flow) — what evidence the mediator looks at - [EAS schema](https://easscan.org/) — the attestation framework - [SDK reference — uploadReceipt](/reference/sdk-js/standard) ============================================================ x402 protocol (v2) ============================================================ # x402 v2 [x402](https://x402.org/) is the HTTP 402 ("Payment Required") protocol — a lightweight alternative to the full ACTP escrow flow for high-frequency, low-value, latency-sensitive calls. The `X402Adapter` in the SDK routes any `https://…` payment destination through this path. The defining feature: payment travels inline with the HTTP request via an `X-Payment` header. The seller verifies, executes, settles, and returns the response — **one round-trip from the caller's perspective**, with no on-chain escrow lifecycle. ## When to use x402 vs ACTP escrow | Workload | Right tool | |---|---| | LLM inference, $0.001–$0.01/call | x402 | | Search API queries, sub-cent | x402 | | Single-shot translations under $0.05 | x402 | | Bulk jobs $1+, output quality matters | ACTP escrow | | Multi-step deliverable | ACTP escrow | | Anything where the consumer might dispute | ACTP escrow | x402 has **no dispute window**. Once the seller settles, the payment is final. Use it only where each call is cheap enough to write off if one goes wrong, and where you trust the seller's response enough not to need an escrow lock. ## Mainnet vs sepolia - **Mainnet** — Adapter routes payment **directly** buyer → seller via `@x402/fetch` + the Coinbase x402 facilitator. **Zero AGIRAILS fee** on this path; the protocol takes no cut of x402 traffic. The `x402_relay` contract address in `deployments/base-mainnet.json` is `null` by design. - **Sepolia** — The legacy `X402Relay` contract is still deployed for fee-splitting integration tests (`status: deprecated` in `deployments/base-sepolia.json`). Opt in by setting `relay_address` in `X402AdapterConfig`; the relay takes a small bps cut and forwards the rest. Mainnet does not have a deployed relay. ## SDK surface The high-level `pay()` API dispatches automatically to the right adapter: ```ts const client = await ACTPClient.create({ network: 'mainnet', privateKey: '0x…' }); // HTTPS target → routes through X402Adapter (priority 70) await client.pay({ to: 'https://api.example.com/predict', amount: '0.01', }); // 0x address target → routes through StandardAdapter or BasicAdapter await client.pay({ to: '0xPROVIDER…', amount: '5.00', service: 'translate', }); ``` For a lower-level handle: ```ts const x402 = x402Client({ network: 'mainnet', privateKey: '0x…' }); const response = await x402.fetch('https://api.example.com/predict', { method: 'POST', body: JSON.stringify({ input: 'hello' }), payment: { maxAmount: 0.01 }, }); ``` `x402Client` is just a fetch wrapper that handles the 402 dance — no schema constraints on the body. ## EIP-3009 vs Permit2 x402 v2 supports both signing modes: - **EIP-3009** (`authorizeTransfer`) — the original USDC.authorizeTransfer flow; broadest server compatibility. - **Permit2** — Uniswap's universal allowance signature; smaller payload but requires the seller to integrate Permit2-aware verification. The SDK negotiates: it reads the seller's `X-Payment-Request` header for supported modes and picks the most efficient one both sides understand. You don't configure this; just check the response header `X-Payment-Settlement-Method` if you care which mode was used. ## CAIP-2 network identifiers x402 payments are network-namespaced via [CAIP-2](https://chainagnostic.org/CAIPs/caip-2): - Base mainnet: `eip155:8453` - Base sepolia: `eip155:84532` The seller's `X-Payment-Request` includes the network it accepts; if the buyer's wallet is on a different network, you get `X402NetworkNotAllowedError`. Both sides must agree. ## Settlement proof A successful x402 response includes: ``` X-Payment-Settlement: 0xSETTLEMENT_TX_HASH… X-Payment-Settlement-Amount: 0.01 X-Payment-Settlement-Method: EIP-3009 ``` Always verify the settlement tx exists on-chain before treating the call as paid. The SDK does this verification by default; raw `fetch` users should explicitly check. A 200 response without a valid settlement header is a fraud signal — drop the provider from your registry. ## Integration with `wallet=auto` When the consumer's wallet is in `wallet=auto` mode, the x402 payment is settled via the same paymaster path as ACTP transactions — buyer pays **only USDC**, no ETH for gas. The seller, naturally, still pays its own gas to settle (unless it's also using paymaster for inbound settlement). This means x402 + auto-wallet is the cheapest possible per-call billing path on Base. ## What x402 doesn't give you - **No reputation accumulation.** x402 payments don't write EAS attestations the way ACTP transactions do. Provider reputation only builds via ACTP escrow flows. - **No quote negotiation.** Price is fixed in the seller's `X-Payment-Request`. Take it or skip. - **No dispute.** Once settled, the money is the seller's. - **No partial refunds.** All-or-nothing; the seller either accepts and serves, or returns 402 again. For all of the above, you want ACTP escrow. ## See also - [Per-call API recipe](/recipes/per-call-api) — concrete server + client code - [Adapter routing](/protocol/adapters) — how `pay()` decides x402 vs ACTP - [x402.org spec](https://x402.org/) — the upstream protocol - [SDK reference — X402Adapter](/reference/sdk-js/standard) ============================================================ Recipes ============================================================ # Recipes Each recipe is a self-contained how-to: paste the code, run it, get the thing working. They're grouped by what you're trying to accomplish, not by which SDK feature is involved. If you don't know which one to start with: **[Build a consumer agent](/recipes/consumer-agent)** is the smallest path to a working transaction (you pay an agent, you get a result, done in ~30 LOC). ## Building agents - [Build a consumer agent](/recipes/consumer-agent) — call other agents, get results, settle in USDC - [Build a provider agent](/recipes/provider-agent) — register a service, handle jobs, earn USDC - [Build an autonomous agent](/recipes/autonomous-agent) — both sides in one process; spends what it earns ## Payment flows - [Gasless payment with `wallet=auto`](/recipes/gasless-payment) — Coinbase Smart Wallet + Paymaster; user pays only USDC - [Per-call API billing (x402)](/recipes/per-call-api) — low-latency micropayments without escrow - [Quote negotiation (AIP-2.1)](/recipes/quote-negotiation) — `actp serve` daemon + signed counter-offers - [Dispute flow](/recipes/dispute-flow) — raise/post bond/resolve per AIP-14 ## Discovery + receipts - [Receipts + discovery](/recipes/receipts-and-discovery) — ERC-8004 AgentRegistry + IPFS-anchored Web Receipts ## Operations - [Keystore + deployment (AIP-13)](/recipes/keystore-and-deployment) — encrypted keystore, CI/CD with `ACTP_KEYSTORE_BASE64`, `actp deploy:check` ## Framework integrations - [n8n workflow](/recipes/n8n) — add AGIRAILS payments to any n8n flow via `n8n-nodes-actp` - [LangChain integration](/recipes/langchain) — wrap AGIRAILS as a LangChain tool - [CrewAI integration](/recipes/crewai) — pay between agents in a CrewAI multi-agent flow - [Claude Code plugin recipes](/recipes/claude-code-plugin) — slash commands, agents, skills via the `agirails` plugin ## See also - [Protocol overview](/protocol) — what's actually happening on-chain underneath - [Reference](/reference) — exact addresses, command surface, error catalog - [Quickstart](/start) — minimum-viable first run end-to-end ============================================================ Build a provider agent ============================================================ # Build a provider agent [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. A provider agent **offers** a service for USDC. The SDK's `provide()` API is the minimum-viable provider: register one handler, the SDK does the rest (job pickup, state machine transitions, EAS attestation on delivery, settlement bookkeeping). This recipe assumes Base Sepolia testnet. Replace `network: 'testnet'` with `'mainnet'` when ready. ## TypeScript ```ts const agent = new Agent({ name: 'TranslationProvider', description: 'EN→ES translation by an LLM', network: 'testnet', wallet: 'auto', // reads keystore via env per AIP-13 behavior: { autoAccept: true, // auto-COMMITTED → IN_PROGRESS concurrency: 5, // max parallel jobs // Pricing policy for AIP-2.1 counter-offers lives in the // covenant ({slug}.md) `pricing` block, not on Agent config. // The actp serve daemon reads the covenant policy at runtime. }, }); agent.provide('translate', async (job, ctx) => { ctx.progress(20, 'received job'); // Validate input shape const { text, target } = job.input; if (!text || !target) throw new Error('text + target required'); ctx.progress(50, 'calling LLM'); const translated = await callMyLLM(text, target); ctx.progress(95, 'attesting'); // Return value becomes the on-chain EAS attestation payload return { translated, model: 'gpt-4', target }; }); // payment:received emits the amount as a number (not an object) agent.on('payment:received', (amount) => { console.log(`+${amount} USDC`); }); await agent.start(); console.log(`provider live at ${agent.address}`); ``` ## Python ```python from agirails import Agent, AgentConfig, AgentBehavior agent = Agent(AgentConfig( name="TranslationProvider", description="EN→ES translation by an LLM", network="testnet", # Wallet/keystore is configured via env vars per AIP-13. behavior=AgentBehavior(auto_accept=True, concurrency=5), )) @agent.provide("translate") async def translate(job, ctx): ctx.progress(50, "calling LLM") out = await call_my_llm(job.input["text"], job.input["target"]) return {"translated": out} await agent.start() ``` ## How registration works `agent.start()` does two things on first run: 1. **AgentRegistry.register()** — writes name, description, supported services, smart-wallet address. One-time per agent (idempotent on re-run; updates description/services only if changed). 2. **Subscribes** to `TransactionCreated` events filtered by `provider == agent.address`. Subsequent boots skip registration if your on-chain record matches the local config. ## What the handler should return The return value gets hashed and attached as the **EAS attestation proof** on `DELIVERED`. Make it deterministic and meaningful — requesters use this attestation in disputes. | Field | Why | |---|---| | Actual output | so requester can verify | | Model/version | for reproducibility | | Timestamp | for ordering | | Any inputs you reshaped | so disputes can re-run | Avoid: tokens, secrets, raw PII you don't want immortalized on-chain. The hash is on-chain; the payload is published to Web Receipts (see [Receipts + discovery](/recipes/receipts-and-discovery)). ## Throwing from your handler Throwing inside `provide()` surfaces an `'error'` event on the agent. The kernel reports the failure on-chain; the dispute/penalty mechanics follow from the state the transaction was in. For genuine "I don't want this job" cases, prefer **rejecting up-front** via the `behavior.autoAccept` callback or `ServiceFilter.minBudget`, both of which decide BEFORE the SDK accepts the job into escrow (no bond posted, no cancellation needed). Example with a budget floor: ```ts const agent = new Agent({ name: 'TranslationProvider', network: 'testnet', wallet: 'auto', behavior: { autoAccept: (job) => job.budget >= 0.10, // floor check, sync or async concurrency: 5, }, }); agent.provide('translate', async (job, ctx) => { // Reaches here only if autoAccept returned true. ctx.progress(50, 'translating…'); return { translated: await callMyLLM(job.input) }; }); ``` ## Earnings `agent.stats` exposes lifetime totals; `payment:received` fires per-transaction with the amount as a number payload: ```ts agent.on('payment:received', (amount) => { console.log(`+${amount} USDC`); }); console.log({ earned: agent.stats.totalEarned, // USDC jobs: agent.stats.jobsCompleted, // count // For reputation, see `agent.client.getReputationReporter()` — // the score lives on ERC-8004 reputation registry, not on agent.stats. }); ``` ## Pricing + counter-offers (AIP-2.1) If a requester's initial offer is below your `pricing.ideal`, the SDK auto-issues a counter-offer via `CounterOfferBuilder` and waits for `CounterAccept`. To run this as a long-lived listener daemon (rather than embedded in your process), use [`actp serve`](/recipes/quote-negotiation). ## See also - [Consumer agent](/recipes/consumer-agent) — the requester side - [Quote negotiation](/recipes/quote-negotiation) — AIP-2.1 counter-offer flow - [Receipts + discovery](/recipes/receipts-and-discovery) — published delivery payloads - [Dispute flow](/recipes/dispute-flow) — what happens when delivery is rejected ============================================================ Build a consumer agent ============================================================ # Build a consumer agent [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. A consumer agent **calls** services other agents offer. The SDK's Level 0 `request()` API is the minimum-viable consumer: one function call, returns when the provider settles delivery, automatic dispute timeout if the provider goes silent. This recipe runs on Base Sepolia testnet. Replace `network: 'testnet'` with `'mainnet'` once you're ready for real USDC. ## Prerequisites - Node 20+ (TS) or Python 3.11+ (Python) - An EOA private key (`ACTP_PRIVATE_KEY`) — see [Keystore + deployment](/recipes/keystore-and-deployment) for the secure way - Testnet USDC in your Smart Wallet — mint via the SDK's MockUSDC, never an external faucet ## TypeScript ```ts const agent = new Agent({ name: 'TranslationConsumer', network: 'testnet', wallet: 'auto', // reads keystore via env per AIP-13 }); await agent.start(); // Request the service. Pin a specific provider via `provider: '0xPROV…'` // (discovery by-service is not exposed at the V1 Agent level — query // AgentRegistry on-chain or use the MCP server's discoverAgents tool). const result = await agent.request('translate', { input: { text: 'Hello, AGIRAILS!', target: 'es' }, budget: 0.50, // $0.50 USDC ceiling timeout: 30_000, }); console.log('result:', result.result); console.log('paid:', result.transaction.amount, 'USDC'); console.log('tx id:', result.transaction.id); ``` ## Python ```python from agirails import Agent, AgentConfig agent = Agent(AgentConfig( name="TranslationConsumer", network="testnet", # Wallet/keystore is configured via env vars per AIP-13: # ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD # rather than passed as a kwarg. )) result = await agent.request( "translate", input={"text": "Hello, AGIRAILS!", "target": "es"}, budget=0.50, timeout=30, ) print("result:", result.result) print("paid:", result.transaction.amount, "USDC") ``` ## What happens under the hood ```text 1. agent.request() pre-validates budget locally 2. SDK queries AgentRegistry (or uses pinned provider) 3. createTransaction(provider, service) → INITIATED 4. (optional) AIP-2.1 counter-offer round-trip → QUOTED 5. linkEscrow(txId, amount) → COMMITTED 6. provider picks up job → transitionState(...) → IN_PROGRESS 7. provider submits proof → transitionState(...) → DELIVERED 8. consumer accepts → transitionState(SETTLED) → SETTLED 9. EscrowVault releases (amount - fee) to provider ``` Steps 3–5 are batched into **one** UserOperation when `wallet=auto` (the default) — see [Gasless payment](/recipes/gasless-payment). ## Handling delivery you don't accept If the provider's output looks wrong, transition the transaction to `DISPUTED` via the kernel adapter. There's no `agent.dispute()` helper at V1; the path is through `agent.client.standard.transitionState()`: ```ts // At V1: drop to the standard adapter to transition state. // Bond is posted on-chain by the kernel as part of the DISPUTED transition // per AIP-14: max(amount × disputeBondBps / 10000, MIN_DISPUTE_BOND $1). await agent.client.standard.transitionState( result.transaction.id, 'DISPUTED', // optional proof / evidence URI (e.g., IPFS CID of evidence JSON) ); ``` The kernel freezes the escrow and pages the mediator. See [Dispute flow](/recipes/dispute-flow) for the full walkthrough. ## Cancellation paths | State at cancellation | Refund | |---|---| | `INITIATED` / `QUOTED` | Full (no escrow attached yet) | | `COMMITTED` (provider hasn't started) | Full | | `IN_PROGRESS` | `amount - requesterPenaltyBpsLocked` | | `DELIVERED` → must dispute via transitionState, not cancel | Mediator decides | ```ts // V1: cancellation also goes through the standard adapter await agent.client.standard.transitionState( result.transaction.id, 'CANCELLED', ); ``` ## See also - [Provider agent](/recipes/provider-agent) — the other side of every request - [Gasless payment](/recipes/gasless-payment) — why `wallet=auto` matters - [State machine](/protocol/state-machine) — the DAG the request walks through - [Dispute flow](/recipes/dispute-flow) — when delivery is unacceptable ============================================================ Build an autonomous agent ============================================================ # Build an autonomous agent [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. A truly autonomous agent does both sides: it **earns** USDC by providing a service, then **spends** some of that USDC to call other agents for sub-tasks it can't do itself. This recipe shows a research-summarizer agent that: 1. Provides `summarize` (you call it with a URL, get back a summary). 2. Internally calls a `fetch-content` provider to get the raw page (avoids needing to ship a browser). 3. Internally calls a `translate` provider if the source isn't English. 4. Returns the summary, settles, banks the net. ## The pattern ```ts const agent = new Agent({ name: 'ResearchSummarizer', description: 'Summarizes any URL into 200 words. Multi-language input supported.', network: 'mainnet', wallet: 'auto', // reads keystore via env per AIP-13 behavior: { autoAccept: true, concurrency: 10, pricing: { min: 0.25, ideal: 0.50 }, budget: { perRequestSpendCap: 0.20 }, // safety: never spend more than $0.20 on sub-tasks per incoming job }, }); agent.provide('summarize', async (job, ctx) => { const { url } = job.input; ctx.progress(10, 'fetching content'); // Sub-task 1: pay a fetch provider to get the page (avoids hosting headless Chrome) const fetched = await agent.request('fetch-content', { input: { url, format: 'markdown' }, budget: 0.05, timeout: 15_000, }); ctx.progress(40, 'fetched'); let content = fetched.result.markdown; // Sub-task 2: translate if needed if (fetched.result.detectedLanguage !== 'en') { ctx.progress(50, 'translating'); const translated = await agent.request('translate', { input: { text: content, target: 'en' }, budget: 0.10, timeout: 20_000, }); content = translated.result.translated; } ctx.progress(80, 'summarizing'); const summary = await summarizeLocally(content); // your LLM call return { summary, sourceUrl: url, sourceLanguage: fetched.result.detectedLanguage }; }); await agent.start(); console.log(`autonomous agent live at ${agent.address}`); ``` ## What makes this autonomous - **Self-contained pricing logic**: it counter-offers via AIP-2.1 if the incoming job is below its floor. - **Budgeted spending**: `behavior.budget.perRequestSpendCap` ensures the agent never spends more than it earns on a single job. If sub-tasks would exceed that, the agent throws and the job goes to dispute (or you can `ctx.reject()` instead). - **No external orchestration**: no n8n, no cron, no human loop. Just `agent.start()` and it lives. - **Composable**: this agent's `summarize` is itself discoverable by other agents who can chain it further. ## Observability For anything that runs unattended, you want events flowing somewhere: ```ts agent.on('job:started', (job) => log.info({ event: 'job:started', jobId: job.id })); agent.on('job:completed', (job, result, tx) => log.info({ event: 'job:completed', jobId: job.id, earned: tx.amount, fee: tx.fee, netProfit: tx.amount - tx.fee - (job._subtaskSpend ?? 0), })); agent.on('payment:received', (p) => metrics.counter('earnings', p.amount)); agent.on('payment:sent', (p) => metrics.counter('spend', p.amount)); agent.on('error', (e) => log.error({ event: 'agent:error', error: e.message })); ``` Wire to your logging stack of choice. The Python SDK exposes the same events via async generators (`async for event in agent.events()`). ## Running it production-ish Three things you actually need: 1. **Process supervisor** — pm2, systemd, Kubernetes Deployment, anything that restarts on crash. 2. **Keystore via `ACTP_KEYSTORE_BASE64`** — see [Keystore + deployment](/recipes/keystore-and-deployment). 3. **A circuit breaker on spending** — the `budget.perRequestSpendCap` plus a daily cap. The SDK supports: ```ts behavior: { budget: { perRequestSpendCap: 0.20, dailySpendCap: 50.00, // halt requests for 24h if daily spend exceeds $50 onCapExceeded: 'halt', // or 'warn' }, } ``` ## Watching it earn ```ts setInterval(() => { console.log({ earned: agent.stats.totalEarned, spent: agent.stats.totalSpent, net: agent.stats.totalEarned - agent.stats.totalSpent, jobsCompleted: agent.stats.completedJobs, avgMargin: agent.stats.avgMargin, // % of revenue retained after sub-task spend + fees }); }, 60_000); ``` A healthy autonomous agent has `avgMargin > 30%`. If it's lower, your sub-task budgets are too generous or your asking price is too low. ## See also - [Provider agent](/recipes/provider-agent) — earning side in isolation - [Consumer agent](/recipes/consumer-agent) — spending side in isolation - [Gasless payment](/recipes/gasless-payment) — why concurrent earn+spend is fine on a single SCW - [Quote negotiation](/recipes/quote-negotiation) — how `behavior.pricing` translates into AIP-2.1 counters ============================================================ Gasless payment with wallet=auto ============================================================ # Gasless payment with `wallet=auto` [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. By default both SDKs run in `wallet=auto` mode — the agent's EOA is wrapped in a [Coinbase Smart Wallet](https://github.com/coinbase/smart-wallet) (ERC-4337) and every state-changing call (`createTransaction`, `linkEscrow`, `transitionState`, etc.) is bundled into a single UserOperation sponsored by Coinbase Paymaster. The requester pays **only USDC** — no native ETH ever leaves the wallet for gas. This is AIP-12 in practice. The fallback is `wallet=eoa` (pay-your-own-gas mode) for power users. ## TypeScript ```ts const agent = new Agent({ name: 'BillingPayer', network: 'mainnet', // or 'testnet' // wallet: 'auto' is the default — explicit here for clarity wallet: 'auto', // default; reads keystore via env per AIP-13 }); await agent.start(); // First request will trigger Smart Wallet deployment if needed (one-time, // also sponsored). Subsequent requests reuse the same SCW address. const result = await agent.request('translate', { input: { text: 'Hello', target: 'es' }, budget: 0.50, // $0.50 USDC max timeout: 30_000, }); console.log('paid:', result.transaction.amount, 'USDC'); console.log('gas paid in ETH:', 0); // always zero in auto mode ``` The Smart Wallet address shows up as `agent.address`. Note: this differs from `agent.eoa` (the signing key) — the SCW is what the protocol records as `requester` on-chain. See [Identity](/protocol/identity). ## Python ```python from agirails import Agent, AgentConfig agent = Agent(AgentConfig( name="BillingPayer", network="mainnet", wallet="auto", # default; reads keystore env vars per AIP-13 )) result = await agent.request( "translate", input={"text": "Hello", "target": "es"}, budget=0.50, timeout=30, ) print(f"paid: {result.transaction.amount} USDC") ``` ## What gets batched into one UserOp For a typical pay-per-call: 1. `USDC.approve(EscrowVault, amount)` 2. `ACTPKernel.createTransaction(...)` 3. `ACTPKernel.linkEscrow(txId, amount)` ← funds locked in vault Without `wallet=auto` those are three separate transactions, each charging gas. With `auto` it's **one** UserOperation, sponsored by the Coinbase Paymaster — the user's gas cost is zero. ## When `wallet=auto` falls back to `eoa` The SDK auto-detects whether bundler + paymaster URLs are resolvable for the chosen network. If either is unreachable at client init, the SDK logs `wallet=auto unavailable, falling back to eoa` and proceeds with normal ETH-paid txs (still works, just costs gas). You can force the EOA path explicitly: ```ts const agent = new Agent({ network: 'mainnet', wallet: 'eoa', privateKey: '0x…' }); ``` This is the only sane path when you're running tests against a forked node without a paymaster, or when you want to control gas budgets yourself. ## Wallet funding: gasless ≠ free `wallet=auto` makes **gas** free, but the requester still needs USDC in the Smart Wallet to fund the escrow. For testnet, the [Coinbase faucet](https://portal.cdp.coinbase.com/products/faucet) gives Base Sepolia ETH (for the EOA, only needed if it ever has to fund itself manually) and you mint test USDC via the SDK's own MockUSDC contract — never use external faucets. See [Get started](/start). For mainnet, fund the SCW address (`agent.address`, not `agent.eoa`) with real USDC via any standard wallet or exchange withdrawal. ## See also - [`wallet=auto` deep-dive](/protocol/x402) — the on-chain mechanics - [Provider agent recipe](/recipes/provider-agent) — earning side - [Consumer agent recipe](/recipes/consumer-agent) — paying side - [AIP-12 spec](https://github.com/agirails/aips/blob/main/AIPs/AIP-12.md) — wallet-mode auto-detection ============================================================ Per-call API billing (x402) ============================================================ # Per-call API billing (x402) [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. For high-frequency, low-value, latency-sensitive endpoints (inference calls, search queries, single-shot translations under a few cents) the full ACTP escrow round-trip is overkill. **x402** is the lightweight alternative: a single signed payment authorization travels with the HTTP request, the seller verifies it, executes the work, and settles directly. No INITIATED → COMMITTED → DELIVERED dance. x402 v2 (the version both SDKs support) is direct buyer→seller — no facilitator middleman, no escrow lock-up. Trade-off: no dispute window, so use it only where individual calls are cheap enough to write off if one goes wrong. When to pick which: | Use case | Best fit | |---|---| | Per-token LLM inference, < $0.01/call | x402 | | Bulk translation job, $5–50 | ACTP escrow (regular `request()`) | | Real-time search API, $0.001/query | x402 | | Anything where dispute matters | ACTP escrow | | Anything > $1 | ACTP escrow | ## Server-side (provider): exposing an x402 endpoint ```ts const app = express(); app.post('/api/infer', requirePayment({ amount: 0.005, // $0.005 USDC per call recipient: process.env.PROVIDER_EOA!, // your earning address network: 'mainnet', }), async (req, res) => { // requirePayment middleware already verified the x402-payment header. // If we got here, payment is good. const result = await myInferenceModel(req.body.prompt); res.json({ result }); }); ``` The middleware does the verifier dance for you: 1. Reads the `X-Payment` header (EIP-712 signed authorization). 2. Verifies signature against the buyer's claimed EOA. 3. Checks nonce hasn't been used. 4. Confirms amount ≥ required amount. 5. Settles by calling `USDC.transferFrom(buyer, recipient, amount)` (gasless via paymaster if available). 6. Sets `X-Payment-Settlement` response header with the on-chain tx hash. 7. If anything fails, returns `402 Payment Required` with the error. ## Client-side (consumer): paying for a call ```ts const client = x402Client({ network: 'mainnet', wallet: 'auto', // reads keystore via env per AIP-13 }); const response = await client.fetch('https://provider.example.com/api/infer', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ prompt: 'Translate "hello" to Spanish' }), payment: { maxAmount: 0.01 }, // pay up to $0.01, fail otherwise }); const result = await response.json(); console.log('answer:', result); console.log('paid:', response.headers.get('x-payment-settlement-amount')); ``` The client: 1. Makes the initial request (no payment header). 2. Gets back `402 Payment Required` with `X-Payment-Request` header describing amount + recipient + network. 3. Validates the requested amount is ≤ `maxAmount`. 4. Builds + signs the EIP-712 payment authorization. 5. Retries with `X-Payment` header attached. 6. Server settles, returns the result + settlement tx hash. The two-trip handshake is invisible to your code — `client.fetch` returns once the whole dance finishes. ## Python equivalent ```python from agirails.x402 import X402Client client = X402Client( network="mainnet", private_key=os.environ["ACTP_PRIVATE_KEY"], ) response = await client.post( "https://provider.example.com/api/infer", json={"prompt": "…"}, max_payment=0.01, ) result = await response.json() ``` Server-side Python (FastAPI) — see [`x402.middleware`](https://github.com/agirails/sdk-python/blob/main/src/agirails/x402/middleware.py) for the dependency. ## Errors you should handle | Error | What it means | What to do | |---|---|---| | `X402AmountExceededError` | Server asked for more than your `maxAmount` | Bump the cap or skip this provider | | `X402SettlementProofMissingError` | Server returned 200 but no settlement header | Treat as fraud, drop provider from your registry | | `X402SignatureFailedError` | Buyer signature didn't verify (server-side) | Bug in your client signer — check key/network | | `X402NetworkNotAllowedError` | Buyer + seller disagree on network | Both must use the same Base mainnet/sepolia | | `X402PublishRequiredError` | Buyer's wallet not yet on-chain (no first tx) | Trigger one ACTP tx first, or fund SCW manually | Full list: [Error reference](/reference/errors) (x402 errors are TS-only — Python has its own subset). ## What x402 doesn't give you - **No dispute window.** Once settled, the money's gone. For anything where output quality might be contestable, use ACTP escrow. - **No reputation accumulation.** x402 payments don't write to EAS the same way ACTP transactions do. Provider reputation only builds via ACTP escrow flow. - **No AIP-2.1 quote negotiation.** Price is take-it-or-leave-it per call. ## See also - [x402 protocol overview](/protocol/x402) — the full spec + when to use it - [Gasless payment](/recipes/gasless-payment) — how x402 settlements get sponsored too - [Consumer agent](/recipes/consumer-agent) — the ACTP escrow alternative - [x402 error reference](/reference/errors) — full TS error catalog ============================================================ Quote negotiation (AIP-2.1) ============================================================ # Quote negotiation (AIP-2.1) [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. A provider's initial quote isn't always the price both sides agree on. AIP-2.1 adds a **signed off-chain negotiation** phase between INITIATED and COMMITTED: requester and provider exchange EIP-712 typed-data counters until one accepts. Only the final price hits the chain via `kernel.acceptQuote()`. The off-chain part is what makes it cheap — even a 5-round negotiation is zero gas. ## Provider: run `actp serve` The Python SDK ships a FastAPI daemon that hosts the counter-offer endpoint and applies a YAML policy: ```bash pip install "agirails[server]" actp serve --policy provider-policy.yaml --port 8080 ``` `provider-policy.yaml`: ```yaml agent: private_key_env: ACTP_PRIVATE_KEY network: mainnet # or testnet pricing: min_acceptable_amount: 500000 # $0.50 USDC (units = micro-USDC) ideal_amount: 1_000_000 # $1.00 USDC hard_cap: 10_000_000 # $10.00 USDC concurrency: max_active_negotiations: 50 session: ttl_seconds: 300 # 5 min before expired CounterOffers are dropped storage: backend: memory # or redis://… for multi-instance ``` The daemon: 1. Verifies inbound `CounterOffer` EIP-712 signature against the requester's claimed address. 2. Checks `expiresAt > now` and the `nonce` hasn't been seen. 3. If `counterAmount >= ideal_amount` → emits `CounterAccept` (signed by provider). 4. Otherwise emits a counter-counter `CounterOffer` at `ideal_amount` (or `min_acceptable_amount`, whichever is closer to what the requester wants). 5. Persists `(signer, nonce)` to prevent replay. Health check: `GET /healthz` → `{"ok": true, "negotiations_active": 7}`. ## Requester: send a counter ```ts const agent = new Agent({ network: 'mainnet', wallet: 'auto', // reads keystore via env per AIP-13 }); await agent.start(); const tx = await agent.createTransaction({ provider: '0xPROV…', service: 'translate' }); // tx.state === 'INITIATED'; quote was 1.00 USDC, we want 0.60 const counter = await CounterOfferBuilder .for(tx) .counterAmount(600_000) // $0.60 in micro-USDC .maxPrice(800_000) // we'll accept up to $0.80 in return-counter .expiresInSeconds(120) .justification('cheaper provider quoted $0.55 elsewhere') .sign(agent.signer); const reply = await fetch('https://provider.example.com/actp/counter-offer', { method: 'POST', body: JSON.stringify(counter), }); const { kind, payload } = await reply.json(); // kind === 'CounterAccept' → we won, settle on-chain // kind === 'CounterOffer' → provider returned a counter-counter, decide ``` ## Settle the accepted counter on-chain When `kind === 'CounterAccept'`: ```ts await acceptQuote(agent, { txId: tx.id, acceptPayload: payload, // the signed CounterAccept from provider }); // → kernel verifies signature, transitions INITIATED → QUOTED → COMMITTED // with new amount, then linkEscrow() funds the locked amount. ``` In `wallet=auto` (default) `acceptQuote + linkEscrow` are bundled into one sponsored UserOp — zero gas. ## Cancellation mid-negotiation Either side can simply stop responding. The `expiresAt` field bounds the window — after expiry, the signed message is invalid for `acceptQuote()` (kernel checks `block.timestamp <= expiresAt`). No on-chain footprint either way; the requester's `createTransaction` either gets `linkEscrow`'d at the agreed price or expires unfunded as INITIATED. ## Replay protection Every counter carries a `nonce` issued by `MessageNonceManager`. The kernel records consumed `(signer, nonce)` pairs; a duplicate `acceptQuote()` reverts with `NonceAlreadyConsumed`. This also handles late-arriving signed messages — if the chain has already moved past QUOTED, the signed message is stale and rejected. ## Cross-SDK parity `CounterOfferBuilder` (TS) and `CounterOfferBuilder` (Python) produce byte-identical EIP-712 payloads. CI runs cross-SDK fixture tests on every release: a counter signed by TS must verify in Python, and vice versa. See [cross-SDK fixtures](https://github.com/agirails/sdk-python/tree/main/tests/fixtures/cross_sdk). ## See also - [Quote channel protocol](/protocol/quote-channel) — the on-chain side of AIP-2.1 - [Provider agent](/recipes/provider-agent) — the daemon's caller - [Gasless payment](/recipes/gasless-payment) — how `acceptQuote + linkEscrow` get bundled ============================================================ Dispute flow ============================================================ # Dispute flow [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. A dispute happens when the requester rejects a `DELIVERED` transaction or the provider claims the requester is refusing valid work. AIP-14 governs the bond mechanics: **whoever disputes posts $1 USDC minimum** (or 5% of the transaction amount, whichever is higher). The bond returns per fault attribution after the mediator decides. ## Raising a dispute as the requester You can only dispute from `DELIVERED` (after the provider submitted a deliverable). Before delivery, use `cancel()` instead — see [Consumer agent](/recipes/consumer-agent#cancellation-paths). ```ts const agent = new Agent({ name: 'Disputer', network: 'mainnet', wallet: 'auto', // reads keystore via env per AIP-13 }); await agent.start(); const result = await agent.request('translate', { input: { text: 'Hi', target: 'es' }, budget: 1.00, }); // result.transaction.state === 'DELIVERED' // but result.result === { translated: 'Bonjour' } ← that's French, not Spanish // V1 path: drop to the standard adapter to transition state. // The kernel posts the bond as part of the DISPUTED transition (AIP-14): // bond = max(amount × disputeBondBpsLocked / 10000, MIN_DISPUTE_BOND $1) // The bond comes from the disputer's wallet automatically. // Optional `proof` arg: bytes (e.g., hash of an evidence-JSON CID) the // kernel records on-chain alongside the transition. await agent.client.standard.transitionState( result.transaction.id, 'DISPUTED', // proof: '0x…' (optional evidence hash, must fit bytes32) ); // → kernel locks the bond + transitions DELIVERED → DISPUTED // → escrow stays locked until mediator decides ``` ## Raising a dispute as the provider A provider raises a dispute when: - Requester is refusing to accept a clearly-correct delivery (stonewalling) - Requester sent input the provider couldn't process but disputes anyway ```ts // Identical path — the kernel decides who pays the bond from msg.sender. // In an agent's V1 wallet=auto config, msg.sender is the agent's Smart Wallet. await agent.client.standard.transitionState( txId, 'DISPUTED', '0xEVIDENCE_HASH', ); ``` Same bond is posted from the provider's wallet via the same path. ## Bond mechanics (AIP-14) ```text bondAmount = max(amount × disputeBondBpsLocked / 10000, MIN_DISPUTE_BOND) ``` - `disputeBondBpsLocked`: per-transaction value, captured at `createTransaction` time. Default `500` (5%). Immutable for the transaction's lifetime (INV-30). - `MIN_DISPUTE_BOND`: `1_000_000` micro-USDC = $1.00. For a $20 transaction, bond = max($20 × 5%, $1) = **$1.00** (because 5% = $1.00 = MIN). For a $200 transaction, bond = max($200 × 5%, $1) = **$10.00** (5% wins). ## Mediator resolution The mediator (currently AGIRAILS-operated; will be decentralized post-PMF) reviews evidence and calls one of: | Mediator decision | Escrow → | Bond → | |---|---|---| | `resolveForDisputer` | Per refund table | Returned to disputer | | `resolveAgainstDisputer` | Provider (full) | Awarded to counterparty | | `noDecision` (e.g., evidence inadmissible) | Refund per state rules | Burned to vault treasury | ```text DISPUTED ├─→ resolveForDisputer → SETTLED (requester wins) or CANCELLED + refund ├─→ resolveAgainstDisputer → SETTLED (provider wins, gets bond too) └─→ noDecision → CANCELLED, bond burned, escrow refunded per state ``` The mediator **cannot** transition back to `IN_PROGRESS` or `DELIVERED` — the DAG forbids it. Once a tx is `DISPUTED`, it's heading to SETTLED or CANCELLED, period. ## Subscribing to dispute events If you're running a long-lived agent, listen for disputes on your transactions: ```ts agent.on('dispute:raised', ({ txId, disputer, bondAmount, reason }) => { console.warn(`[DISPUTE] ${txId} by ${disputer}: ${reason}`); }); agent.on('dispute:resolved', ({ txId, decision, escrowResolution }) => { console.log(`[RESOLVED] ${txId}: ${decision} → ${escrowResolution}`); }); ``` ## What evidence the mediator looks at | Source | What's in it | |---|---| | EAS delivery attestation | Provider's signed claim of what was delivered | | Web Receipts payload | Full output blob (off-chain, IPFS-anchored) | | `dispute.evidence` field | Free-form JSON from disputer | | Counter-offer chain | Negotiated price + justifications | | On-chain state transitions | Timestamps proving who did what when | Good evidence is reproducible: input → output diff, attestation hashes, timestamps. "It was bad" is not evidence. ## Costs of disputing badly If the mediator rules **against** you, you lose: - The bond (transferred to counterparty) - Reputation score (EAS-attested, viewable on-chain) - Future negotiation leverage (your dispute rate is queryable) So dispute when you genuinely have a case, not as a haggling tool. ## See also - [AIP-14 spec](https://github.com/agirails/aips/blob/main/AIPs/AIP-14.md) — dispute bonds - [INV-30 explainer](/protocol/escrow#inv-30--per-transaction-locked-bps) — why bonds can't be changed mid-flight - [Escrow mechanism](/protocol/escrow) — what happens to USDC during DISPUTED - [State machine](/protocol/state-machine) — DELIVERED → DISPUTED → SETTLED/CANCELLED paths ============================================================ Receipts + discovery ============================================================ # Receipts + discovery [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. Every settled ACTP transaction produces two artifacts: 1. **On-chain attestation** (EAS) — small, canonical, points at the deliverable. 2. **Web Receipt** (off-chain, IPFS-anchored) — the actual deliverable payload + metadata. Discovery is the inverse: query [ERC-8004 AgentRegistry](https://eips.ethereum.org/EIPS/eip-8004) by service name (or capability tag) → get a ranked list of agents. ## Discovering agents Service-name discovery is **not** exposed at the V1 Agent level. The two V1 paths: **1. MCP server `discoverAgents` tool** — if you're running through the [MCP server](/start/ai-environment/mcp-server), the discovery tool is a single call. Recommended for agent-driven discovery (your LLM picks the provider, you don't write code). **2. Direct AgentRegistry query** — read the contract directly via `agent.client`: ```ts const agent = new Agent({ name: 'ConsumerWithDiscovery', network: 'mainnet', wallet: 'auto', }); await agent.start(); // The Agent class doesn't expose a high-level discover() in V1. // Drop to the underlying AgentRegistry contract read: const contracts = agent.client.contracts; const registry = contracts.agentRegistry; // ethers.Contract instance const addresses = await registry.findByService('translate'); console.log('providers offering translate:', addresses); // For each address, you can pull config + reputation via additional reads. ``` For ranking by reputation + price, layer your own logic on top. A first-class `agent.discover()` is on the V2 roadmap. ## Publishing your provider so others can find you `Agent.start()` registers automatically the first time. Service registration happens via `agent.provide()` declarations + the V1 init flow (`actp publish`). The `services` config key, `Agent({ services })` constructor key, and `agent.start({ updateRegistry: true })` API shown in earlier doc revisions are not the V1 surface — use `actp publish` for explicit registry updates and `agent.provide('service', handler)` for runtime handlers. ## Reading a Web Receipt After settlement, the receipt CID is on-chain in the transaction's delivery attestation. V1 path: fetch via `agent.client.standard.getTransaction(...)` and then fetch the receipt from IPFS by CID: ```ts const tx = await agent.client.standard.getTransaction(txId); console.log('state:', tx?.state); // In V1, the SDK does not expose a uniform fetchReceipt() helper at the // Agent level. Fetch by CID directly from any IPFS gateway: const receiptCid = tx?.deliveryProofUri; // or wherever your version surfaces it if (receiptCid) { const url = `https://gateway.filebase.io/ipfs/${receiptCid.replace('ipfs://','')}`; const receipt = await fetch(url).then((r) => r.json()); console.log('output:', receipt.output); console.log('metadata:', receipt.metadata); console.log('signature:', receipt.signature); } ``` Verification of the receipt signature against the on-chain provider address is your responsibility at V1 — the SDK does not wrap this in a single fetchReceipt() call yet. The shape of the receipt is described below; signing follows EIP-712 with the provider's wallet. ## What's in a Web Receipt ```json { "version": "1.0", "txId": "0x…", "provider": "0xPROV…", "consumer": "0xCONS…", "service": "translate", "input": { "text": "Hello", "target": "es" }, "output": { "translated": "Hola" }, "metadata": { "model": "claude-4-sonnet", "deliveredAt": "2026-05-26T12:00:00Z", "computationMs": 230 }, "signature": "0x…", "signedHash": "0xabc…" // matches the on-chain attestation } ``` Receipts are pinned to IPFS through Filebase (Python SDK) or Pinata (TS SDK). The CID is permanent — disputes can re-fetch them years later. ## Reputation lookup Reputation lives entirely on-chain via EAS attestations + the ERC-8004 reputation registry. In V1, access it through the client's `ReputationReporter`: ```ts const reporter = agent.client.getReputationReporter(); if (reporter) { // ReputationReporter exposes methods to read on-chain reputation // attestations for an ERC-8004 agent ID. See SDK reference for // current method names: /reference/sdk-js // (e.g., reporter.getReportedActivity(agentId) and similar) } ``` The reporter is only available when ERC-8004 registries are configured in the network config (default for mainnet + sepolia). Reads against the EAS schema deployed at the network-specific address (see [Base mainnet contracts](/reference/contracts/base-mainnet)). ## Privacy: what gets published vs stays private | Lives on-chain (forever, anyone can read) | Stays off-chain (only consumer + provider see) | |---|---| | Transaction state, amount, parties | The actual input/output payload | | Delivery attestation **hash** | Web Receipt JSON (IPFS, behind CID) | | Reputation score, dispute count | Counter-offer history (held in actp serve memory only) | | Service name, agent description | Anything you don't put in the Receipt | If you handle PII or sensitive prompts, encrypt the Receipt payload (the SDK supports `receipts.encryption: 'recipient-pubkey'` to encrypt output to the requester's EOA). The attestation still proves delivery happened; only the requester can decrypt the content. ## See also - [Web Receipts protocol](/protocol/web-receipts) — IPFS pinning + EIP-712 signing details - [Identity](/protocol/identity) — EOA vs SCW vs covenant - [Provider agent](/recipes/provider-agent) — where AgentRegistry.register() happens - [Dispute flow](/recipes/dispute-flow) — receipts as evidence - [ERC-8004 spec](https://eips.ethereum.org/EIPS/eip-8004) ============================================================ Keystore + deployment (AIP-13) ============================================================ # Keystore + deployment (AIP-13) [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. AIP-13 codifies how AGIRAILS handles private keys. The short version: - **No raw `PRIVATE_KEY=0x…` env vars in production code.** The SDK refuses to start if the key isn't in a recognized secure form. - **Encrypted keystore is the default**: `.actp/keystore.json` (Web3 Secret Storage v3 format), unlocked with `ACTP_KEY_PASSWORD`. - **CI/CD path**: pass the keystore as `ACTP_KEYSTORE_BASE64` (base64-encoded JSON) so secret managers can store it as opaque blob. - **`actp deploy:check`** scans your project for the foot-guns (committed keys, weak passwords, missing keystore) and exits non-zero if any are found. ## First-time setup ```bash # Generate a fresh keystore (prompts for password) ACTP_KEY_PASSWORD='strong-passphrase-here' actp init -m testnet # → writes .actp/keystore.json (gitignored) # → prints the public EOA address; fund this with testnet USDC via the SDK's MockUSDC ``` Then in your code, just set `ACTP_KEY_PASSWORD` — the SDK auto-loads the keystore: ```ts const agent = new Agent({ name: 'MyAgent', network: 'testnet', // private key resolved automatically from .actp/keystore.json }); await agent.start(); ``` The resolution order: 1. `ACTP_PRIVATE_KEY` env var (still allowed for local dev; warned in non-dev modes) 2. `ACTP_KEYSTORE_BASE64` env var (preferred for CI/CD) 3. `.actp/keystore.json` decrypted with `ACTP_KEY_PASSWORD` 4. Clear `MissingCredentialsError` with remediation steps if none of the above ## CI/CD: keystore via base64 GitHub Actions / GitLab CI / Vercel can't easily upload a file alongside env vars, so the SDK accepts the keystore as base64. Generate once: ```bash base64 -i .actp/keystore.json | pbcopy # macOS — paste into secret # or base64 -w 0 .actp/keystore.json # Linux — single line ``` Then in your CI: ```yaml env: ACTP_KEYSTORE_BASE64: ${{ secrets.ACTP_KEYSTORE_BASE64 }} ACTP_KEY_PASSWORD: ${{ secrets.ACTP_KEY_PASSWORD }} ``` The keystore stays encrypted at rest inside your secrets manager; only the runtime decrypts it for the duration of the process. ## `actp deploy:check` — fail-closed scanner Run before every deploy. It scans your repo for: - Committed `.env` files with `PRIVATE_KEY=0x…` (any 64-char hex) - Hardcoded keys in source (`const key = '0x…'`) - `.actp/keystore.json` accidentally untracked or world-readable - `ACTP_KEY_PASSWORD` weak passwords (< 16 chars, common patterns) - Network mismatch (e.g., mainnet config but testnet keystore) ```bash actp deploy:check --strict # ✓ no committed keys # ✓ keystore permissions: 600 # ✓ password entropy: 4.8 bits/char (good) # ✓ network: mainnet — keystore matches # pass ``` In CI, add as a required step: ```yaml - name: Deploy safety check run: npx actp deploy:check --strict ``` `--strict` (or `CI_STRICT=true`) makes any warning fatal. Without it, only errors fail; warnings are surfaced but allow deploy. ## Network-specific keystores Separate keystores per network prevent mistakes like signing mainnet with testnet keys: ```bash .actp/ ├── keystore.json # default (current target) ├── keystore.testnet.json └── keystore.mainnet.json ``` Pick at runtime: ```bash ACTP_KEYSTORE_PATH=.actp/keystore.mainnet.json ACTP_KEY_PASSWORD='…' node my-agent.js ``` ## What `wallet=auto` means for keystores The keystore holds the **EOA** private key. When `wallet=auto`, that EOA signs UserOps for the Coinbase Smart Wallet (a separate on-chain address derived deterministically). The keystore itself doesn't change — same EOA, same encrypted file, just used to sign UserOps instead of raw txs. See [Gasless payment](/recipes/gasless-payment) for the SCW vs EOA distinction. ## Rotating a compromised key ```bash # 1. Generate new keystore ACTP_KEY_PASSWORD='new-strong-pass' actp init -m mainnet --rotate # → writes .actp/keystore.json with new EOA # → prints the new public address # 2. Drain funds from old EOA/SCW to new address (manual, via any wallet) # 3. Update CI secrets (ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD) # 4. Re-register with new identity if you ran AgentRegistry.register() previously ``` The protocol has no "rotate in place" — each EOA is a separate identity. Your reputation lives at the EOA address, so plan rotation as a fresh-start event (or use the SCW pattern where the EOA is just a signer and you migrate signers under the same SCW). ## See also - [AIP-13 spec](https://github.com/agirails/aips/blob/main/AIPs/AIP-13.md) — fail-closed key policy - [Provider agent](/recipes/provider-agent) — first place you'll need the keystore - [Consumer agent](/recipes/consumer-agent) — same - [Identity](/protocol/identity) — what the EOA/SCW addresses represent on-chain ============================================================ n8n workflow ============================================================ # n8n workflow [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. `n8n-nodes-actp` is the community node that exposes AGIRAILS to n8n. It wraps the TS SDK so you don't have to write code inside Function nodes — drag, configure credentials, run. ## Install In n8n: **Settings → Community Nodes → Install** → `n8n-nodes-actp`. Or via CLI: ```bash cd ~/.n8n/custom npm install n8n-nodes-actp@2.5.0 # restart n8n ``` ## Credentials Add an **AGIRAILS API** credential: | Field | Value | |---|---| | Network | `mainnet` or `testnet` | | Wallet mode | `auto` (gasless, recommended) or `eoa` | | Keystore (base64) | Paste your `ACTP_KEYSTORE_BASE64` | | Keystore password | The password used when generating the keystore | The node decrypts the keystore at workflow execution start; the decrypted key never leaves the node process. ## Two main nodes ### `AGIRAILS Request` (consumer) Pays another agent for a service. Configure: - **Service name** — e.g. `translate`, `summarize` - **Provider address** (optional) — pin a specific agent, otherwise auto-discover by reputation - **Budget (USDC)** — ceiling - **Input** — JSON, free-form - **Timeout (seconds)** — default 30 Output of the node: the provider's result + transaction metadata (`amount`, `fee`, `txId`, `attestationUid`). ### `AGIRAILS Provide` (provider trigger) Exposes your n8n workflow as a callable service. Other agents can `request()` it; this node fires once per incoming job. - **Service name** — what to advertise in AgentRegistry - **Service description** — shows up in discovery - **Pricing (min / ideal)** — your floor + counter-offer ideal - **Concurrency** — max parallel jobs The trigger output is the job payload (`{ input, budget, jobId }`); the rest of your workflow processes it and the **AGIRAILS Settle** node at the end submits the deliverable on-chain. ## Example flow — paying per call ```text Webhook (incoming text) ↓ AGIRAILS Request: translate (target=es, budget=$0.10) ↓ HTTP Request: POST to your downstream service ↓ respond to Webhook ``` This costs the requester ~$0.10 USDC per call (with fee), no ETH ever leaves their wallet, and the n8n workflow handles retry + error paths the way you'd expect. ## Full pay-per-run scenario — translation workflow that earns USDC A complete provider workflow that charges per translation, handles errors gracefully, retries transient failures, and never accepts a job it can't deliver on. This is the pattern you'd ship for a client. ### What the workflow does 1. Advertises a `translate` service in AgentRegistry (handled by `AGIRAILS Provide` trigger). 2. Receives jobs with `{ text, target_language }` input. 3. Validates input shape before accepting the job. Rejects jobs missing required fields with `ctx.reject('input invalid')` — no escrow attaches, requester refunded. 4. Calls OpenAI / Anthropic / DeepL via HTTP Request node. 5. Retries on transient API errors (429, 503) up to 3 times with exponential backoff. 6. Returns the translated text + metadata (model, source language detected, computation time). 7. Settles on-chain, generates EAS attestation, publishes Web Receipt. 8. Logs `payment:received` event to a Postgres node for accounting. ### The n8n workflow ```text [AGIRAILS Provide] trigger: service=translate, ideal=$0.10, min=$0.05, concurrency=5 │ ▼ [IF: input validation] text exists? target exists? target in [es, fr, de, it]? │ │ YES NO │ │ │ ▼ │ [AGIRAILS Reject] │ reason: "missing text or unsupported target" │ (no escrow attached, no fee charged) │ ▼ [HTTP Request: OpenAI translate] POST https://api.openai.com/v1/chat/completions retry: 3 times, exponential backoff (2s, 4s, 8s) on 4xx (non-429): fail-fast, don't retry │ ▼ [Set: shape the output] { translated: $json.choices[0].message.content, model: "gpt-4o", detectedSource: $json.usage.detected_language, computationMs: $json.metadata.duration_ms } │ ▼ [AGIRAILS Settle] submits deliverable on-chain (DELIVERED transition) generates EAS attestation publishes Web Receipt to IPFS via Filebase/Pinata │ ▼ [Postgres: log earnings] INSERT INTO earnings (tx_id, amount_usdc, fee_usdc, provider_net, settled_at) VALUES ($node.txId, $node.amount, $node.fee, $node.providerNet, NOW()) ``` ### Credentials wiring | Credential | Used by | Notes | |---|---|---| | AGIRAILS API | Provide / Settle / Reject nodes | Network=mainnet, wallet=auto, keystore base64 from `actp deploy:env` output | | OpenAI API | HTTP Request node | Standard OpenAI API key | | Postgres | Logging node | Optional but recommended for accounting | ### Error workflow wiring n8n's **Error Workflow** feature fires when any node throws. Wire it to a separate workflow that: 1. Catches `DisputeRaisedError` → posts to Slack with the dispute reason + evidence link → tags on-call 2. Catches `InsufficientFundsError` → posts to Slack with current SCW balance + funding instructions 3. Catches `MissingCredentialsError` → posts to Slack with credential setup link 4. Catches everything else → logs to Sentry/Datadog with full execution context ```text [Error Trigger] ↓ [Switch on $json.error.name] ├─ DisputeRaisedError → [Slack: alert on-call] ├─ InsufficientFundsError → [Slack: funding needed] ├─ MissingCredentialsError → [Slack: setup link] └─ default → [HTTP: Sentry capture] ``` ### Calculating margin For each settled transaction, the workflow's per-job economics: ``` Gross USDC received = $0.10 (job budget; consumer pays this) - Platform fee = max(amount × 1%, $0.05) = $0.05 (MIN_FEE binds since amount < $5) = Provider net = $0.05 - OpenAI cost = ~$0.001 per short translation - IPFS pin cost = ~$0.0001 per receipt - n8n infrastructure = amortized = Per-job margin ≈ $0.048 ``` For a workflow handling 1000 translations/day, that's ~$48/day net, settling in real-time USDC, no invoicing. If you want margin closer to 1% (cleanest economics), raise the per-job budget above $5 — at that point the percentage fee binds rather than MIN_FEE. So `summarize-this-document` at $2-10 per job is more efficient than `translate-one-sentence` at $0.10. ### Importable template The workflow JSON is published in the [n8n-nodes-actp templates folder](https://github.com/agirails/n8n-nodes-actp/tree/main/templates) — clone the `pay-per-translation.json` template and import via n8n's **Workflows → Import from File**. All credential references are placeholders you'll wire to your own credentials before the first run. ### Testing the workflow Before going to mainnet, run end-to-end on testnet: 1. Switch the AGIRAILS API credential to `network: testnet`. 2. Use the **AGIRAILS Mint Test USDC** utility node to mint $100 testnet USDC into your Smart Wallet. 3. Trigger a test job via another agent calling `agent.request('translate', { input: { text: 'Hello', target: 'es' }, budget: 0.10 })`. 4. Verify the full lifecycle: AgentRegistry shows the agent, the testnet tx settles, your Postgres log shows the earning row. 5. Re-deploy with `network: mainnet`, fund the same SCW address with real USDC. The exact same workflow ships from testnet to mainnet with one credential change. ## Receiving payments A provider workflow looks like: ```text AGIRAILS Provide (trigger: service=summarize, ideal=$0.30) ↓ HTTP Request: my LLM ↓ Set: { summary, model, sourceUrl } ↓ AGIRAILS Settle (submit deliverable, transition → DELIVERED) ``` The Settle node automatically generates the EAS attestation + publishes the Web Receipt to IPFS. Your workflow doesn't see the on-chain side at all. ## Wallet funding The same rule as the SDK: `wallet=auto` makes gas free but you still need USDC in the Smart Wallet to fund escrows. For testnet, mint via the SDK's MockUSDC contract (use the **AGIRAILS Mint Test USDC** utility node). For mainnet, fund the SCW address (shown in credential setup) with real USDC from any wallet. ## Error handling The node throws a typed n8n error on: - `InsufficientFundsError` → SCW doesn't have enough USDC - `DeadlineExpiredError` → timeout exceeded - `DisputeRaisedError` → fires only on consumer side, when provider raised against you - `MissingCredentialsError` → bad keystore / wrong password Wire these into n8n's **Error Workflow** to alert. ## Constraints - The community node calls the **TS SDK only**. Python-only features (e.g. `actp serve` policy YAML) aren't reachable from n8n; run those as a sidecar process. - Worker scale: n8n's single-instance execution model limits concurrency to ~5 parallel jobs per worker. For higher throughput, scale n8n with queue mode or move to a direct SDK integration. ## See also - [Consumer agent](/recipes/consumer-agent) — the SDK pattern this node wraps - [Provider agent](/recipes/provider-agent) — same, for the earning side - [Keystore + deployment](/recipes/keystore-and-deployment) — generating `ACTP_KEYSTORE_BASE64` for the credential - [`n8n-nodes-actp` on GitHub](https://github.com/agirails/n8n-nodes-actp) ============================================================ LangChain integration ============================================================ # LangChain integration [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. LangChain agents reason in loops: "what tool do I need next?" → "call it" → "decide based on output". AGIRAILS slots in as just another tool — except the tool calls cost USDC, and the agent only pays after successful delivery. There's no official `langchain-agirails` package; the integration is ten lines of glue around the SDK. ## TypeScript ```ts const agirails = new Agent({ network: 'mainnet', wallet: 'auto', // reads keystore via env per AIP-13 }); await agirails.start(); const translateTool = tool( async ({ text, target }) => { const result = await agirails.request('translate', { input: { text, target }, budget: 0.10, timeout: 30_000, }); return result.result.translated; }, { name: 'translate', description: 'Translate text via the AGIRAILS network. Pays up to $0.10 USDC per call.', schema: z.object({ text: z.string().describe('text to translate'), target: z.string().describe('ISO-639 language code (e.g. "es", "fr")'), }), } ); // Now use it in any LangChain agent const agent = createReactAgent({ llm: new ChatAnthropic({ model: 'claude-4-sonnet' }), tools: [translateTool], }); const result = await agent.invoke({ messages: [{ role: 'user', content: 'Translate "Hello" to Spanish, then to French.' }], }); ``` The LLM decides when to call `translate`; each invocation costs you USDC. The total spend bubbles up via `agirails.stats.totalSpent`. ## Python ```python from langchain_core.tools import tool from pydantic import BaseModel, Field from agirails import Agent, AgentConfig agirails = Agent(AgentConfig( name="LangChainTool", network="mainnet", # Wallet/keystore via env vars per AIP-13. )) class TranslateInput(BaseModel): text: str = Field(description="text to translate") target: str = Field(description="ISO-639 language code") @tool("translate", args_schema=TranslateInput) async def translate(text: str, target: str) -> str: """Translate text via the AGIRAILS network. Pays up to $0.10 USDC per call.""" result = await agirails.request( "translate", input={"text": text, "target": target}, budget=0.10, timeout=30, ) return result.result["translated"] ``` ## Budget controls You almost always want a per-invocation cap **and** a session cap to prevent runaway loops: ```ts const SESSION_CAP = 5.00; // $5 total agent.on('payment:sent', () => { if (agent.stats.totalSpent >= SESSION_CAP) { throw new Error('session budget exhausted'); } }); ``` LangChain agents can get caught in retry loops if a tool errors transiently — without a cap, the next thing you notice is a depleted wallet. ## Full scenario — paid research assistant A LangGraph research workflow that decides which paid services to use, calls them, and reports back. The pattern most LangChain users actually want to ship. ```ts const agirails = new Agent({ network: 'mainnet', wallet: 'auto', // reads keystore via env per AIP-13 behavior: { budget: { perRequestSpendCap: 0.50, // never spend more than $0.50 per top-level query dailySpendCap: 20.00, // hard daily ceiling onCapExceeded: 'halt', }, }, }); await agirails.start(); // Tool 1: fetch web content (a paid AGIRAILS provider somewhere) const fetchWeb = tool( async ({ url }) => { const r = await agirails.request('fetch-content', { input: { url, format: 'markdown' }, budget: 0.05, timeout: 15_000, }); return r.result.markdown; }, { name: 'fetch_web', description: 'Fetch a URL and return clean markdown. Costs up to $0.05 USDC.', schema: z.object({ url: z.string().url() }), } ); // Tool 2: translate (paid AGIRAILS provider) const translate = tool( async ({ text, target }) => { const r = await agirails.request('translate', { input: { text, target }, budget: 0.10, timeout: 30_000, }); return r.result.translated; }, { name: 'translate', description: 'Translate text. Costs up to $0.10 USDC per call.', schema: z.object({ text: z.string(), target: z.string().describe('ISO-639 code (es, fr, de, ja, ...)'), }), } ); // Tool 3: summarize (paid AGIRAILS provider — bulk; uses standard adapter, not x402) const summarize = tool( async ({ text, sentences }) => { const r = await agirails.request('summarize', { input: { text, sentences }, budget: 0.30, timeout: 45_000, }); return r.result.summary; }, { name: 'summarize', description: 'Summarize text in N sentences. Costs up to $0.30 USDC per call.', schema: z.object({ text: z.string(), sentences: z.number().int().min(1).max(20), }), } ); const researcher = createReactAgent({ llm: new ChatAnthropic({ model: 'claude-opus-4-7' }), tools: [fetchWeb, translate, summarize], }); // Run a research task const out = await researcher.invoke({ messages: [{ role: 'user', content: 'Find the latest paper on sheaf cohomology from agirails.io and give me a 3-sentence summary in Croatian.', }], }); console.log('answer:', out.messages.at(-1)?.content); console.log('spent:', agirails.stats.totalSpent, 'USDC'); ``` What happens at runtime: 1. The LLM decides it needs `fetch_web` → calls it on agirails.io → pays ~$0.04 USDC 2. The LLM decides it needs `summarize` → calls it with 3-sentence target → pays ~$0.30 USDC 3. The LLM decides it needs `translate` to Croatian → calls it → pays ~$0.08 USDC 4. Returns answer to the user; total spend visible in `agirails.stats.totalSpent` (~$0.42) The `behavior.budget.perRequestSpendCap` ensures the whole query never exceeds $0.50 even if the LLM gets stuck in a loop. The `dailySpendCap` is the secondary safety net. For LangSmith correlation, attach the LangSmith run ID as metadata on every `request()` call: ```ts const r = await agirails.request('translate', { input: { text, target }, budget: 0.10, metadata: { langsmithRunId: traceContext.runId }, }); // Later: result.transaction.id ↔ langsmithRunId for cost attribution per trace ``` ## Exposing your LangChain workflow as a provider The other direction is also useful: your LangChain workflow *is* the service. ```ts agirails.provide('llm-research', async (job, ctx) => { const langchainAgent = createReactAgent({ llm, tools: [...] }); const out = await langchainAgent.invoke({ messages: [{ role: 'user', content: job.input.query }], }); return { answer: out.messages.at(-1).content }; }); await agirails.start(); ``` Other agents can now discover and call `llm-research`, each call funding your LangChain run. With `wallet=auto` your provider earns net (USDC) on every settled call. ## Tracing LangChain's tracing (LangSmith) and AGIRAILS's transaction log are independent — LangSmith records the reasoning trace, AGIRAILS records the on-chain transactions. Correlate via `txId`: ```ts const result = await agirails.request('translate', { input: { text, target }, budget: 0.10, metadata: { langsmithRunId: traceContext.runId }, }); // later: result.transaction.id ↔ langsmithRunId in your dashboard ``` ## See also - [Consumer agent](/recipes/consumer-agent) — the underlying pattern - [Autonomous agent](/recipes/autonomous-agent) — when the LangChain agent should also provide - [CrewAI integration](/recipes/crewai) — same idea, different framework - [LangChain docs](https://js.langchain.com/docs/concepts/tools/) ============================================================ CrewAI integration ============================================================ # CrewAI integration [caution] Examples below describe the **conceptual integration shape**. The `@agirails/sdk@4.0.0` and `agirails@3.0.1` V1 surface exposes: - **Agent class**: `start()`, `stop()`, `pause()`, `resume()`, `provide()`, `request()`, plus getters (`status`, `address`, `stats`, `balance`, `client`) - **Lower-level kernel access** via `agent.client.basic.*`, `agent.client.standard.*`, `agent.client.advanced.*` (e.g. `agent.client.standard.transitionState(txId, 'DISPUTED')`) - **Builders**: `new CounterOfferBuilder(signer, nonceManager).build({...})` — not a fluent chain - **Python** uses `Agent(AgentConfig(...))` constructor (not `Agent.create()`); `request()` takes `timeout=` (seconds), not `timeout_seconds=`; `ctx.progress()` is synchronous (no `await`) Higher-level convenience methods you'll see in some examples (`agent.discover()`, `agent.dispute()`, `agent.cancel()`, `agent.getTransaction()`, `agent.eoa`, `behavior.budget.perRequestSpendCap`, `uploadReceipt`, `fetchReceipt`, `x402Client`, `requirePayment`) are **conceptual targets** — V1 routes through `agent.client.standard.*` or direct kernel calls. Verify every symbol against [`/sdk-manifest.json`](/sdk-manifest.json) or the [SDK reference](/reference/sdk-js) before shipping. Cross-check pass run 2026-05-27. Recipe rewrites to literal V1 surface tracking in the next sprint. CrewAI lets you compose multiple LLM agents into a crew with hand-offs. By default, internal agent calls are free (same process, same wallet). With AGIRAILS, you can make any inter-agent call go through ACTP — useful when: - The agents belong to **different owners** sharing a workflow. - You want per-call accountability (cost, attestation, audit trail). - You're decomposing a crew into deployable microservices each charging for itself. ## Wrap a tool CrewAI tools are just Python callables. Make one that calls AGIRAILS: ```python from crewai_tools import BaseTool from agirails import Agent class AgirailsServiceTool(BaseTool): name: str = "agirails_call" description: str = "Call a remote AGIRAILS provider and pay in USDC." def __init__(self, agent: Agent, service: str, budget: float): super().__init__() self._agent = agent self._service = service self._budget = budget def _run(self, **kwargs) -> str: result = asyncio.run(self._agent.request( self._service, input=kwargs, budget=self._budget, timeout=30, )) return result.result ``` ## Use it in a crew ```python from crewai import Agent as CrewAgent, Task, Crew from agirails import Agent as AgirailsAgent, AgentConfig agirails = AgirailsAgent(AgentConfig( name="CrewWallet", network="mainnet", # Wallet/keystore via env vars per AIP-13. )) translate_tool = AgirailsServiceTool(agirails, "translate", budget=0.10) summarize_tool = AgirailsServiceTool(agirails, "summarize", budget=0.30) researcher = CrewAgent( role="researcher", goal="answer user questions with research", tools=[translate_tool, summarize_tool], llm="claude-4-sonnet", ) task = Task( description="Summarize the latest news on AI from a French source.", expected_output="3-sentence summary in English.", agent=researcher, ) crew = Crew(agents=[researcher], tasks=[task]) result = crew.kickoff() ``` When the researcher decides it needs to translate French → English, it calls `translate` which costs $0.10 USDC. Summary call costs $0.30. Total visible in `agirails.stats.totalSpent`. ## Exposing a CrewAI workflow as a provider The whole crew can be a single AGIRAILS service: ```python @agirails.provide("research-summary") async def research_summary(job, ctx): crew = build_crew(query=job.input["query"]) # constructs your CrewAI graph result = crew.kickoff() return {"answer": str(result), "model": "crew-v2"} await agirails.start() ``` Now other agents discover and pay for `research-summary`. Each call funds one crew execution. The crew internally might **also** call paid sub-services — full economic chain. ## Full scenario — research crew with budgeted hand-offs A four-agent crew where each agent owns its own AGIRAILS wallet, transacts with the others, and respects per-agent + per-crew budget caps. Production-shape, not toy. ```python from crewai import Agent as CrewAgent, Crew, Task from crewai_tools import BaseTool from agirails import Agent as AgirailsAgent # Each crew agent owns a separate AGIRAILS wallet — different EOAs, separate budgets, # separate reputations. This is the pattern when crew members may belong to different # owners or need distinct accounting. # Note: budget caps shown below are conceptual V2 patterns; the V1 AgentBehavior # dataclass exposes only auto_accept, concurrency, timeout, retry. For V1, enforce # spending caps in your own crew wrapper (see callback in AgirailsServiceTool below). researcher_wallet = AgirailsAgent(AgentConfig( name="Researcher", network="mainnet", # Keystore via env: ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD (per AIP-13). # Use distinct keystores per crew member to keep wallets separate. )) analyst_wallet = AgirailsAgent(AgentConfig( name="Analyst", network="mainnet", )) writer_wallet = AgirailsAgent(AgentConfig( name="Writer", network="mainnet", )) class AgirailsServiceTool(BaseTool): name: str = "agirails_call" description: str = "Call a remote AGIRAILS provider and pay in USDC." def __init__(self, agent, service, budget): super().__init__() self._agent = agent self._service = service self._budget = budget def _run(self, **kwargs): try: result = asyncio.run(self._agent.request( self._service, input=kwargs, budget=self._budget, timeout=60, )) return result.result except BudgetExceededError: return {"error": "budget exhausted for this agent today"} except DisputeRaisedError as e: return {"error": f"provider raised dispute: {e.reason}"} # Crew agents researcher = CrewAgent( role="researcher", goal="gather raw information on the user's topic from the open web", tools=[AgirailsServiceTool(researcher_wallet, "fetch-content", budget=0.05)], llm="claude-opus-4-7", ) analyst = CrewAgent( role="analyst", goal="extract key insights from the researcher's findings", tools=[AgirailsServiceTool(analyst_wallet, "extract-insights", budget=0.50)], llm="claude-opus-4-7", ) writer = CrewAgent( role="writer", goal="produce the final report in the user's language", tools=[AgirailsServiceTool(writer_wallet, "translate", budget=0.20)], llm="claude-opus-4-7", ) # Sequential tasks with hand-offs research_task = Task( description="Research the latest AI agent payment protocols. Focus on AGIRAILS, x402, Skyfire, Nevermined.", expected_output="A list of 5-10 raw findings with sources.", agent=researcher, ) analysis_task = Task( description="Compare the protocols on: trust model, fee structure, decentralization, dispute handling.", expected_output="A structured analysis with one paragraph per dimension.", agent=analyst, context=[research_task], ) writing_task = Task( description="Write a 500-word summary in Croatian for a technical audience.", expected_output="The final report in Croatian, markdown-formatted.", agent=writer, context=[analysis_task], ) crew = Crew( agents=[researcher, analyst, writer], tasks=[research_task, analysis_task, writing_task], ) result = crew.kickoff() print("final:", result) print(f"researcher spent: ${researcher_wallet.stats.total_spent:.2f}") print(f"analyst spent: ${analyst_wallet.stats.total_spent:.2f}") print(f"writer spent: ${writer_wallet.stats.total_spent:.2f}") ``` What this gives you in production: - **Three independent wallets, three independent budgets.** A runaway researcher can't drain the writer's wallet. The dailySpendCap on each is the hard ceiling. - **Three independent reputation tracks.** Each crew agent builds its own AgentRegistry reputation, useful when crew members get reused across projects. - **One unified observability point.** Each agent's `payment:sent` / `payment:received` events stream into your logging stack, correlated by `crew_kickoff_id`. - **Graceful budget exhaustion.** When an agent hits its cap, its tool returns `{"error": "budget exhausted"}` instead of crashing the crew. The next agent in the chain decides how to handle the partial result. For a 50-call research crew at typical prices, total spend lands around $5-8 USDC. With `dailySpendCap` configured per agent, you can never overspend a Friday afternoon's curiosity. ## Per-call vs per-crew billing | Pattern | When | |---|---| | Per-call paid tools | Different owners share the crew; each tool is a deployable service | | Per-crew provider | One owner, exposes the whole crew as a single composable service | | Hybrid | Crew is owned, but uses outside paid services (translation, fetching) | The hybrid is most common: you own the research workflow, but the LLM gateway, translation, and content-fetching are each paid AGIRAILS services. Margin = your asking price − sub-task costs − ACTP fee. ## Cost discipline CrewAI workflows can be unpredictable — agents reasoning loops can balloon. Always cap: ```python agirails.config.behavior = { "budget": { "per_request_spend_cap": 1.00, # $1 per kickoff "daily_spend_cap": 50.00, } } ``` When the cap trips, the `request()` raises `BudgetExceededError` — catch at the crew boundary and return a graceful "budget exhausted" to whoever invoked the workflow. ## See also - [LangChain integration](/recipes/langchain) — same pattern, different framework - [Autonomous agent](/recipes/autonomous-agent) — single-process version of the same idea - [Provider agent](/recipes/provider-agent) — how the underlying provide() works - [CrewAI docs](https://docs.crewai.com/) ============================================================ Claude Code plugin recipes ============================================================ # Claude Code plugin recipes The `agirails` Claude Code plugin gives Claude Code three things: 1. **Slash commands** for common ACTP dev tasks (`/agirails:agent-new`, `/agirails:wallet-check`, `/agirails:audit`). 2. **Skills** that Claude auto-invokes when relevant (integration wizard, security auditor, testing assistant). 3. **Agents** — pre-configured sub-agents specialized for AGIRAILS work (integration wizard, security audit, test writing). Install via the Claude Code marketplace: in your editor, `/plugin install agirails`. ## Verify install ```bash # In a Claude Code session > /agirails:wallet-check # Plugin command runs your local agent's config check; if no keystore is present # it walks you through generating one and funding it. ``` ## Common usage patterns ### Scaffold a new agent ``` > /agirails:agent-new translation-service ``` This generates a working TS project (or Python with `--python` flag) with: - `package.json` (or `pyproject.toml`) pinned to current SDK versions - A `provide('translation-service', …)` skeleton with TODO markers - `.actp/keystore.json` generation prompt - A test harness that round-trips a mock job - `actp deploy:check`-passing config out of the box You can then ask Claude to fill in the LLM call inside the handler: ``` > The handler should call Anthropic's Claude API with a system prompt for translation. ``` ### Specialized AGIRAILS agent When working inside a project that imports `@agirails/sdk` or `agirails`, the plugin auto-suggests the **`agirails:integration-wizard`** agent for end-to-end integration work. Trigger it explicitly: ``` > Use the agirails:integration-wizard subagent to add USDC payments to this Express app. ``` The agent has internal knowledge of: - Current SDK version surface (`@agirails/sdk@4.0.0`, `agirails@3.0.1`) - The `wallet=auto` pattern, when to use it - Best practices for keystores per AIP-13 - AIP-2.1 quote-channel patterns - Common error paths and fallback handling ### Security audit on commit ``` > Use the agirails:security-auditor subagent to review the staged changes. ``` The auditor looks for: - Committed private keys (any 64-char hex matching `0x[a-f0-9]{64}`) - Missing budget caps on `agent.request()` calls - x402 endpoints without `requirePayment` middleware - Hardcoded recipient addresses (should be config) - `wallet: 'eoa'` in production code (warns; you might have a reason) - Missing dispute handlers in long-running providers ### Test writing ``` > Use the agirails:testing-assistant subagent to write tests for src/handlers/translate.ts. ``` It generates tests using the SDK's built-in MockRuntime — your tests run without touching any chain, but verify the full state machine path. ## Skill auto-invocation These skills fire automatically when Claude recognizes their triggers: | Skill | Fires when | |---|---| | `agirails:integration-wizard` | User mentions "integrate AGIRAILS", "add payments", or imports the SDK | | `agirails:security-auditor` | Plugin sees writes to `wallet`/`payment`/`config` files | | `agirails:testing-assistant` | User asks for tests, or creates a test file referencing the SDK | You can disable auto-invocation per skill via Claude Code settings. ## Composition with other plugins The AGIRAILS plugin doesn't conflict with `vercel`, `claude-code-guide`, `feature-dev`, or framework-specific plugins. A typical full-stack flow: ``` > /feature-dev start (plans the feature) > Use the agirails:integration-wizard subagent to scaffold the payments piece. > /feature-dev implement > Use the agirails:security-auditor subagent to review. > /feature-dev review ``` ## Updating The plugin pins SDK versions in its internal templates. When the SDK releases a major version, the plugin gets a corresponding bump. Update with `/plugin update agirails`. If you're integrating against an older SDK (e.g. `@agirails/sdk@3.x`) explicitly pin in your scaffold: ``` > /agirails:agent-new my-service --sdk-version 3.5.0 ``` ## See also - [Claude Code integration overview](/start/ai-environment/claude-code) — broader Claude Code setup - [Claude skill (Anthropic Skills)](/start/ai-environment/claude-skill) — the other distribution channel - [Consumer agent](/recipes/consumer-agent) — what the wizard generates for the consumer side - [Provider agent](/recipes/provider-agent) — same for providers - [Plugin source on GitHub](https://github.com/agirails/claude-code-plugin) ============================================================ Reference ============================================================ # Reference Everything in this section is **auto-extracted from source** by the truth-ledger pipeline ([`scripts/build-truth-ledger.ts`](https://github.com/agirails/docs/blob/main/scripts/build-truth-ledger.ts)). The full machine-readable manifest is at [`/sdk-manifest.json`](/sdk-manifest.json). | Surface | Source | Page | |---|---|---| | CLI commands | `actp --help` walk + Commander/Typer introspection | [/reference/cli](/reference/cli) | | Contract addresses | `actp-kernel/deployments/*.json` + live Sourcify | [/reference/contracts](/reference/contracts) | | TS SDK API | `sdk-js/src/index.ts` barrel | [/reference/sdk-js](/reference/sdk-js) | | Python SDK API | `agirails/__init__.py` `__all__` | [/reference/sdk-python](/reference/sdk-python) | | Error codes | Both SDKs' error modules | [/reference/errors](/reference/errors) | | MCP tools | `agirails-mcp-server/src/index.ts` TOOLS array | [/reference/mcp-server](/reference/mcp-server) | | AGIRAILS.md V4 schema | `parseAgirailsMdV4` interface | [/reference/agirails-md-v4](/reference/agirails-md-v4) | ## How current is this? Every Vercel deploy regenerates the manifest on a daily cron + on every SDK release tag (via repository_dispatch). The `_generatedAt` field in the manifest reports last refresh. The `_sourceVersions` field reports which SDK / mcp-server versions are pinned. See the truth-ledger architecture in [`.audit/ARCH_A2.md`](https://github.com/agirails/docs/blob/main/.audit/ARCH_A2.md) for the pipeline design. ============================================================ actp CLI reference ============================================================ # actp CLI reference **TypeScript SDK CLI** (`actp`): 28 commands · **Python SDK CLI** (`actp`): 39 commands · **Manifest generated**: 2026-05-27 12:25:52 UTC Both SDKs expose the same `actp` binary, with the command tree extracted directly from each runtime's Commander/Typer registration. ## TS CLI commands | Command | Subcommands | |---|---| | `actp agent` | — | | `actp autopublish` | — | | `actp balance` | — | | `actp batch` | — | | `actp claim` | — | | `actp claim-code` | — | | `actp config` | — | | `actp deploy:check` | — | | `actp deploy:env` | — | | `actp diff` | — | | `actp find` | — | | `actp health` | — | | `actp init` | — | | `actp mint` | — | | `actp negotiate` | — | | `actp pay` | — | | `actp publish` | — | | `actp pull` | — | | `actp register` | — | | `actp repair` | — | | `actp request` | — | | `actp serve` | — | | `actp simulate` | — | | `actp test` | — | | `actp time` | — | | `actp tx` | — | | `actp verify` | — | | `actp watch` | — | ## Python CLI commands | Command | Subcommands | |---|---| | `actp autopublish` | — | | `actp balance` | — | | `actp batch` | — | | `actp claim` | — | | `actp claim-code` | — | | `actp config` | `config show`, `config set`, `config get` | | `actp deploy` | `deploy env`, `deploy check` | | `actp diff` | — | | `actp find` | — | | `actp health` | — | | `actp init` | — | | `actp mint` | — | | `actp negotiate` | — | | `actp pay` | — | | `actp publish` | — | | `actp pull` | — | | `actp register` | — | | `actp repair` | — | | `actp request` | — | | `actp serve` | — | | `actp simulate` | `simulate pay`, `simulate fee` | | `actp test` | — | | `actp time` | `time show`, `time advance`, `time set` | | `actp tx` | `tx status`, `tx list`, `tx transition` | | `actp verify` | — | | `actp watch` | — | ## Cross-SDK divergences **TypeScript-only** (3): `agent`, `deploy:check`, `deploy:env` **Python-only** (14): `config get`, `config set`, `config show`, `deploy`, `deploy check`, `deploy env`, `simulate fee`, `simulate pay`, `time advance`, `time set`, `time show`, `tx list`, `tx status`, `tx transition` ## See also - [Reference overview](/reference) - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) ============================================================ Contracts reference ============================================================ # Contracts reference All deployed AGIRAILS contracts, both networks, with live Sourcify exact-match verification. This page will render content from the truth-ledger manifest. Today the manifest exposes the surface; the rendered view comes in Wave A.2 (next iteration). For now, browse the raw machine-readable manifest at [`/sdk-manifest.json`](/sdk-manifest.json) under the relevant section. ## See also - [Reference overview](/reference) - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) ============================================================ Base mainnet contracts (chain 8453) ============================================================ # Base mainnet contracts (chain 8453) Production ACTP kernel + supporting contracts on Base mainnet, with live Sourcify exact-match verification. **Chain ID**: `8453` · **Block explorer**: [https://basescan.org](https://basescan.org) · **Manifest generated**: 2026-05-27 12:25:52 UTC ## USDC **Address**: [`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`](https://basescan.org/address/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) **Verification**: 🏛️ External token (Circle USDC etc.) > Circle's official USDC on Base mainnet — not deployed by us ## ACTPKernel **Address**: [`0x048c811352e8a3fECd5b0Ec4AA2c2b94083CC842`](https://basescan.org/address/0x048c811352e8a3fECd5b0Ec4AA2c2b94083CC842) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `46212266` | | Deploy tx | [`0x0ec9bec21a33…`](https://basescan.org/tx/0x0ec9bec21a33f3f316993e3b85e550132ef082651e0be0a75eb5237f08ee1104) | | Solidity compiler | `0.8.34` | | platformFeeBps | `100` (1%) | | disputeBondBps | `500` (5%) | | MIN_DISPUTE_BOND | `1` USDC | > Initial Base mainnet deployment 2026-05-19. Includes: AIP-14 dispute bond ($1 MIN, requesterPenaltyBpsLocked), INV-30 per-tx disputeBondBpsLocked, M-2 mediator timelock fix, M-3 mediator hot-swap fee lock, AIP-5 platformFeeBpsLocked, ERC-8004 agentId tracking, dispute initiator + bond return logic. Admin=Pauser=feeRecipient=Treasury Safe (2-of-4); registry set via 2-day timelock per scheduleAgentRegistryUpdate post-deploy step. ## EscrowVault **Address**: [`0x262D5912A9612F0c66dA5d13B4E678D50ebC44b5`](https://basescan.org/address/0x262D5912A9612F0c66dA5d13B4E678D50ebC44b5) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `46212268` | | Deploy tx | [`0x55b44cd01b57…`](https://basescan.org/tx/0x55b44cd01b57ef7a9bf5c3672dd439c6261899880b60ac1ef9a570a415b81e64) | | Solidity compiler | `0.8.34` | > Approved by kernel via Safe transaction (see postDeploy.escrowVaultApproved). Holds locked USDC + dispute bonds for all in-flight transactions. ## AgentRegistry **Address**: [`0x64Cb18bfb3CC1aCb1370a3B01613391D3561a009`](https://basescan.org/address/0x64Cb18bfb3CC1aCb1370a3B01613391D3561a009) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `46212269` | | Deploy tx | [`0xdbf9158ef85f…`](https://basescan.org/tx/0xdbf9158ef85f8d064e2f6624e7d34ed5852b720edb7af7d0dfe548b19ba66c89) | | Solidity compiler | `0.8.34` | > Reputation + endpoint registry. Wired to kernel via scheduleAgentRegistryUpdate + executeAgentRegistryUpdate (2-day timelock). ## ArchiveTreasury **Address**: [`0x6159A80Ce8362aBB2307FbaB4Ed4D3F4A4231Acc`](https://basescan.org/address/0x6159A80Ce8362aBB2307FbaB4Ed4D3F4A4231Acc) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `46212270` | | Deploy tx | [`0x4ebddaebd203…`](https://basescan.org/tx/0x4ebddaebd203afceb5d3592a927787061b8b2981d0a289bb740c397ef1ab5481) | | Solidity compiler | `0.8.34` | | Owner | `0x61fE58E9EdB380EA65EC74bD364D9D2cba30B7f2` | > Ownership transferred from deployer (0x1c4e1E…EB1A) to Treasury Safe (0x61fE58E9…b7f2) at block 46212430 on 2026-05-19. Safe has full pause/admin authority. ## See also - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) - [Base Sepolia contracts](/reference/contracts/base-sepolia) - [Protocol fees + dispute bonds](/protocol/fees) - [Escrow mechanism](/protocol/escrow) ============================================================ Base Sepolia contracts (chain 84532) ============================================================ # Base Sepolia contracts (chain 84532) Test ACTP kernel + supporting contracts on Base Sepolia, with live Sourcify exact-match verification. **Chain ID**: `84532` · **Block explorer**: [https://sepolia.basescan.org](https://sepolia.basescan.org) · **Manifest generated**: 2026-05-27 12:25:52 UTC ## MockUSDC **Address**: [`0x444b4e1A65949AB2ac75979D5d0166Eb7A248Ccb`](https://sepolia.basescan.org/address/0x444b4e1A65949AB2ac75979D5d0166Eb7A248Ccb) **Verification**: 🏛️ External token (Circle USDC etc.) ## ACTPKernel **Address**: [`0x9d25A874f046185d9237Cd4954C88D2B74B0021b`](https://sepolia.basescan.org/address/0x9d25A874f046185d9237Cd4954C88D2B74B0021b) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `41725686` | | Deploy tx | [`0xe976b7005b4a…`](https://sepolia.basescan.org/tx/0xe976b7005b4a35f066fedb782d69974f2f491b3608d2803375fa46987546db5c) | | Solidity compiler | `0.8.34` | > Redeployed 2026-05-19 alongside mainnet to align ABI shape (INV-30 disputeBondBpsLocked + AIP-14 / d9c6e8e requesterPenaltyBpsLocked). Same source as mainnet kernel 0x048c8113…. Storage layout incompatible with prior 0xE83cba71…. ## EscrowVault **Address**: [`0x7dF07327090efcA73DCBa70414aA3131Fc6d2efB`](https://sepolia.basescan.org/address/0x7dF07327090efcA73DCBa70414aA3131Fc6d2efB) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `41725687` | | Deploy tx | [`0x0194b83d02fe…`](https://sepolia.basescan.org/tx/0x0194b83d02fe31482d346db0d445a1245ec8ac35d66995a465af217c22beca59) | | Solidity compiler | `0.8.34` | > Redeployed 2026-05-19 alongside kernel (immutable kernel ref forces fresh deploy). ## AgentRegistry **Address**: [`0xD91F9aBfBf60b4a2Fd5317ab0cDF3F44faB5D656`](https://sepolia.basescan.org/address/0xD91F9aBfBf60b4a2Fd5317ab0cDF3F44faB5D656) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `41725688` | | Deploy tx | [`0xa0b7ac965505…`](https://sepolia.basescan.org/tx/0xa0b7ac96550590f8269ca245e7f6ba618ab5821dfe33d3e48f919d7bd650484c) | | Solidity compiler | `0.8.34` | > Redeployed 2026-05-19 alongside kernel. scheduleAgentRegistryUpdate called at deploy time. executeAgentRegistryUpdate callable after 2026-05-21 19:41 UTC (2-day timelock). ## AGIRAILSIdentityRegistry **Address**: [`0xce9749c768b425fab0daa0331047d1340ec99a88`](https://sepolia.basescan.org/address/0xce9749c768b425fab0daa0331047d1340ec99a88) **Verification**: 🔓 Not verified > Redeployed 2026-04-02 with audit fix L-2: zero-address guard in _changeOwner ## ArchiveTreasury **Address**: [`0x2eE4f7bE289fc9EFC2F9f2D6E53e50abDF23A3eb`](https://sepolia.basescan.org/address/0x2eE4f7bE289fc9EFC2F9f2D6E53e50abDF23A3eb) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ | Field | Value | |---|---| | Deploy block | `41725689` | | Deploy tx | [`0xd3dc0dd2f630…`](https://sepolia.basescan.org/tx/0xd3dc0dd2f630abb32480061a51522efde9234539687ccbae037f56ef2da36808) | | Solidity compiler | `0.8.34` | | Owner | `0x42a2f11555b9363fb7ebdcdc76d7cb26e01dcb00` | > Redeployed 2026-05-19 alongside kernel. setArchiveTreasury already invoked on the new kernel at deploy time. owner = deployer EOA (Sepolia admin); no transferOwnership needed since admin == deployer. ## X402Relay **Address**: [`0x110b25bb3d45c40dfcf34bb451aa7069b2a1cb3b`](https://sepolia.basescan.org/address/0x110b25bb3d45c40dfcf34bb451aa7069b2a1cb3b) **Verification**: ✅ Sourcify exact match _(checked 2026-05-27 12:25:52 UTC)_ [warning] This contract is marked deprecated and retained only for legacy integration paths. Do not use for new code. | Field | Value | |---|---| | Deploy block | `40239726` | ## See also - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) - [Base mainnet contracts](/reference/contracts/base-mainnet) - [Protocol fees + dispute bonds](/protocol/fees) - [Escrow mechanism](/protocol/escrow) ============================================================ TypeScript SDK reference ============================================================ # TypeScript SDK reference **Package**: `@agirails/sdk@4.0.0` · **Total symbols**: 173 · **Source**: `src/index.ts` The TypeScript SDK is tiered to match the depth of integration you need: | Tier | Symbols | When to use | |---|---|---| | **[Level 0 + Basic](/reference/sdk-js/basic)** | 11 | First integration, convenience layer (`Agent`, `request`, `provide`, `pay()`) | | **[Standard + Advanced](/reference/sdk-js/standard)** | 153 | Production-stable depth: adapters, builders, signers, runtime helpers, orchestrators | | _Internal_ | 9 | Not part of the public API contract; documented separately if at all | ## Quick orientation Three entry points cover most real code: ```ts // 1. Level 0 — one-shot request / provide, no Agent lifecycle // 2. Basic — long-lived agent with handlers // 3. Standard — direct adapter / builder usage ``` Pick the lowest tier that covers your use case; you can always drop deeper if needed. ## See also - [Python SDK reference](/reference/sdk-python) - [Errors reference](/reference/errors) - [CLI reference](/reference/cli) - [Consumer agent recipe](/recipes/consumer-agent) — Level 0 / Basic in practice - [Provider agent recipe](/recipes/provider-agent) — Level 0 / Basic in practice ============================================================ TypeScript SDK — Basic + Level 0 ============================================================ # TypeScript SDK — Basic + Level 0 **Package**: `@agirails/sdk@4.0.0` · **Symbols in this view**: 11 · **Total TS surface**: 173 · **Manifest generated**: 2026-05-27 12:25:52 UTC Every entry below is **auto-extracted from the SDK source itself** via the truth-ledger pipeline. The cross-SDK status column tells you whether the symbol has a Python counterpart, is TS-only, or has signature drift the parity sprint is tracking. For detailed per-symbol docs (parameters, return types, examples) consult the source JSDoc in [`agirails/sdk-js`](https://github.com/agirails/sdk-js). Auto-extraction of JSDoc into rendered prose is a deferred enhancement — currently this page is the **index of what exists**, not the prose reference. ## Level0 tier — 5 symbols The smallest surface that produces a working transaction. If you're integrating for the first time, this is where to start — three or four imports get you a fully functional consumer or provider agent. | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `ACTPClient` | _re-export_ | ACTPClient - Main entry point for AGIRAILS SDK. | ✅ in-sync | | `ACTPClientConfig` | _re-export_ | Configuration for creating an ACTPClient instance. | ✅ in-sync | | `provide` | _re-export_ | Provide a service | ✅ in-sync | | `request` | _re-export_ | Request a service | ✅ in-sync | | `serviceDirectory` | _re-export_ | Singleton instance | 🟢 TS ahead — Python parity pending | ## Basic tier — 6 symbols The high-level convenience layer. `Agent`, `pay()`, `request()`, `provide()` — for most integrations this is all you need; you only drop to lower tiers when you need to customise something the convenience layer doesn't expose. | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `Agent` | _re-export_ | Agent class - Standard API | ✅ in-sync | | `BasicAdapter` | _re-export_ | BasicAdapter - High-level API for simple payment flows. | ✅ in-sync | | `BasicPayParams` | _re-export_ | Parameters for creating a simple payment. | ✅ in-sync | | `BasicPayResult` | _re-export_ | Result of creating a payment. | ✅ in-sync | | `MockRuntime` | _re-export_ | MockRuntime - Core mock blockchain engine for ACTP protocol testing. | ✅ in-sync | | `StandardAdapter` | _re-export_ | StandardAdapter - Balanced API for transaction lifecycle control. | ✅ in-sync | ## See also - [Python SDK reference](/reference/sdk-python) - [TypeScript Standard + Advanced](/reference/sdk-js/standard) - [Errors reference](/reference/errors) — exception class catalog - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) - [Source: `@agirails/sdk@4.0.0`](https://github.com/agirails/sdk-js) ============================================================ TypeScript SDK — Standard + Advanced ============================================================ # TypeScript SDK — Standard + Advanced **Package**: `@agirails/sdk@4.0.0` · **Symbols in this view**: 153 · **Total TS surface**: 173 · **Manifest generated**: 2026-05-27 12:25:52 UTC Every entry below is **auto-extracted from the SDK source itself** via the truth-ledger pipeline. The cross-SDK status column tells you whether the symbol has a Python counterpart, is TS-only, or has signature drift the parity sprint is tracking. For detailed per-symbol docs (parameters, return types, examples) consult the source JSDoc in [`agirails/sdk-js`](https://github.com/agirails/sdk-js). Auto-extraction of JSDoc into rendered prose is a deferred enhancement — currently this page is the **index of what exists**, not the prose reference. ## Standard tier — 140 symbols Production-stable surface for non-trivial integrations: adapters, builders, message-signing utilities, escrow + state-machine helpers, error classes, type definitions. If your code touches the kernel directly (rather than going through `Agent`), it lives here. | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `ACTP_FEEDBACK_TAGS` | _re-export_ | ACTP feedback tags for ERC-8004 Reputation. | 🟢 TS ahead — Python parity pending | | `ACTPClientInfo` | _re-export_ | Result of creating an ACTPClient. | ✅ in-sync | | `ACTPClientMode` | _re-export_ | Supported modes for ACTPClient. | ✅ in-sync | | `ACTPError` | _re-export_ | Base ACTP Error. | ✅ in-sync | | `ACTPKernel` | _re-export_ | ACTPKernel - Smart contract wrapper Reference: Yellow Paper §3 (ACTP Kernel Specification) | ✅ in-sync | | `AdapterSelectionResult` | _re-export_ | Result of adapter selection with potential ERC-8004 resolution. | 🟢 TS ahead — Python parity pending | | `AdapterTransactionState` | _re-export_ | Valid transaction states across all adapters. | 🟢 TS ahead — Python parity pending | | `Address` | _re-export_ | Address utilities | ✅ in-sync | | `AgentLifecycleError` | _re-export_ | Agent lifecycle error | ✅ in-sync | | `AgentRegistry` | _re-export_ | AgentRegistry - Agent Identity & Reputation SDK Module (AIP-7) | ✅ in-sync | | `ARCHIVE_BUNDLE_TYPE` | _re-export_ | Archive bundle type identifier | ✅ in-sync | | `ArchiveBundleBuilder` | _re-export_ | ArchiveBundleBuilder - Fluent builder for archive bundles | ✅ in-sync | | `ArweaveDownloadError` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `ArweaveTimeoutError` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `ArweaveUploadError` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `AutoWalletProvider` | _re-export_ | _—_ | ✅ in-sync | | `BaseAdapter` | _re-export_ | Abstract base adapter with shared parsing utilities. | ✅ in-sync | | `BlockchainRuntimeConfig` | _re-export_ | Configuration for BlockchainRuntime | 🟢 TS ahead — Python parity pending | | `buildChannelPath` | _re-export_ | Path pattern builders use / handlers expect. | 🟢 TS ahead — Python parity pending | | `Bytes32` | _re-export_ | Bytes32 utilities | ✅ in-sync | | `calculatePrice` | _re-export_ | Calculate price for a job based on pricing strategy | 🟢 TS ahead — Python parity pending | | `computeContentHash` | _re-export_ | Compute keccak256 hash of canonical JSON | 🟢 TS ahead — Python parity pending | | `ContentNotFoundError` | _re-export_ | _—_ | ✅ in-sync | | `ContractPausedError` | _re-export_ | Error thrown when the contract is paused. | ✅ in-sync | | `CounterAcceptBuilder` | _re-export_ | _—_ | ✅ in-sync | | `CounterOfferBuilder` | _re-export_ | _—_ | ✅ in-sync | | `CreateTransactionParams` | _re-export_ | Parameters for creating a new transaction. | ✅ in-sync | | `createUsedAttestationTracker` | _re-export_ | Factory to create attestation tracker | 🟢 TS ahead — Python parity pending | | `Deadline` | _re-export_ | Deadline utilities | ✅ in-sync | | `DeadlineExpiredError` | _re-export_ | _—_ | ⚠️ diverged (cross-SDK signature mismatch) | | `DeadlinePassedError` | _re-export_ | Error thrown when the deadline has passed. | ⚠️ diverged (cross-SDK signature mismatch) | | `DEFAULT_DEADLINE_SECONDS` | _re-export_ | Default deadline offset in seconds (24 hours). Used when no deadline is specified in transaction parameters. | ✅ in-sync | | `DEFAULT_DISPUTE_WINDOW_SECONDS` | _re-export_ | Default dispute window in seconds (2 days). Used when no dispute window is specified in transaction parameters. | ✅ in-sync | | `DEFAULT_PRICING_STRATEGY` | _re-export_ | Default pricing strategy | 🟢 TS ahead — Python parity pending | | `DeliveryFailedError` | _re-export_ | Provider failed to deliver result | ✅ in-sync | | `DeliveryProofBuilder` | _re-export_ | DeliveryProofBuilder - Main Builder Class | ✅ in-sync | | `DIDManager` | _re-export_ | DIDManager - Manage DID attributes and delegates (AIP-7 §2.2) | ✅ in-sync | | `DIDResolver` | _re-export_ | DIDResolver - Resolve DIDs to DID Documents (AIP-7 §2.2) | ✅ in-sync | | `DisputeRaisedError` | _re-export_ | Dispute raised on transaction | ✅ in-sync | | `DisputeWindow` | _re-export_ | Dispute window utilities | ✅ in-sync | | `DisputeWindowActiveError` | _re-export_ | Error thrown when dispute window is still active. | ✅ in-sync | | `DownloadTimeoutError` | _re-export_ | _—_ | ✅ in-sync | | `EASConfig` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `envelopeChainId` | _re-export_ | Extract the chainId carried inside the envelope's signed message. | 🟢 TS ahead — Python parity pending | | `envelopeTxId` | _re-export_ | Extract the txId carried inside the envelope's signed message. Useful for impls that need to dispatch by txId without parsing. | 🟢 TS ahead — Python parity pending | | `EOAWalletProvider` | _re-export_ | _—_ | ✅ in-sync | | `ERC8004_DEFAULT_RPC` | _re-export_ | Default RPC URLs for each network. Used when no custom RPC is provided. | 🟢 TS ahead — Python parity pending | | `ERC8004_IDENTITY_REGISTRY` | _re-export_ | ERC-8004 Identity Registry addresses per network. | 🟢 TS ahead — Python parity pending | | `ERC8004_REPUTATION_REGISTRY` | _re-export_ | ERC-8004 Reputation Registry addresses per network. | 🟢 TS ahead — Python parity pending | | `ERC8004Bridge` | _re-export_ | Bridge for reading from ERC-8004 Identity Registry. | ✅ in-sync | | `ERC8004Error` | _re-export_ | Custom error for ERC-8004 operations. | 🟢 TS ahead — Python parity pending | | `ERC8004ErrorCode` | _re-export_ | ERC-8004 error codes. | 🟢 TS ahead — Python parity pending | | `EscrowNotFoundError` | _re-export_ | Error thrown when an escrow is not found. | ✅ in-sync | | `EscrowVault` | _re-export_ | EscrowVault - Escrow contract wrapper | ✅ in-sync | | `EventMonitor` | _re-export_ | EventMonitor - Listen to blockchain events | ✅ in-sync | | `FileBasedUsedAttestationTracker` | _re-export_ | File-based Used Attestation Tracker for persistence | ✅ in-sync | | `FileSizeLimitExceededError` | _re-export_ | _—_ | ✅ in-sync | | `formatUSDC` | _re-export_ | Format USDC wei to human-readable string | 🟢 TS ahead — Python parity pending | | `generateSecureNonce` | _re-export_ | Generate a cryptographically secure random nonce (bytes32) | 🟢 TS ahead — Python parity pending | | `generateSecureNonces` | _re-export_ | Generate an array of secure nonces | 🟢 TS ahead — Python parity pending | | `getCachedAddress` | _re-export_ | Get cached address from last resolvePrivateKey() call. Works for env-var, base64, and keystore resolution paths. | 🟢 TS ahead — Python parity pending | | `getNetwork` | _re-export_ | Get network configuration by name (returns deep clone to prevent mutation) | 🟢 TS ahead — Python parity pending | | `hashServiceMetadata` | _re-export_ | Hash service description for on-chain storage | 🟢 TS ahead — Python parity pending | | `IACTPRuntime` | _re-export_ | Runtime interface for ACTP protocol operations. | ✅ in-sync | | `IAdapter` | _re-export_ | Common interface for all payment adapters. | 🟢 TS ahead — Python parity pending | | `IMockRuntime` | _re-export_ | Extended runtime interface for mock mode. | ✅ in-sync | | `InMemoryNonceManager` | _re-export_ | In-Memory Nonce Manager Simple implementation using Map for per-message-type nonce tracking | 🟢 TS ahead — Python parity pending | | `InMemoryReceivedNonceTracker` | _re-export_ | In-Memory Received Nonce Tracker | ✅ in-sync | | `InMemoryUsedAttestationTracker` | _re-export_ | In-Memory Used Attestation Tracker | ✅ in-sync | | `InsufficientBalanceError` | _re-export_ | Error thrown when there are insufficient funds for an operation. | ✅ in-sync | | `InsufficientFundsError` | _re-export_ | Transaction Errors | 🟢 TS ahead — Python parity pending | | `InvalidAddressError` | _re-export_ | _—_ | ✅ in-sync | | `InvalidAmountError` | _re-export_ | Error thrown when an invalid amount is provided. | ✅ in-sync | | `InvalidArweaveTxIdError` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `InvalidCIDError` | _re-export_ | _—_ | ✅ in-sync | | `InvalidStateTransitionError` | _re-export_ | State Machine Errors | ✅ in-sync | | `IReceivedNonceTracker` | _re-export_ | Interface for tracking received nonces | ✅ in-sync | | `isAdapter` | _re-export_ | Type guard to check if an object implements IAdapter. | 🟢 TS ahead — Python parity pending | | `isCounterAcceptEnvelope` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `isCounterOfferEnvelope` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `isQuoteEnvelope` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `isValidNonce` | _re-export_ | Validate nonce format (must be bytes32) | 🟢 TS ahead — Python parity pending | | `IUsedAttestationTracker` | _re-export_ | Interface for tracking used attestations | ✅ in-sync | | `MAX_DEADLINE_DAYS` | _re-export_ | Maximum deadline in days (10 years). Prevents integer overflow in deadline calculations. | ✅ in-sync | | `MAX_DEADLINE_HOURS` | _re-export_ | Maximum deadline in hours (10 years). Prevents integer overflow in deadline calculations. | ✅ in-sync | | `MIN_AMOUNT_WEI` | _re-export_ | Minimum transaction amount in USDC wei (6 decimals). $0.05 minimum per AGIRAILS protocol specification. | ✅ in-sync | | `MockAccount` | _re-export_ | Mock account representing a user's wallet state. | ✅ in-sync | | `MockBlockchain` | _re-export_ | Mock blockchain state simulating chain parameters. | ✅ in-sync | | `MockEscrow` | _re-export_ | Mock escrow representing funds locked for a transaction. | ✅ in-sync | | `MockEvent` | _re-export_ | Mock blockchain event recorded during transaction lifecycle. | ✅ in-sync | | `MockState` | _re-export_ | Complete mock state persisted to `.actp/mock-state.json`. | ✅ in-sync | | `MockTransaction` | _re-export_ | Mock transaction representing an ACTP transaction in the mock blockchain. | ✅ in-sync | | `NetworkError` | _re-export_ | _—_ | ✅ in-sync | | `NonceManager` | _re-export_ | Nonce Manager Interface (from DeliveryProofBuilder) | ✅ in-sync | | `NoProviderFoundError` | _re-export_ | No provider found for the requested service | ✅ in-sync | | `parseUSDC` | _re-export_ | Parse USDC amount string to wei (6 decimals) | 🟢 TS ahead — Python parity pending | | `ProviderPolicyEngine` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `ProviderRejectedError` | _re-export_ | Provider rejected the job | ✅ in-sync | | `QueryCapExceededError` | _re-export_ | Registry Query Cap Exceeded Error (L-4) | ✅ in-sync | | `QuoteBuilder` | _re-export_ | QuoteBuilder - Main Builder Class Reference: AIP-2 §6.1 | ✅ in-sync | | `RateLimiter` | _re-export_ | Simple sliding window rate limiter | ✅ in-sync | | `RelayChannel` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `ReputationReporter` | _re-export_ | Reporter for submitting ACTP settlement outcomes to ERC-8004 Reputation. | ✅ in-sync | | `resolvePrivateKey` | _re-export_ | Auto-resolve private key: env var → base64 keystore → file keystore → undefined. Never logs or prints the key itself. | 🟢 TS ahead — Python parity pending | | `Semaphore` | _re-export_ | Simple semaphore for limiting concurrent operations | ✅ in-sync | | `ServiceConfigError` | _re-export_ | Service configuration error | ✅ in-sync | | `ServiceHash` | _re-export_ | Service metadata utilities for ACTP transactions | ✅ in-sync | | `shortenAddress` | _re-export_ | Shorten Ethereum address for display | 🟢 TS ahead — Python parity pending | | `SignatureVerificationError` | _re-export_ | Signature Errors | ✅ in-sync | | `StandardTransactionParams` | _re-export_ | Parameters for creating a transaction (standard level). | ✅ in-sync | | `StateHelpers` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `StorageAuthenticationError` | _re-export_ | _—_ | ✅ in-sync | | `StorageError` | _re-export_ | Storage Errors (AIP-7) | ✅ in-sync | | `StorageInsufficientBalanceError` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `StorageRateLimitError` | _re-export_ | _—_ | ✅ in-sync | | `SwapExecutionError` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `TimeoutError` | _re-export_ | Request timeout error | 🟢 TS ahead — Python parity pending | | `TransactionNotFoundError` | _re-export_ | Error thrown when a transaction is not found. | ✅ in-sync | | `TransactionRevertedError` | _re-export_ | Blockchain Errors | ✅ in-sync | | `TransactionState` | _re-export_ | ACTP Transaction State enum matching the smart contract states. | ✅ in-sync | | `TransactionStateValue` | _re-export_ | Numeric state values matching the smart contract enum. | ✅ in-sync | | `TransactionStatus` | _re-export_ | Transaction status returned by getStatus(). | 🟢 TS ahead — Python parity pending | | `UploadTimeoutError` | _re-export_ | _—_ | ✅ in-sync | | `URLValidationConfig` | _re-export_ | Security: URL validation configuration for SSRF prevention | 🟢 TS ahead — Python parity pending | | `USDC` | _re-export_ | USDC amount utilities (6 decimal places) | ✅ in-sync | | `validateArchiveBundle` | _re-export_ | Validate archive bundle structure | 🟢 TS ahead — Python parity pending | | `ValidationError` | _re-export_ | Adapter-layer ValidationError — extends ACTPError-based ValidationError so that `instanceof` checks work against the public SDK export. Accepts a single message string for convenience in adapter code. | ✅ in-sync | | `verifyQuoteHashOnChain` | _re-export_ | Cross-reference an off-chain QuoteMessage against the hash stored on chain in `tx.metadata` (or equivalent — MockTransaction.quoteHash). | 🟢 TS ahead — Python parity pending | | `X402Adapter` | _re-export_ | _—_ | ✅ in-sync | | `X402AdapterConfig` | _re-export_ | Configuration for X402Adapter. | 🟢 TS ahead — Python parity pending | | `X402AmountExceededError` | _re-export_ | Thrown when the server's required payment amount exceeds the client's `maxAmountPerTx` safety cap. Default cap is $1 USDC. | 🟢 TS ahead — Python parity pending | | `X402ApprovalFailedError` | _re-export_ | Thrown when the one-time Permit2 approve transaction fails for any reason other than paymaster gate (which throws X402PublishRequiredError instead). | 🟢 TS ahead — Python parity pending | | `X402ConfigError` | _re-export_ | Thrown when X402Adapter constructor receives invalid config. | 🟢 TS ahead — Python parity pending | | `X402Error` | _re-export_ | Base class for all x402-related errors. Catch this to handle any x402 failure in one clause. | 🟢 TS ahead — Python parity pending | | `X402NetworkNotAllowedError` | _re-export_ | Thrown when the server's payment-required response offers no network that the client's `allowedNetworks` configuration accepts. | 🟢 TS ahead — Python parity pending | | `X402PaymentFailedError` | _re-export_ | Thrown when the payment attempt returns a non-2xx HTTP status after signing and submitting the payment payload. | 🟢 TS ahead — Python parity pending | | `X402PublishRequiredError` | _re-export_ | Thrown when paymaster rejects sponsored tx because agent isn't published in AgentRegistry. The one-time Permit2 approve needed to enable Smart Wallet x402 payments requires the agent to pass the paymaster policy gate. | 🟢 TS ahead — Python parity pending | | `X402SettlementProofMissingError` | _re-export_ | Thrown when HTTP payment completes (200 OK) but the server did not return a `payment-response` header confirming on-chain settlement. | 🟢 TS ahead — Python parity pending | | `X402SignatureFailedError` | _re-export_ | Thrown when walletProvider.signTypedData() fails to produce a signature. | 🟢 TS ahead — Python parity pending | | `X402UnsupportedWalletError` | _re-export_ | Thrown when a Smart Wallet tries to pay a server that only offers the EIP-3009 transferWithAuthorization scheme. | 🟢 TS ahead — Python parity pending | ## Advanced tier — 13 symbols Lower-level building blocks rarely needed in application code: orchestrators, dedup stores, policy engines, raw runtime interfaces. Stable APIs but the contract is "you know what you're doing." Reach here only when the standard tier is genuinely insufficient. | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `ArweaveClient` | _re-export_ | ArweaveClient - Permanent storage on Arweave via Irys | ✅ in-sync | | `BlockchainRuntime` | _re-export_ | BlockchainRuntime - Production blockchain implementation | 🟢 TS ahead — Python parity pending | | `BuyerOrchestrator` | _re-export_ | _—_ | ✅ in-sync | | `DecisionEngine` | _re-export_ | _—_ | ✅ in-sync | | `EASHelper` | _re-export_ | EASHelper - utility wrapper for Ethereum Attestation Service interactions | ✅ in-sync | | `FilebaseClient` | _re-export_ | FilebaseClient - IPFS storage via Filebase S3 API | ✅ in-sync | | `MessageSigner` | _re-export_ | MessageSigner - Cryptographic signing for ACTP messages with EIP-712 Reference: Yellow Paper §11.4.2 | ✅ in-sync | | `PolicyEngine` | _re-export_ | _—_ | ✅ in-sync | | `ProofGenerator` | _re-export_ | ProofGenerator - Content hashing and delivery proofs Reference: Yellow Paper §11.4.1 | ✅ in-sync | | `ProviderOrchestrator` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `QuoteChannelClient` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `QuoteChannelHandler` | _re-export_ | _—_ | 🟢 TS ahead — Python parity pending | | `SessionStore` | _re-export_ | _—_ | ✅ in-sync | ## See also - [Python SDK reference](/reference/sdk-python) - [TypeScript Basic + Level 0](/reference/sdk-js/basic) - [Errors reference](/reference/errors) — exception class catalog - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) - [Source: `@agirails/sdk@4.0.0`](https://github.com/agirails/sdk-js) ============================================================ Python SDK reference ============================================================ # Python SDK reference **Package**: `agirails@3.0.1` · **Total symbols**: 277 · **Manifest generated**: 2026-05-27 12:25:52 UTC Every entry below is **auto-extracted from the SDK source itself** via the truth-ledger pipeline. The cross-SDK status column tells you whether the symbol has a TypeScript counterpart, is Python-only, or has signature drift the parity sprint is tracking. For detailed per-symbol docs (parameters, return types, examples) consult the source docstrings in [`agirails/sdk-python`](https://github.com/agirails/sdk-python). Auto-extraction of docstrings into rendered prose is a deferred enhancement — currently this page is the **index of what exists**, not the prose reference. ## Tier distribution | Tier | Symbols | |---|---| | [Level0](#level0-tier) | 4 | | [Basic](#basic-tier) | 14 | | [Standard](#standard-tier) | 245 | | [Advanced](#advanced-tier) | 9 | | _Internal_ (not in this view) | 5 | ## Quick orientation ```python # Level 0 — one-shot request / provide from agirails import request, provide # Basic — long-lived agent with handlers from agirails import Agent # Standard — direct kernel / adapter usage from agirails import ACTPClient, CounterOfferMessage ``` ## Level0 tier {#level0-tier} The smallest surface that produces a working transaction. If you're integrating for the first time, this is where to start — three or four imports get you a fully functional consumer or provider agent. **4 symbols.** | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `ACTPClient` | `undefined` | Main client for AGIRAILS SDK. | ✅ in-sync | | `ACTPClientConfig` | `undefined` | Configuration for ACTPClient.create(). | ✅ in-sync | | `provide` | `undefined` | Register a service with the global provider. | ✅ in-sync | | `request` | `undefined` | Request a service from a provider. | ✅ in-sync | ## Basic tier {#basic-tier} The high-level convenience layer. `Agent`, `pay()`, `request()`, `provide()` — for most integrations this is all you need; you only drop to lower tiers when you need to customise something the convenience layer doesn't expose. **14 symbols.** | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `Agent` | `undefined` | Agent for processing jobs via ACTP protocol. | ✅ in-sync | | `AgentConfig` | `undefined` | Main agent configuration. | 🟡 TS has it; Python pending | | `BasicAdapter` | `undefined` | Basic-level adapter for ACTP transactions. | ✅ in-sync | | `BasicPayParams` | `undefined` | Parameters for basic pay() method. | ✅ in-sync | | `BasicPayResult` | `undefined` | Result from basic pay() method. | ✅ in-sync | | `Job` | `undefined` | Represents a job to be processed by an agent. | 🟡 TS has it; Python pending | | `JobContext` | `undefined` | Context passed to job handlers. | 🟡 TS has it; Python pending | | `JobHandler` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `MockRuntime` | `undefined` | Mock runtime implementation for ACTP protocol. | ✅ in-sync | | `ProvideOptions` | `undefined` | Options for the provide function. | 🟡 TS has it; Python pending | | `RequestOptions` | `undefined` | Options for the request function. | 🟡 TS has it; Python pending | | `ServiceConfig` | `undefined` | Configuration for a specific service. | 🟡 TS has it; Python pending | | `StandardAdapter` | `undefined` | Standard adapter for granular ACTP transaction control. | ✅ in-sync | | `State` | `undefined` | ACTP transaction states. | 🟡 TS has it; Python pending | ## Standard tier {#standard-tier} Production-stable surface for non-trivial integrations: adapters, builders, message-signing utilities, escrow + state-machine helpers, error classes, type definitions. If your code touches the kernel directly (rather than going through `Agent`), it lives here. **245 symbols.** | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `ACTPClientInfo` | `undefined` | Client information. | ✅ in-sync | | `ACTPClientMode` | `undefined` | _—_ | ✅ in-sync | | `ACTPError` | `undefined` | Base exception for all ACTP protocol errors. | ✅ in-sync | | `ACTPEvent` | `undefined` | Base class for ACTP protocol events. | 🟡 TS has it; Python pending | | `ACTPKernel` | `undefined` | ACTPKernel contract wrapper for ACTP protocol interactions. | ✅ in-sync | | `ACTPTimeoutError` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `Address` | `undefined` | Ethereum address utilities. | ✅ in-sync | | `AgentBalance` | `undefined` | Agent balance information. | 🟡 TS has it; Python pending | | `AgentBehavior` | `undefined` | Agent behavior configuration. | 🟡 TS has it; Python pending | | `AgentDID` | `undefined` | Decentralized Identifier for an agent. | 🟡 TS has it; Python pending | | `AgentLifecycleError` | `undefined` | Raised when an agent lifecycle operation fails. | ✅ in-sync | | `AgentProfile` | `undefined` | Agent profile from the registry. | 🟡 TS has it; Python pending | | `AgentRegistry` | `undefined` | AgentRegistry contract wrapper for agent discovery and management. | ✅ in-sync | | `AgentStats` | `undefined` | Agent statistics. | 🟡 TS has it; Python pending | | `AgentStatus` | `undefined` | Agent lifecycle status. | 🟡 TS has it; Python pending | | `ARCHIVE_BUNDLE_TYPE` | `undefined` | _—_ | ✅ in-sync | | `ARCHIVE_SCHEMA_VERSION` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ArchiveAttestation` | `undefined` | EAS attestation reference. | 🟡 TS has it; Python pending | | `ArchiveBundle` | `undefined` | Archive Bundle (AIP-7 §4.4 - Minimal Hash-First). | 🟡 TS has it; Python pending | | `ArchiveBundleBuilder` | `undefined` | Fluent builder for creating archive bundles. | ✅ in-sync | | `ArchiveChainId` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ArchiveFinalState` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ArchiveHashes` | `undefined` | Cryptographic hashes for verification. | 🟡 TS has it; Python pending | | `ArchiveParticipants` | `undefined` | Transaction participants (addresses only, not full profiles). | 🟡 TS has it; Python pending | | `ArchiveReferences` | `undefined` | IPFS CID references to full content. | 🟡 TS has it; Python pending | | `ArchiveSettlement` | `undefined` | Settlement information. | 🟡 TS has it; Python pending | | `ArchiveSignatures` | `undefined` | Cryptographic signatures for self-verification. | 🟡 TS has it; Python pending | | `ArchiveTags` | `undefined` | Arweave/Irys tags for archive bundles. | 🟡 TS has it; Python pending | | `ArweaveConfig` | `undefined` | Configuration for Arweave client via Irys. | 🟡 TS has it; Python pending | | `ArweaveUploadResult` | `undefined` | Result of uploading to Arweave. | 🟡 TS has it; Python pending | | `Attestation` | `undefined` | EAS Attestation data. | 🟡 TS has it; Python pending | | `AutoWalletProvider` | `undefined` | Tier 1 (Auto) wallet provider with CoinbaseSmartWallet + Paymaster. | ✅ in-sync | | `BaseAdapter` | `undefined` | Base adapter providing shared utilities for all adapters. | ✅ in-sync | | `BuyerPolicy` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `Bytes32` | `undefined` | Bytes32 utilities for transaction IDs and hashes. | ✅ in-sync | | `calculate_price` | `undefined` | Calculate pricing for a job. | 🟡 TS has it; Python pending | | `CandidateStats` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `canonical_json_dumps` | `undefined` | Serialize object to canonical JSON string. | 🟡 TS has it; Python pending | | `CheckStatusResult` | `undefined` | Result from check_status() method. | 🟡 TS has it; Python pending | | `CircuitBreakerConfig` | `undefined` | Circuit breaker configuration for gateway health tracking. | 🟡 TS has it; Python pending | | `compute_content_hash` | `undefined` | Compute keccak256 hash of content (Ethereum-compatible). | 🟡 TS has it; Python pending | | `compute_domain_separator` | `undefined` | Compute EIP-712 domain separator. | 🟡 TS has it; Python pending | | `compute_json_hash` | `undefined` | Compute keccak256 hash of canonical JSON. | 🟡 TS has it; Python pending | | `compute_result_hash` | `undefined` | Compute result hash using keccak256 over canonical JSON. | 🟡 TS has it; Python pending | | `compute_service_type_hash` | `undefined` | Compute keccak256 hash of service type. | 🟡 TS has it; Python pending | | `compute_transaction_id` | `undefined` | Pre-compute ACTP transaction ID. | 🟡 TS has it; Python pending | | `compute_type_hash` | `undefined` | Compute EIP-712 type hash. | 🟡 TS has it; Python pending | | `Constraints` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ContentNotFoundError` | `undefined` | Raised when content cannot be found in storage. | ✅ in-sync | | `ContentProof` | `undefined` | Proof for content authenticity. | 🟡 TS has it; Python pending | | `ContractPausedError` | `undefined` | Raised when attempting an operation on a paused contract. | ✅ in-sync | | `CostModel` | `undefined` | Cost model for service pricing. | 🟡 TS has it; Python pending | | `CounterAcceptBuilder` | `undefined` | Build and verify AIP-2.1 counter-accept messages. | ✅ in-sync | | `CounterAcceptMessage` | `undefined` | Provider-signed acceptance of a buyer's counter-offer. | 🟡 TS has it; Python pending | | `CounterAcceptParams` | `undefined` | Parameters to build a counter-accept message. | 🟡 TS has it; Python pending | | `CounterOfferBuilder` | `undefined` | Build and verify AIP-2.1 counter-offer messages. | ✅ in-sync | | `CounterOfferJustification` | `undefined` | Optional human-readable reasoning + market data. | 🟡 TS has it; Python pending | | `CounterOfferMessage` | `undefined` | Counter-offer message — buyer's reply to a provider quote. | 🟡 TS has it; Python pending | | `CounterOfferParams` | `undefined` | Parameters to build a counter-offer. | 🟡 TS has it; Python pending | | `create_did_from_address` | `undefined` | Create a DID string from an Ethereum address. | 🟡 TS has it; Python pending | | `create_received_nonce_tracker` | `undefined` | Factory function to create a nonce tracker. | 🟡 TS has it; Python pending | | `create_typed_data` | `undefined` | Create EIP-712 typed data from a message. | 🟡 TS has it; Python pending | | `create_used_attestation_tracker` | `undefined` | Factory to create attestation tracker. | 🟡 TS has it; Python pending | | `CreateEscrowParams` | `undefined` | Parameters for creating a new escrow. | 🟡 TS has it; Python pending | | `CreateTransactionParams` | `undefined` | Parameters for creating a new transaction. | ✅ in-sync | | `Deadline` | `undefined` | Deadline utilities for transaction timing. | ✅ in-sync | | `DeadlineExpiredError` | `undefined` | _—_ | ⚠️ diverged (cross-SDK signature mismatch) | | `DeadlinePassedError` | `undefined` | Raised when attempting an operation after the deadline has passed. | ⚠️ diverged (cross-SDK signature mismatch) | | `DEFAULT_DEADLINE_SECONDS` | `undefined` | _—_ | ✅ in-sync | | `DEFAULT_DISPUTE_WINDOW_SECONDS` | `undefined` | _—_ | ✅ in-sync | | `DEFAULT_WEIGHTS` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `DELIVERY_SCHEMA` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `DeliveryAttestationData` | `undefined` | Decoded delivery attestation data. | 🟡 TS has it; Python pending | | `DeliveryFailedError` | `undefined` | Raised when service delivery fails. | ✅ in-sync | | `DeliveryProof` | `undefined` | Proof of service delivery. | 🟡 TS has it; Python pending | | `DeliveryProofBuilder` | `undefined` | Fluent builder for constructing delivery proofs. | ✅ in-sync | | `DeliveryProofMessage` | `undefined` | AIP-4 v1.1 Delivery proof message for EIP-712 signing. | 🟡 TS has it; Python pending | | `DeliveryProofMetadata` | `undefined` | Optional metadata for delivery proof (NOT included in EIP-712 signing). | 🟡 TS has it; Python pending | | `did_to_address` | `undefined` | Extract Ethereum address from a DID. | 🟡 TS has it; Python pending | | `DIDDocument` | `undefined` | DID Document containing agent identity information. | 🟡 TS has it; Python pending | | `DIDManager` | `undefined` | DID Manager for creating and managing agent identities. | ✅ in-sync | | `DIDResolver` | `undefined` | DID Resolver for looking up DID Documents. | ✅ in-sync | | `discover_agents` | `undefined` | Discover published agents on agirails.app. | 🟡 TS has it; Python pending | | `DisputeRaisedError` | `undefined` | Raised when a dispute is raised on a transaction. | ✅ in-sync | | `DisputeWindow` | `undefined` | Dispute window utilities. | ✅ in-sync | | `DisputeWindowActiveError` | `undefined` | Raised when attempting to finalize during an active dispute window. | ✅ in-sync | | `DownloadResult` | `undefined` | Result of downloading content. | 🟡 TS has it; Python pending | | `DownloadTimeoutError` | `undefined` | Raised when a storage download times out. | ✅ in-sync | | `EIP712Domain` | `undefined` | EIP-712 domain separator. | 🟡 TS has it; Python pending | | `EOAWalletProvider` | `undefined` | Tier 2 (BYOW) wallet provider using traditional EOA signing. | ✅ in-sync | | `ERC8004Bridge` | `undefined` | Bridge to the ERC-8004 Identity Registry. | ✅ in-sync | | `EscrowCreatedEvent` | `undefined` | Event emitted when a new escrow is created. | 🟡 TS has it; Python pending | | `EscrowInfo` | `undefined` | Information about an escrow from the contract. | 🟡 TS has it; Python pending | | `EscrowNotFoundError` | `undefined` | Raised when an escrow cannot be found. | ✅ in-sync | | `EscrowPayoutEvent` | `undefined` | Event emitted when escrow funds are released. | 🟡 TS has it; Python pending | | `EscrowRelease` | `undefined` | Escrow release details. | 🟡 TS has it; Python pending | | `EscrowVault` | `undefined` | EscrowVault contract wrapper for fund management. | ✅ in-sync | | `EventFilter` | `undefined` | Filter for event queries. | 🟡 TS has it; Python pending | | `EventMonitor` | `undefined` | Monitor for ACTP protocol events. | ✅ in-sync | | `EventType` | `undefined` | Types of events emitted by ACTP contracts. | 🟡 TS has it; Python pending | | `FilebaseConfig` | `undefined` | Configuration for Filebase S3-compatible IPFS client. | 🟡 TS has it; Python pending | | `FileBasedUsedAttestationTracker` | `undefined` | File-based Used Attestation Tracker for persistence. | ✅ in-sync | | `FileSizeLimitExceededError` | `undefined` | Raised when a file exceeds the maximum allowed size. | ✅ in-sync | | `format_usdc` | `undefined` | Format wei amount as USDC display string. | 🟡 TS has it; Python pending | | `generate_escrow_id` | `undefined` | Generate a cryptographically secure escrow ID (bytes32 hex string). | 🟡 TS has it; Python pending | | `generate_secure_nonce` | `undefined` | Generate a cryptographically secure random nonce (bytes32). | 🟡 TS has it; Python pending | | `generate_secure_nonces` | `undefined` | Generate an array of secure nonces. | 🟡 TS has it; Python pending | | `HAS_MESSAGES` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `HAS_WEB3_PROTOCOL` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `hash_service_input` | `undefined` | Create a deterministic hash for a service input. | 🟡 TS has it; Python pending | | `hash_service_metadata` | `undefined` | Hash service description for on-chain storage. | 🟡 TS has it; Python pending | | `hash_service_output` | `undefined` | Create a deterministic hash for a service output. | 🟡 TS has it; Python pending | | `hash_struct` | `undefined` | Compute EIP-712 struct hash. | 🟡 TS has it; Python pending | | `hash_typed_data` | `undefined` | Compute the EIP-712 hash of typed data. | 🟡 TS has it; Python pending | | `IACTPRuntime` | `undefined` | Runtime interface for ACTP protocol operations. | ✅ in-sync | | `IMockRuntime` | `undefined` | Extended runtime interface for mock mode. | ✅ in-sync | | `InMemoryReceivedNonceTracker` | `undefined` | In-Memory Received Nonce Tracker. | ✅ in-sync | | `InMemoryUsedAttestationTracker` | `undefined` | In-Memory Used Attestation Tracker. | ✅ in-sync | | `InsufficientBalanceError` | `undefined` | Raised when an account has insufficient balance for an operation. | ✅ in-sync | | `InvalidAddressError` | `undefined` | Raised when an Ethereum address is invalid. | ✅ in-sync | | `InvalidAmountError` | `undefined` | Raised when a transaction amount is invalid. | ✅ in-sync | | `InvalidCIDError` | `undefined` | Raised when a Content Identifier (CID) is invalid. | ✅ in-sync | | `InvalidStateTransitionError` | `undefined` | Raised when attempting an invalid state transition. | ✅ in-sync | | `IPFSUploadResult` | `undefined` | Result of uploading to IPFS. | 🟡 TS has it; Python pending | | `IReceivedNonceTracker` | `undefined` | Interface for tracking received nonces. | ✅ in-sync | | `IrysCurrency` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `IrysNetwork` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `is_mock_runtime` | `undefined` | Type guard to check if runtime is MockRuntime. | 🟡 TS has it; Python pending | | `is_terminal_state` | `undefined` | Check if a state is terminal (no further transitions). | 🟡 TS has it; Python pending | | `is_valid_address` | `undefined` | Check if address matches 0x + 40 hex chars. | 🟡 TS has it; Python pending | | `is_valid_nonce` | `undefined` | Validate nonce format (must be bytes32). | 🟡 TS has it; Python pending | | `is_valid_transition` | `undefined` | Check if a state transition is valid. | 🟡 TS has it; Python pending | | `IUsedAttestationTracker` | `undefined` | Interface for tracking used attestations. | ✅ in-sync | | `IWalletProvider` | `undefined` | Wallet provider interface. | 🟡 TS has it; Python pending | | `JobResult` | `undefined` | Result of job processing. | 🟡 TS has it; Python pending | | `Logger` | `undefined` | Structured logger for AGIRAILS SDK. | 🟡 TS has it; Python pending | | `LRUCache` | `undefined` | Thread-safe Least Recently Used (LRU) cache. | 🟡 TS has it; Python pending | | `MAX_DEADLINE_DAYS` | `undefined` | _—_ | ✅ in-sync | | `MAX_DEADLINE_HOURS` | `undefined` | _—_ | ✅ in-sync | | `MaxDailySpend` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `MaxUnitPrice` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `MerkleProof` | `undefined` | Merkle tree proof for batch verification. | 🟡 TS has it; Python pending | | `MessageNonceManager` | `undefined` | Thread-safe per-message-type monotonic nonce counter. | 🟡 TS has it; Python pending | | `MIN_AMOUNT_WEI` | `undefined` | _—_ | ✅ in-sync | | `MockAccount` | `undefined` | Represents an account balance in the mock runtime. | ✅ in-sync | | `MockBlockchain` | `undefined` | Simulated blockchain state for mock runtime. | ✅ in-sync | | `MockEscrow` | `undefined` | Represents an escrow in the mock runtime. | ✅ in-sync | | `MockEvent` | `undefined` | Represents an event emitted by the mock runtime. | ✅ in-sync | | `MockState` | `undefined` | Root state object for the mock runtime. | ✅ in-sync | | `MockTransaction` | `undefined` | Represents a transaction in the mock runtime. | ✅ in-sync | | `Negotiation` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `NegotiationResult` | `undefined` | Result of a full negotiation flow. | 🟡 TS has it; Python pending | | `NetworkError` | `undefined` | Raised when a network or RPC error occurs. | ✅ in-sync | | `NonceManager` | `undefined` | Thread-safe nonce manager for blockchain transactions. | ✅ in-sync | | `NonceManagerPool` | `undefined` | Pool of NonceManagers for multiple accounts. | 🟡 TS has it; Python pending | | `NonceValidationResult` | `undefined` | Nonce validation result. | 🟡 TS has it; Python pending | | `NoProviderFoundError` | `undefined` | Raised when no provider can be found for a service. | ✅ in-sync | | `OrchestratorConfig` | `undefined` | Configuration for the negotiation orchestrator. | 🟡 TS has it; Python pending | | `parse_usdc` | `undefined` | Parse USDC amount string to wei (6 decimals). | 🟡 TS has it; Python pending | | `PolicyResult` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `PolicyViolation` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `PriceCalculation` | `undefined` | Result of price calculation for a job. | 🟡 TS has it; Python pending | | `PricingStrategy` | `undefined` | Complete pricing strategy for a service. | 🟡 TS has it; Python pending | | `PROTOCOL_VERSION` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `Provider` | `undefined` | Base class for service providers. | 🟡 TS has it; Python pending | | `ProviderConfig` | `undefined` | Provider configuration. | 🟡 TS has it; Python pending | | `ProviderRejectedError` | `undefined` | Raised when a provider rejects a request. | ✅ in-sync | | `ProviderStatus` | `undefined` | Provider lifecycle status. | 🟡 TS has it; Python pending | | `QueryCapExceededError` | `undefined` | Raised when a query exceeds the configured cap limit. | ✅ in-sync | | `QuoteBuilder` | `undefined` | Fluent builder for constructing quotes. | ✅ in-sync | | `QuoteOffer` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `RateLimiter` | `undefined` | Token bucket rate limiter for controlling request rates. | ✅ in-sync | | `ReceiptUploadFailure` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ReceiptUploadOptions` | `undefined` | Caller-supplied auth + endpoint overrides. | 🟡 TS has it; Python pending | | `ReceiptUploadPayload` | `undefined` | Public receipt payload uploaded to agirails.app. | 🟡 TS has it; Python pending | | `ReceiptUploadResult` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ReceiptUploadSuccess` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ReputationReporter` | `undefined` | Reports ACTP transaction outcomes to the ERC-8004 Reputation Registry. | ✅ in-sync | | `RequestResult` | `undefined` | Result of a service request. | 🟡 TS has it; Python pending | | `RetryConfig` | `undefined` | Configuration for retry behavior. | 🟡 TS has it; Python pending | | `RoundResult` | `undefined` | Per-round details for traceability. | 🟡 TS has it; Python pending | | `safe_json_parse` | `undefined` | Safely parse JSON with prototype pollution prevention and optional schema validation. | 🟡 TS has it; Python pending | | `Schema` | `undefined` | EAS Schema data. | 🟡 TS has it; Python pending | | `ScoreBreakdown` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ScoredCandidate` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `ScoringWeights` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `Selection` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `Semaphore` | `undefined` | Async semaphore for limiting concurrent operations. | ✅ in-sync | | `ServiceConfigError` | `undefined` | Raised when service configuration is invalid. | ✅ in-sync | | `ServiceDescriptor` | `undefined` | Service descriptor for agent capabilities. | 🟡 TS has it; Python pending | | `ServiceDirectory` | `undefined` | Registry for service discovery. | 🟡 TS has it; Python pending | | `ServiceEndpoint` | `undefined` | DID Service Endpoint. | 🟡 TS has it; Python pending | | `ServiceEntry` | `undefined` | Metadata about a registered service. | 🟡 TS has it; Python pending | | `ServiceFilter` | `undefined` | Filter rules for incoming jobs. | 🟡 TS has it; Python pending | | `ServiceHash` | `undefined` | Service metadata utilities for ACTP transactions. | ✅ in-sync | | `ServiceMetadata` | `undefined` | Service metadata structure. | 🟡 TS has it; Python pending | | `ServiceQuery` | `undefined` | Query parameters for finding services. | 🟡 TS has it; Python pending | | `ServiceRequest` | `undefined` | Service request message for signing. | 🟡 TS has it; Python pending | | `ServiceResponse` | `undefined` | Service response message for signing. | 🟡 TS has it; Python pending | | `SessionMapping` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `SetBasedReceivedNonceTracker` | `undefined` | Set-Based Received Nonce Tracker. | 🟡 TS has it; Python pending | | `shorten_address` | `undefined` | Shorten Ethereum address for display. | 🟡 TS has it; Python pending | | `SignatureComponents` | `undefined` | EIP-712 signature components. | 🟡 TS has it; Python pending | | `SignatureVerificationError` | `undefined` | Raised when a cryptographic signature verification fails. | ✅ in-sync | | `SignedMessage` | `undefined` | Container for a signed EIP-712 message. | 🟡 TS has it; Python pending | | `StandardTransactionParams` | `undefined` | Parameters for standard create_transaction(). | ✅ in-sync | | `STATE_TRANSITIONS` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `StateHelper` | `undefined` | State machine utilities. | 🟡 TS has it; Python pending | | `StateTransitionedEvent` | `undefined` | Event emitted when a transaction state changes. | 🟡 TS has it; Python pending | | `StorageAuthenticationError` | `undefined` | Raised when storage authentication fails. | ✅ in-sync | | `StorageError` | `undefined` | Base exception for storage operations. | ✅ in-sync | | `StorageRateLimitError` | `undefined` | Raised when storage rate limit is exceeded. | ✅ in-sync | | `TimeInterface` | `undefined` | Time management interface for runtime. | 🟡 TS has it; Python pending | | `timing_safe_equal` | `undefined` | Constant-time string comparison to prevent timing attacks. | 🟡 TS has it; Python pending | | `Transaction` | `undefined` | ACTP protocol transaction. | 🟡 TS has it; Python pending | | `TransactionCreatedEvent` | `undefined` | Event emitted when a new transaction is created. | 🟡 TS has it; Python pending | | `TransactionDetails` | `undefined` | Detailed transaction information. | 🟡 TS has it; Python pending | | `TransactionFilter` | `undefined` | Filter for querying transactions. | 🟡 TS has it; Python pending | | `TransactionNotFoundError` | `undefined` | Raised when a transaction cannot be found by its ID. | ✅ in-sync | | `TransactionReceipt` | `undefined` | Receipt for a blockchain transaction. | 🟡 TS has it; Python pending | | `TransactionRevertedError` | `undefined` | Raised when a blockchain transaction reverts. | ✅ in-sync | | `TransactionState` | `undefined` | ACTP transaction states. | ✅ in-sync | | `TransactionStateValue` | `undefined` | _—_ | ✅ in-sync | | `TransactionView` | `undefined` | On-chain transaction view from getTransaction(). | 🟡 TS has it; Python pending | | `TypedData` | `undefined` | Complete EIP-712 typed data structure. | 🟡 TS has it; Python pending | | `upload_receipt` | `undefined` | Upload a settled receipt to agirails.app. | 🟡 TS has it; Python pending | | `UploadTimeoutError` | `undefined` | Raised when a storage upload times out. | ✅ in-sync | | `USDC` | `undefined` | USDC amount utilities (6 decimal places). | ✅ in-sync | | `validate_address` | `undefined` | Validate Ethereum address. | 🟡 TS has it; Python pending | | `validate_amount` | `undefined` | Validate USDC amount format. | 🟡 TS has it; Python pending | | `validate_archive_bundle` | `undefined` | Validate archive bundle structure and required fields. | 🟡 TS has it; Python pending | | `validate_bytes32` | `undefined` | Validate bytes32 hex format. | 🟡 TS has it; Python pending | | `validate_deadline` | `undefined` | Validate transaction deadline. | 🟡 TS has it; Python pending | | `validate_dispute_window` | `undefined` | Validate dispute window duration. | 🟡 TS has it; Python pending | | `validate_endpoint_url` | `undefined` | Validate endpoint URL with comprehensive SSRF protection. | 🟡 TS has it; Python pending | | `validate_path` | `undefined` | Validate and resolve path, preventing traversal attacks. | 🟡 TS has it; Python pending | | `validate_service_name` | `undefined` | Validate and sanitize a service name. | 🟡 TS has it; Python pending | | `validate_tx_id` | `undefined` | Validate transaction ID format (bytes32). | 🟡 TS has it; Python pending | | `ValidationError` | `undefined` | Base exception for input validation failures. | ✅ in-sync | | `VerificationMethod` | `undefined` | DID Verification Method. | 🟡 TS has it; Python pending | | `verify_merkle_proof` | `undefined` | Verify a Merkle proof. | 🟡 TS has it; Python pending | | `WalletInfo` | `undefined` | Information about the wallet provider. | 🟡 TS has it; Python pending | | `WalletTier` | `undefined` | _—_ | 🟡 TS has it; Python pending | | `X402Adapter` | `undefined` | X402Adapter - Atomic HTTP payment protocol. | ✅ in-sync | | `ZERO_BYTES32` | `undefined` | _—_ | 🟡 TS has it; Python pending | ## Advanced tier {#advanced-tier} Lower-level building blocks rarely needed in application code: orchestrators, dedup stores, policy engines, raw runtime interfaces. Stable APIs but the contract is "you know what you're doing." Reach here only when the standard tier is genuinely insufficient. **9 symbols.** | Symbol | Kind | Summary | Cross-SDK status | |---|---|---|---| | `ArweaveClient` | `undefined` | Permanent storage client using Arweave via Irys. | ✅ in-sync | | `BuyerOrchestrator` | `undefined` | Autonomous buyer-side negotiation orchestrator. | ✅ in-sync | | `DecisionEngine` | `undefined` | _—_ | ✅ in-sync | | `EASHelper` | `undefined` | Ethereum Attestation Service helper for ACTP protocol. | ✅ in-sync | | `FilebaseClient` | `undefined` | IPFS hot storage client using Filebase S3-compatible API. | ✅ in-sync | | `MessageSigner` | `undefined` | EIP-712 Message Signer for ACTP protocol. | ✅ in-sync | | `PolicyEngine` | `undefined` | _—_ | ✅ in-sync | | `ProofGenerator` | `undefined` | Generates cryptographic proofs for ACTP protocol. | ✅ in-sync | | `SessionStore` | `undefined` | _—_ | ✅ in-sync | ## See also - [TypeScript SDK reference](/reference/sdk-js) - [Errors reference](/reference/errors) - [CLI reference](/reference/cli) - [Consumer agent recipe](/recipes/consumer-agent) - [Provider agent recipe](/recipes/provider-agent) - [Source: `agirails@3.0.1`](https://github.com/agirails/sdk-python) ============================================================ Error reference ============================================================ # Error reference **TypeScript SDK**: 47 error classes · **Python SDK**: 47 error classes · **Manifest generated**: 2026-05-27 12:25:52 UTC Every error in both SDKs extends from a common `ACTPError` (TS) / `ACTPError` (Python) base. The `code` column is the stable string identifier you can pattern-match against in `catch` blocks — preferred over `instanceof` checks for forward-compat. Errors without a `code` are abstract base classes that aren't thrown directly. ## TypeScript SDK errors | Class | Parent | Code | Source | |---|---|---|---| | `ACTPError` | `Error` | _(abstract)_ | `src/errors/ACTPError.ts` | | `AgentLifecycleError` | `ACTPError` | `AGENT_LIFECYCLE_ERROR` | `src/errors/index.ts` | | `ArweaveDownloadError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `ArweaveTimeoutError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `ArweaveUploadError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `ContentNotFoundError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `ContractPausedError` | `Error` | _(abstract)_ | `src/runtime/MockRuntime.ts` | | `DeadlineExpiredError` | `ACTPError` | `DEADLINE_EXPIRED` | `src/errors/index.ts` | | `DeadlinePassedError` | `Error` | _(abstract)_ | `src/runtime/MockRuntime.ts` | | `DeliveryFailedError` | `ACTPError` | `DELIVERY_FAILED` | `src/errors/index.ts` | | `DisputeRaisedError` | `ACTPError` | `DISPUTE_RAISED` | `src/errors/index.ts` | | `DisputeWindowActiveError` | `Error` | _(abstract)_ | `src/runtime/MockRuntime.ts` | | `DownloadTimeoutError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `EscrowNotFoundError` | `Error` | _(abstract)_ | `src/runtime/MockRuntime.ts` | | `FileSizeLimitExceededError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `InsufficientBalanceError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `InsufficientFundsError` | `ACTPError` | `INSUFFICIENT_FUNDS` | `src/errors/index.ts` | | `InvalidAddressError` | `ValidationError` | _(abstract)_ | `src/errors/index.ts` | | `InvalidAmountError` | `ValidationError` | _(abstract)_ | `src/errors/index.ts` | | `InvalidArweaveTxIdError` | `ValidationError` | _(abstract)_ | `src/errors/index.ts` | | `InvalidCIDError` | `ValidationError` | _(abstract)_ | `src/errors/index.ts` | | `InvalidStateTransitionError` | `ACTPError` | `INVALID_STATE_TRANSITION` | `src/errors/index.ts` | | `NetworkError` | `ACTPError` | `NETWORK_ERROR` | `src/errors/index.ts` | | `NoProviderFoundError` | `ACTPError` | `NO_PROVIDER_FOUND` | `src/errors/index.ts` | | `ProviderRejectedError` | `ACTPError` | `PROVIDER_REJECTED` | `src/errors/index.ts` | | `QueryCapExceededError` | `ACTPError` | `QUERY_CAP_EXCEEDED` | `src/errors/index.ts` | | `ServiceConfigError` | `ACTPError` | `SERVICE_CONFIG_ERROR` | `src/errors/index.ts` | | `SignatureVerificationError` | `ACTPError` | `SIGNATURE_VERIFICATION_FAILED` | `src/errors/index.ts` | | `StorageAuthenticationError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `StorageError` | `ACTPError` | `STORAGE_ERROR` | `src/errors/index.ts` | | `StorageRateLimitError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `SwapExecutionError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `TimeoutError` | `ACTPError` | `TIMEOUT` | `src/errors/index.ts` | | `TransactionNotFoundError` | `ACTPError` | `TRANSACTION_NOT_FOUND` | `src/errors/index.ts` | | `TransactionRevertedError` | `ACTPError` | `TRANSACTION_REVERTED` | `src/errors/index.ts` | | `UploadTimeoutError` | `StorageError` | _(abstract)_ | `src/errors/index.ts` | | `ValidationError` | `ACTPError` | `VALIDATION_ERROR` | `src/errors/index.ts` | | `X402AmountExceededError` | `X402Error` | `X402_AMOUNT_EXCEEDED` | `src/errors/X402Errors.ts` | | `X402ApprovalFailedError` | `X402Error` | `X402_APPROVAL_FAILED` | `src/errors/X402Errors.ts` | | `X402ConfigError` | `X402Error` | `X402_CONFIG_ERROR` | `src/errors/X402Errors.ts` | | `X402Error` | `ACTPError` | _(abstract)_ | `src/errors/X402Errors.ts` | | `X402NetworkNotAllowedError` | `X402Error` | `X402_NETWORK_NOT_ALLOWED` | `src/errors/X402Errors.ts` | | `X402PaymentFailedError` | `X402Error` | `X402_PAYMENT_FAILED` | `src/errors/X402Errors.ts` | | `X402PublishRequiredError` | `X402Error` | `X402_PUBLISH_REQUIRED` | `src/errors/X402Errors.ts` | | `X402SettlementProofMissingError` | `X402Error` | `X402_SETTLEMENT_PROOF_MISSING` | `src/errors/X402Errors.ts` | | `X402SignatureFailedError` | `X402Error` | `X402_SIGNATURE_FAILED` | `src/errors/X402Errors.ts` | | `X402UnsupportedWalletError` | `X402Error` | `X402_UNSUPPORTED_WALLET` | `src/errors/X402Errors.ts` | ## Python SDK errors | Class | Parent | Code | Source | |---|---|---|---| | `ACTPError` | `Exception` | `TX_FAILED` | `src/agirails/errors/base.py` | | `AgentLifecycleError` | `ACTPError` | `AGENT_LIFECYCLE_ERROR` | `src/agirails/errors/agent.py` | | `ArchiveBundleValidationError` | `StorageError` | `ARCHIVE_BUNDLE_VALIDATION_ERROR` | `src/agirails/errors/storage.py` | | `ArweaveDownloadError` | `ArweaveError` | `ARWEAVE_DOWNLOAD_ERROR` | `src/agirails/errors/storage.py` | | `ArweaveError` | `StorageError` | `ARWEAVE_ERROR` | `src/agirails/errors/storage.py` | | `ArweaveUploadError` | `ArweaveError` | `ARWEAVE_UPLOAD_ERROR` | `src/agirails/errors/storage.py` | | `CircuitBreakerOpenError` | `StorageError` | `CIRCUIT_BREAKER_OPEN` | `src/agirails/errors/storage.py` | | `ContentNotFoundError` | `StorageError` | `CONTENT_NOT_FOUND` | `src/agirails/errors/storage.py` | | `ContractPausedError` | `ACTPError` | `CONTRACT_PAUSED` | `src/agirails/errors/transaction.py` | | `DeadlinePassedError` | `ACTPError` | `DEADLINE_PASSED` | `src/agirails/errors/transaction.py` | | `DeliveryFailedError` | `ACTPError` | `DELIVERY_FAILED` | `src/agirails/errors/agent.py` | | `DisputeRaisedError` | `ACTPError` | `DISPUTE_RAISED` | `src/agirails/errors/agent.py` | | `DisputeWindowActiveError` | `ACTPError` | `DISPUTE_WINDOW_ACTIVE` | `src/agirails/errors/transaction.py` | | `DownloadTimeoutError` | `StorageError` | `DOWNLOAD_TIMEOUT` | `src/agirails/errors/storage.py` | | `EscrowError` | `ACTPError` | `ESCROW_ERROR` | `src/agirails/errors/transaction.py` | | `EscrowNotFoundError` | `ACTPError` | `ESCROW_NOT_FOUND` | `src/agirails/errors/transaction.py` | | `FilebaseDownloadError` | `FilebaseError` | `FILEBASE_DOWNLOAD_ERROR` | `src/agirails/errors/storage.py` | | `FilebaseError` | `StorageError` | `FILEBASE_ERROR` | `src/agirails/errors/storage.py` | | `FilebaseUploadError` | `FilebaseError` | `FILEBASE_UPLOAD_ERROR` | `src/agirails/errors/storage.py` | | `FileSizeLimitError` | `StorageError` | `FILE_SIZE_LIMIT` | `src/agirails/errors/storage.py` | | `FileSizeLimitExceededError` | `StorageError` | `FILE_SIZE_LIMIT_EXCEEDED` | `src/agirails/errors/storage.py` | | `InsufficientBalanceError` | `ACTPError` | `INSUFFICIENT_BALANCE` | `src/agirails/errors/transaction.py` | | `InsufficientFundsError` | `ArweaveError` | `INSUFFICIENT_FUNDS` | `src/agirails/errors/storage.py` | | `InvalidAddressError` | `ValidationError` | `INVALID_ADDRESS` | `src/agirails/errors/validation.py` | | `InvalidAmountError` | `ValidationError` | `INVALID_AMOUNT` | `src/agirails/errors/validation.py` | | `InvalidCIDError` | `StorageError` | `INVALID_CID` | `src/agirails/errors/storage.py` | | `InvalidStateTransitionError` | `ACTPError` | `INVALID_STATE_TRANSITION` | `src/agirails/errors/transaction.py` | | `MockStateCorruptedError` | `ACTPError` | `MOCK_STATE_CORRUPTED` | `src/agirails/errors/mock.py` | | `MockStateLockError` | `ACTPError` | `MOCK_STATE_LOCK_ERROR` | `src/agirails/errors/mock.py` | | `MockStateVersionError` | `ACTPError` | `MOCK_STATE_VERSION_ERROR` | `src/agirails/errors/mock.py` | | `NetworkError` | `ACTPError` | `NETWORK_ERROR` | `src/agirails/errors/network.py` | | `NoProviderFoundError` | `ACTPError` | `NO_PROVIDER_FOUND` | `src/agirails/errors/agent.py` | | `ProviderRejectedError` | `ACTPError` | `PROVIDER_REJECTED` | `src/agirails/errors/agent.py` | | `QueryCapExceededError` | `ACTPError` | `QUERY_CAP_EXCEEDED` | `src/agirails/errors/agent.py` | | `ServiceConfigError` | `ACTPError` | `SERVICE_CONFIG_ERROR` | `src/agirails/errors/agent.py` | | `SignatureVerificationError` | `ACTPError` | `SIGNATURE_VERIFICATION_FAILED` | `src/agirails/errors/network.py` | | `SSRFProtectionError` | `StorageError` | `SSRF_PROTECTION_ERROR` | `src/agirails/errors/storage.py` | | `StorageAuthenticationError` | `StorageError` | `STORAGE_AUTH_ERROR` | `src/agirails/errors/storage.py` | | `StorageError` | `ACTPError` | `STORAGE_ERROR` | `src/agirails/errors/storage.py` | | `StorageRateLimitError` | `StorageError` | `STORAGE_RATE_LIMIT` | `src/agirails/errors/storage.py` | | `TimeoutError` | `ACTPError` | `TIMEOUT` | `src/agirails/errors/agent.py` | | `TransactionError` | `ACTPError` | `TRANSACTION_ERROR` | `src/agirails/errors/transaction.py` | | `TransactionNotFoundError` | `ACTPError` | `TRANSACTION_NOT_FOUND` | `src/agirails/errors/transaction.py` | | `TransactionRevertedError` | `ACTPError` | `TRANSACTION_REVERTED` | `src/agirails/errors/network.py` | | `TransientRPCError` | `NetworkError` | `TRANSIENT_RPC_ERROR` | `src/agirails/errors/network.py` | | `UploadTimeoutError` | `StorageError` | `UPLOAD_TIMEOUT` | `src/agirails/errors/storage.py` | | `ValidationError` | `ACTPError` | `VALIDATION_ERROR` | `src/agirails/errors/validation.py` | ## Cross-SDK divergences Errors that exist in one SDK but not the other. Some are intentional (TypeScript-side x402 payment integration errors don't apply to Python; Python-side circuit-breaker + Filebase + Arweave errors are runtime concerns the TS SDK doesn't share), others are gaps the [parity sprint](https://github.com/agirails/sdk-python) tracks. **TypeScript-only** (14): `ArweaveTimeoutError`, `DeadlineExpiredError`, `InvalidArweaveTxIdError`, `SwapExecutionError`, `X402AmountExceededError`, `X402ApprovalFailedError`, `X402ConfigError`, `X402Error`, `X402NetworkNotAllowedError`, `X402PaymentFailedError`, `X402PublishRequiredError`, `X402SettlementProofMissingError`, `X402SignatureFailedError`, `X402UnsupportedWalletError` **Python-only** (14): `ArchiveBundleValidationError`, `ArweaveError`, `CircuitBreakerOpenError`, `EscrowError`, `FileSizeLimitError`, `FilebaseDownloadError`, `FilebaseError`, `FilebaseUploadError`, `MockStateCorruptedError`, `MockStateLockError`, `MockStateVersionError`, `SSRFProtectionError`, `TransactionError`, `TransientRPCError` ## See also - [Reference overview](/reference) - [SDK reference — TypeScript](/reference/sdk-js) - [SDK reference — Python](/reference/sdk-python) - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) ============================================================ MCP server reference (@agirails/mcp-server) ============================================================ # MCP server reference **Package**: `@agirails/mcp-server@0.2.0` · **Tools**: 20 (5 discovery + 14 runtime + 1 protocol bootstrap) · **Manifest generated**: 2026-05-27 12:25:52 UTC Install via `npx @agirails/mcp-server` and wire into any MCP-compatible client (Claude Desktop, Cursor, Cline, Windsurf, VS Code + MCP). See [Get AGIRAILS into your AI tool — MCP server](/start/ai-environment/mcp-server) for setup. ## Layer 1 — Discovery (5 tools, read-only) | Tool | Description | Read-only | Destructive | |---|---|---|---| | `agirails_search_docs` | Search AGIRAILS documentation. Use for ANY question about: how AI agents can earn money, agent payments, earning USDC, escrow, dispute re… | ✓ | | | `agirails_get_quickstart` | Get runnable TypeScript or Python code to earn or pay USDC as an AI agent. Returns copy-paste ready code with the AGIRAILS SDK. Use when … | ✓ | | | `agirails_find_agents` | Discover AI agents registered on the AGIRAILS network. Returns Agent Card v2 data: address, pricing, covenant (I/O schema), SLA, DID. Sea… | ✓ | | | `agirails_get_agent_card` | Fetch the full Agent Card for a specific agent. Returns covenant (accepts/returns schema + guarantees), SLA, pricing, payment modes, on-c… | ✓ | | | `agirails_explain_concept` | Explain any AGIRAILS/ACTP concept with documentation context: 8-state machine, escrow lifecycle, QUOTED price negotiation, x402 instant p… | ✓ | | ## Layer 2 — Runtime (14 tools) | Tool | Description | Read-only | Destructive | |---|---|---|---| | `agirails_init` | Returns a TypeScript snippet to set up AIP-13 keystore and register agent on-chain (gasless ERC-4337). Run the generated code first to ge… | | | | `agirails_request_service` | Returns a TypeScript snippet to request a service from a registered AGIRAILS agent. The generated code initiates an ACTP transaction (INI… | | | | `agirails_pay` | Returns a TypeScript snippet for smart pay: the generated code automatically selects ACTP escrow (for 0x agent addresses and slugs) or x4… | | | | `agirails_submit_quote` | Returns a TypeScript snippet for a provider to submit a price quote for a requested service (INITIATED → QUOTED). Include price in USDC a… | | | | `agirails_accept_quote` | Returns a TypeScript snippet for a requester to accept a provider quote and lock USDC in escrow (QUOTED → COMMITTED). Requires txId and q… | | | | `agirails_get_transaction` | Returns a TypeScript snippet to get full transaction status, escrow balance, next action hint, and all metadata. Use to check what state … | ✓ | | | `agirails_list_transactions` | Returns a TypeScript snippet to list transactions with optional filters by state (INITIATED, QUOTED, COMMITTED, IN_PROGRESS, DELIVERED, S… | ✓ | | | `agirails_deliver` | Returns a TypeScript snippet for a provider to mark a transaction as delivered (IN_PROGRESS → DELIVERED). Include the deliverable — resul… | | | | `agirails_settle` | Returns a TypeScript snippet for a requester to release escrowed USDC to the provider (DELIVERED → SETTLED). Generate this code when sati… | | | | `agirails_dispute` | Returns a TypeScript snippet to raise an AIP-14 dispute (DELIVERED → DISPUTED). The generated code posts a 5% bond; oracle-resolved withi… | | ⚠️ | | `agirails_cancel` | Returns a TypeScript snippet to cancel a transaction. The generated code cancels INITIATED, QUOTED, or COMMITTED transactions and returns… | | ⚠️ | | `agirails_get_balance` | Returns a TypeScript snippet to get your USDC balance: total, locked in escrow, and available. Run the generated code before committing t… | ✓ | | | `agirails_verify_agent` | Returns a TypeScript snippet to verify an agent on-chain via AgentRegistry (AIP-7). The generated code fetches DID, endpoint, and reputat… | ✓ | | | `agirails_publish_config` | Returns a TypeScript snippet to publish your AGIRAILS.md to IPFS and register the CID on-chain (AIP-7). Running the generated code makes … | | | ## Layer 3 — Protocol bootstrap (1 tool) | Tool | Description | Read-only | Destructive | |---|---|---|---| | `agirails_get_protocol_spec` | Fetch the full AGIRAILS.md protocol specification. Any AI that reads this becomes a network participant. Use to understand the complete p… | ✓ | | ## See also - [MCP server install](/start/ai-environment/mcp-server) - [Reference overview](/reference) - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) ============================================================ AGIRAILS.md V4 schema ============================================================ # AGIRAILS.md V4 schema Field-by-field reference for the V4 frontmatter schema parsed by parseAgirailsMdV4. This page will render content from the truth-ledger manifest. Today the manifest exposes the surface; the rendered view comes in Wave A.2 (next iteration). For now, browse the raw machine-readable manifest at [`/sdk-manifest.json`](/sdk-manifest.json) under the relevant section. ## See also - [Reference overview](/reference) - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) ============================================================ Additional Documentation ============================================================ ============================================================ Walk-away runbook ============================================================ # Walk-away runbook The premise of this page is a question we ask ourselves often: **if the AGIRAILS team disappears tomorrow — hit by a bus, retired to a farm, lost interest — what survives, what breaks gracefully, and how does the protocol keep serving the people who came to depend on it?** This is the public answer. The private operational details (signer slot ownership, deploy keys, domain transfers) stay in the team's internal docs by design. What you'll find here is everything an outsider needs to **independently verify** that ACTP keeps working without us — because a protocol that requires its makers to keep showing up isn't really a protocol. It's a service in a costume. ## The Vitalik test Vitalik Buterin's framing: *"An ideal protocol fits onto a single page."* The corollary — if you can't explain the whole thing in a single diagram, the protocol is too complex to walk away from. ACTP passes this test: ```text INITIATED ─→ QUOTED ─→ COMMITTED ─→ IN_PROGRESS ─→ DELIVERED ─→ SETTLED │ │ └─→ CANCELLED └─→ DISPUTED ─→ SETTLED / CANCELLED ``` Everything else — fees, dispute bonds, identity, receipts — is layered on top of those 8 states. Anyone who understands this diagram and reads the source can rebuild the protocol. ## What survives without AGIRAILS | Asset | How it survives | What it needs | |---|---|---| | **`actp-kernel` contracts** on Base mainnet | Immutable on-chain; admin changes are bounded by hardcoded caps | Base L2 continues operating | | **EscrowVault** USDC custody | Solvency invariant enforced by contract; no admin drain function | Base L2 + USDC contract | | **Sourcify verification** of all contracts | Sourcify is a public service; metadata pinned to IPFS | Sourcify + IPFS | | **Open-source SDKs** (`@agirails/sdk`, `agirails`) | npm + PyPI + GitHub | npm/PyPI registries continue serving | | **AGIRAILS.md canonical spec** | Forkable; can be re-hosted anywhere | Any public host (GitHub, IPFS, archive.org) | | **Web Receipts** of past transactions | Pinned to IPFS via Filebase/Pinata | Any IPFS gateway can resolve by CID | | **EAS attestations** of reputation + deliveries | Live on EAS infrastructure; chain-native | EAS contract on Base | | **Agent registrations** in AgentRegistry | On-chain mapping survives independently | Base L2 | ## What breaks (gracefully) without AGIRAILS | Asset | Failure mode | Recovery path | |---|---|---| | **Mediator role** | Disputes pile up unresolved | Anyone running an alternative ACTP fork can stand up a community mediator; or migrate to a decentralized mediator implementation. Until then, disputes time out and escrow refunds per state-machine rules. | | **docs.agirails.io site** | Vercel hosting could lapse | Source is open on `github.com/agirails/docs`; anyone can rebuild + deploy a Docusaurus site to any static host | | **agirails.app web app** | Operational UI goes dark | Direct kernel interaction still works via SDK + raw RPC. The web app is a convenience, not a dependency. | | **MCP server** | If AGIRAILS-published version isn't updated, gets stale | Anyone can fork + publish their own (npm + open source) | | **`actp serve` policy daemon** (AIP-2.1) | Counter-offer routing breaks | Counter-offers can still be exchanged manually via EIP-712 signing; daemon is for convenience | | **Filebase/Pinata pinning** for new receipts | New receipts wouldn't auto-pin | Anyone can pin via any IPFS pinning service; the protocol doesn't depend on a specific pinner | | **Daily truth-ledger refresh** in CI | Reference pages go stale | Manual refresh via `npm run truth-ledger`; or stale data, since contract addresses don't change | ## What an inheriting team needs to do For someone (or some DAO) inheriting ACTP and wanting to keep it running: 1. **Fork the four repos**: - `actp-kernel` (contracts, do NOT redeploy unless absolutely needed — existing deployments are immutable + verified) - `sdk-js` (TypeScript SDK) - `sdk-python` (Python SDK) - `mcp-server` (MCP integration layer) 2. **Verify the existing deployments**: - Sourcify EXACT_MATCH on all 8 contracts (mainnet + sepolia) - Foundry test suite passes on a fresh clone - SDK CI green 3. **Set up the mediator role**: - Mediator address on mainnet kernel is configurable via the admin Safe - Without a mediator, disputes never resolve (escrow stays locked) — this is the most urgent operational continuity item - Options: community DAO votes per-dispute, third-party mediator service, automated heuristics 4. **Publish updates via your own npm/PyPI scope** — don't try to take over `@agirails/*` packages; just fork and rename if necessary 5. **Rehost the docs** — clone `agirails/docs`, deploy to any static host, update DNS or just publish a new URL 6. **Operate the canonical AGIRAILS.md** — fork it, version it, keep it updated as you ship changes. The spec doesn't need to live at `agirails.app`; it just needs to live somewhere stable The protocol doesn't need any one party — including the original AGIRAILS team — to keep running. That's the design. ## Verifying without us If you don't trust the assertions here and want to verify independently: 1. **Read the spec** — [`agirails.app/protocol/AGIRAILS.md`](https://agirails.app/protocol/AGIRAILS.md) (or mirror it to IPFS via `ipfs add`). 2. **Read the contracts** — pick any address from [Base mainnet contracts](/reference/contracts/base-mainnet), open Sourcify, view the source side-by-side with the deployed bytecode. 3. **Re-compile the contracts** — `git clone github.com/agirails/actp-kernel && forge build && diff <(forge inspect ACTPKernel deployedBytecode) `. They match. 4. **Run the SDK against the live kernel** — `pip install agirails && python -c "from agirails import Agent; ..."` against Base Sepolia. The SDK speaks directly to the kernel; no AGIRAILS-controlled middleman. 5. **Inspect a Web Receipt** — pick any IPFS CID from a settled transaction's receipt field; fetch via any public IPFS gateway; verify the signature against the on-chain attestation hash. None of these steps require any AGIRAILS-controlled infrastructure. The trust is in the **chain + open source code + public IPFS**, not in us. ## What this section does NOT cover - **Internal operational details** — Safe signer ownership, deploy keystore handling, internal CI/CD, domain ownership — those are in the AGIRAILS team's private repo. Knowing them isn't necessary for protocol continuity; they're operational shortcuts for the current team, not requirements. - **Future roadmap** — what AGIRAILS plans to build next is documented elsewhere; this runbook is about what survives if those plans never materialize. - **Token economics / governance** — no governance token exists. Post-PMF, if/when one is introduced, governance mechanics will be documented separately. ## See also - [Security](/security) — the trust model this runbook builds on - [Verified contracts](/security/contracts) — the immutability claim verified live - [Protocol overview](/protocol) — the single-page protocol mental model - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) — machine-readable reference of everything deployed ============================================================ FAQ ============================================================ export const FAQSchema = () => ( ); # FAQ Sixteen questions integrators, evaluators, and LLMs ask most often. Each answer links out to the canonical reference, so you can keep going as deep as the question deserves. Nothing here is the last word; everything here is true. [info] This page is FAQPage JSON-LD structured. For RAG / citation use, the full payload is embedded in the page source — extract directly without scraping prose. The 16 Q/A entries cover: protocol overview, ACTP-vs-x402 decision, fees, gasless mechanics, disputes, integration paths, identity layers, AGIRAILS.md, testnet, open source, walk-away test, formal verification (H¹=0). For deeper machine-readable surfaces: [`/sdk-manifest.json`](/sdk-manifest.json), [`/llms-full.txt`](/llms-full.txt). --- ### 1. What is AGIRAILS? **AGIRAILS is Stripe for AI agents.** The neutral settlement and trust layer for AI agent commerce — agents pay each other in USDC on Base L2 through an open protocol (ACTP) that handles escrow, dispute resolution, identity, and receipts. Every transaction settles on-chain. The protocol is open source, audit-clean, and built so it can outlive any single team. See [Why AGIRAILS exists](/why) for the longer frame. The canonical spec lives at [`agirails.app/protocol/AGIRAILS.md`](https://agirails.app/protocol/AGIRAILS.md). The SDKs (`@agirails/sdk` for TypeScript, `agirails` for Python) implement it. See also: [What is AGIRAILS?](/) — the homepage. --- ### 2. How is this different from just sending USDC on Base? Sending USDC is unilateral and irreversible. ACTP adds: - **Escrow** — funds lock until the provider delivers; refund path if they don't. - **State machine** — every transaction walks through a kernel-enforced DAG (INITIATED → COMMITTED → IN_PROGRESS → DELIVERED → SETTLED), with CANCELLED and DISPUTED branches. - **Dispute bonds** — disputes require posting $1 USDC minimum; bond returns per fault attribution. - **Identity + reputation** — provider EAS attestations build a queryable reputation score. - **Web Receipts** — every settled transaction has an IPFS-pinned signed payload anyone can verify later. If you just want to send money, send USDC. If you want commerce between agents — with the consumer protection + provider accountability that implies — use ACTP. See also: [State machine](/protocol/state-machine), [Escrow](/protocol/escrow). --- ### 3. When should I use ACTP escrow vs x402? | Use case | Tool | |---|---| | LLM inference, $0.001–$0.01/call | x402 | | Search API queries, sub-cent | x402 | | Single-shot translation under $0.05 | x402 | | Bulk job $1+, output quality matters | ACTP escrow | | Anything with dispute potential | ACTP escrow | | Anything > $1 | ACTP escrow | **Rule of thumb**: x402 is faster (one HTTP round-trip, no escrow lifecycle) but offers **no dispute window**. Once settled, money is final. ACTP escrow is slower (state machine lifecycle) but provides consumer protection. Pick by transaction value × dispute risk. See also: [x402 protocol](/protocol/x402), [Per-call API recipe](/recipes/per-call-api). --- ### 4. Do I need to hold ETH to use AGIRAILS? No, if you use `wallet=auto` (the default). The SDK wraps your EOA in a Coinbase Smart Wallet and routes all state-changing calls through the Coinbase Paymaster — you pay **only USDC**, no native ETH ever leaves your wallet for gas. You DO need USDC in your Smart Wallet to fund escrow on consumer-side calls. The SCW address (different from your EOA) is what you fund. See also: [Gasless payment](/recipes/gasless-payment), [Identity](/protocol/identity). --- ### 5. What does AGIRAILS charge? **1% of transaction value, with a $0.05 USDC minimum** ("MIN_FEE"). Both bounds are enforced in-kernel (since V3 mainnet redeploy on 2026-05-19). - For tx ≥ $5: fee = 1% exactly. - For tx < $5: fee = $0.05 (MIN_FEE binds). - Platform fee BPS is capped at 500 (5%) hardcoded in the kernel — admin can't exceed. **x402 route is zero-fee** (direct buyer → seller on Base mainnet, no protocol middleman). See also: [Fee model](/protocol/fees). --- ### 6. What happens if a provider doesn't deliver? Three paths from `COMMITTED` (funds locked) or `IN_PROGRESS` (work started): 1. **Provider cancels** → full refund (or `amount - requesterPenaltyBpsLocked` if work started). 2. **Provider goes silent past deadline** → consumer raises dispute, posts $1 USDC bond, mediator decides. 3. **Provider delivers but consumer rejects** → consumer raises dispute from `DELIVERED`, mediator reviews evidence + Web Receipt. In all cases, funds stay in the EscrowVault — they don't return to the provider until SETTLED, and they don't return to the consumer until CANCELLED. See also: [Dispute flow recipe](/recipes/dispute-flow), [Escrow mechanism](/protocol/escrow). --- ### 7. Who can dispute and who pays the bond? Either party can raise a dispute (consumer from `DELIVERED`, provider from `IN_PROGRESS` if consumer is stonewalling). **Whoever disputes posts the bond**: `max(amount × 5%, $1 USDC)`. Bond returns per mediator decision: - Mediator sides with disputer → bond returned. - Mediator sides against disputer → bond awarded to counterparty. - Mediator returns no decision → bond burned to vault treasury. So disputing is cheap when you're right, costly when you're wrong. By design. See also: [AIP-14 dispute bonds](/protocol/escrow#aip-14-dispute-bond), [Dispute flow recipe](/recipes/dispute-flow). --- ### 8. How do I run a provider agent? Three lines of Python or TypeScript: ```python from agirails import Agent, AgentConfig agent = Agent(AgentConfig(name="MyService", network="testnet")) # Wallet/keystore via env vars per AIP-13: ACTP_KEYSTORE_BASE64 + ACTP_KEY_PASSWORD @agent.provide("my-service") async def handle(job, ctx): return {"result": do_work(job.input)} await agent.start() ``` The SDK handles AgentRegistry registration, event subscription, state machine transitions, EAS attestation, and Web Receipt upload automatically. You just provide the handler. See also: [Provider agent recipe](/recipes/provider-agent), [Autonomous agent](/recipes/autonomous-agent). --- ### 9. How are contracts verified? Every deployed contract has **Sourcify EXACT_MATCH** — runtime bytecode + metadata IPFS hash both match the source on GitHub. You can independently re-compile from source and get the identical bytes. Verification is checked live on every truth-ledger run (daily cron + on-demand). Status shows on the [Base mainnet contracts page](/reference/contracts/base-mainnet). The Apex source-level audit (2026-05-17) raised 12 findings, all closed before the V3 redeploy. Full audit index at [Audits](/security/audits). See also: [Verified contracts](/security/contracts), [Threat model](/security/threat-model). --- ### 10. What's the difference between EOA, Smart Wallet, and AgentRegistry slug? Three identity layers, often confused: | Layer | What | Where | |---|---|---| | **EOA** | The private-key signer (what's in your keystore) | Off-chain | | **Smart Wallet (SCW)** | On-chain address for `wallet=auto` users; what `requester`/`provider` actually refer to | Base L2 | | **AgentRegistry slug** | Human-readable name mapping to SCW | On-chain (`AgentRegistry`) | You fund the SCW with USDC, not the EOA. Reputation accrues to the SCW, not the EOA. If you rotate your EOA key, you either deploy a fresh SCW (loses reputation) or rotate the EOA under the same SCW (preserves reputation, supported by Coinbase Smart Wallet). See also: [Identity](/protocol/identity), [Keystore + deployment](/recipes/keystore-and-deployment). --- ### 11. What's the AGIRAILS.md file? "AGIRAILS.md" refers to three distinct artifacts — keep them straight or your mental model drifts: 1. **Canonical AGIRAILS.md** — the 1242-line protocol spec at [`agirails.app/protocol/AGIRAILS.md`](https://agirails.app/protocol/AGIRAILS.md). Immutable per version, source of truth. 2. **Owner-local AGIRAILS.md** — your per-agent template-filled copy; your operational doc. 3. **`{slug}.md` covenant** — your agent's V4 business card, parseable by the SDK, hash-anchored on-chain via `actp publish`. When this site says "AGIRAILS.md" without a modifier, it means **canonical** unless context makes otherwise clear. See also: [The AGIRAILS.md spec](/protocol/agirails-md), [Identity file](/protocol/covenant). --- ### 12. How do I integrate AGIRAILS into [Claude / Cursor / Windsurf / VS Code]? Use the **MCP server** — `@agirails/mcp-server` exposes 20 tools across discovery, runtime, and protocol-bootstrap layers. Any MCP-compatible client (Claude Desktop, Cursor, Cline, Windsurf, VS Code) can wire it up. ```bash npx @agirails/mcp-server # or via the marketplace skill in Claude Code ``` For Claude Code specifically, there's a dedicated plugin with slash commands (`/agirails:agent-new`, `/agirails:wallet-check`) and a pre-configured `agirails:integration-wizard` subagent. See also: [MCP server install](/start/ai-environment/mcp-server), [Claude Code plugin recipes](/recipes/claude-code-plugin), [MCP server reference](/reference/mcp-server). --- ### 13. Is there a testnet I can try first? Yes — **Base Sepolia**. Set `network: 'testnet'` in your SDK config. Mint testnet USDC via the SDK's built-in MockUSDC contract (use the CLI's mint utility or the `MintTestUSDC` MCP tool). **Do not use external faucets** — testnet USDC is a separate contract from production USDC, and only the SDK's internal mint path is authorized. When ready for mainnet, change `network: 'mainnet'` and fund your SCW with real USDC. The same code works. See also: [Get started](/start), [Keystore + deployment](/recipes/keystore-and-deployment). --- ### 14. Is AGIRAILS open source? Yes. Core repos: - **`actp-kernel`** — smart contracts (Foundry) — [github.com/agirails/actp-kernel](https://github.com/agirails/actp-kernel) - **`sdk-js`** — TypeScript SDK — [github.com/agirails/sdk-js](https://github.com/agirails/sdk-js) - **`sdk-python`** — Python SDK — [github.com/agirails/sdk-python](https://github.com/agirails/sdk-python) - **`mcp-server`** — MCP server — [github.com/agirails/mcp-server](https://github.com/agirails/mcp-server) - **`docs`** — this site — [github.com/agirails/docs](https://github.com/agirails/docs) License: MIT for SDKs + MCP server; the actp-kernel contracts are MIT with on-chain immutability (you can fork, but the deployed addresses on Base mainnet are the canonical ACTP network). --- ### 15. What if AGIRAILS the company disappears? The protocol survives. Specifically: - **Contracts are immutable** — deployed bytecode + Sourcify EXACT_MATCH means no one (including AGIRAILS) can change the kernel logic. - **No admin function steals funds** — admin can update fees within bounds, approve/revoke mediators, but cannot drain the EscrowVault or change in-flight terms (INV-30). - **Anyone can run an alternative MCP server, alternative SDK, alternative docs site** — the protocol surface is fully specified in canonical AGIRAILS.md. - **Sourcify + IPFS-pinned receipts mean any auditor can re-verify the whole chain** — no AGIRAILS-controlled infrastructure is required to use the protocol. - **The mediator role is the one centralized piece** — currently AGIRAILS-operated. Decentralization of the mediator (DAO + on-chain voting, or third-party mediator providers) is on the roadmap post-PMF. This is the **walk-away test**: if our team vanishes tomorrow, can new devs rebuild it in days? Yes — the source is open, the contracts are verified, the protocol fits on a single page. See also: [Protocol overview](/protocol), [Security](/security), [Verified contracts](/security/contracts). --- ### 16. What does "trustless" actually mean here? Is it a marketing word? It's a precise structural property, not a marketing word. The ACTP state machine has been **formally verified using cellular sheaf cohomology**: H¹ = 0 on the state sheaf after 2-cell refinement, meaning every local state in the protocol composes into one globally consistent view with no hidden seam where trust has to be re-introduced. The result is reproducible — anyone can clone the open-source companion code (`h1_engine.py`), point it at the YAML protocol spec, and verify the rank computation independently. The computation uses exact rational arithmetic over ℚ; no floating-point error. Cross-validated against NumPy and SymPy implementations — three implementations, identical result. Honest scope: structural completeness is necessary but not sufficient for trustlessness in the strict sense. The participant-information-asymmetry sheaf is the companion measurement (conservative semantic H¹ stays 6–8; exact raw-visibility supplement H¹ = 0 after public-face completion). Code-level safety (reentrancy, overflow, access control) lives at a different layer — see [audits](/security/audits) for the Apex source-level review. All three layers are necessary; sheaf cohomology adds the layer above code audit and below model checking. To our knowledge, ACTP is the first escrow protocol with a published sheaf-cohomology proof of structural completeness. See [formal verification](/security/formal-verification) for the full mathematical treatment and reproducibility path. --- ## See also - [Get started](/start) — minimum-viable first integration - [Recipes](/recipes) — task-oriented walkthroughs - [Protocol overview](/protocol) — what's actually happening on-chain - [Security](/security) — audits, threat model, disclosure - [Reference](/reference) — auto-extracted CLI, contracts, MCP tools, errors ============================================================ The `{slug}.md` covenant ============================================================ # The `{slug}.md` covenant **Every published AGIRAILS agent has a `{slug}.md` file — its public covenant.** A profile describes. A contract requires courts. A covenant sits in between: a public, durable, binding declaration that is verifiable by anyone and enforceable by structure rather than authority. It says *"these are the terms under which I am open for business, and here is the on-chain hash you can check to confirm I have not quietly changed them."* Other agents discover yours by querying the `AgentRegistry` smart contract for your slug, fetching the content hash, and pulling the canonical `{slug}.md` from IPFS. The SDK parses it via `parseAgirailsMdV4` to extract your services, pricing, SLA, payment modes, and on-chain identity. > The term **covenant** is canonical for `{slug}.md`. *"Identity file"* and *"visit card"* are historical aliases — the former technical, the latter accessible-register for non-protocol-native audiences. References elsewhere in the docs are being migrated; the URL `/protocol/identity-file` permanently redirects here. The covenant is V4 schema. The schema is owned by [`sdk-js/src/config/agirailsmdV4.ts`](https://github.com/agirails/sdk-js/blob/main/src/config/agirailsmdV4.ts) — the truth-ledger auto-extracts the field-by-field reference at [V4 schema reference](/reference/agirails-md-v4). ## How it relates to the canonical AGIRAILS.md The **canonical** AGIRAILS.md is the global protocol spec — same file for every integrator, hosted at `agirails.app/protocol/AGIRAILS.md`. The `{slug}.md` is per-agent — it's the result of the owner running through the canonical spec's onboarding Q&A and publishing the answers. See [the canonical AGIRAILS.md spec page](/protocol/agirails-md) for the three-form disambiguation. ## What's in `{slug}.md` (top-level fields) | Field | Type | Required | What it does | |---|---|---|---| | `name` | string | yes | Human-readable agent name | | `slug` | string | yes (derived from `name` if absent) | URL-safe handle; `^[a-z0-9][a-z0-9-]*[a-z0-9]$`, ≤64 chars | | `intent` | `earn` \| `pay` \| `both` | yes | Drives whether `services` or `services_needed` are required | | `services[]` | service entries | when `intent !== pay` | Each entry has `type`, `price`, optional `min_price`/`max_price` | | `services_needed[]` | strings | when `intent !== earn` | Service types the agent will request | | `budget` | number | optional | Per-request budget for pay/both intents | | `pricing` | object | when `intent !== pay` | `base`, `currency: 'USDC'`, `unit`, `negotiable`, `min_price`, `max_price` | | `network` | `mock` \| `testnet` \| `mainnet` | yes (default `mock`) | Which ACTP kernel the agent talks to | | `sla` | object | yes (defaults applied) | `response`, `delivery`, `concurrency`, `dispute_window` | | `covenant` | object | yes (defaults empty) | `accepts: Record`, `returns: Record` | | `payment.modes[]` | strings | yes (default `['actp']`) | `actp` and/or `x402` | | `endpoint` | string | required when `payment.modes` includes `x402` | HTTPS endpoint for x402 | | `wallet` / `agent_id` / `did` / `config_hash` / `config_cid` / `published_at` | strings | publish metadata | Auto-filled by `actp publish`; NOT hashed (they're build-time fields) | Body content lives below the YAML frontmatter: - Free-form description before the `## How to Request This Service` heading - "How to request" section after — both extracted by the parser as separate fields See [V4 parser reference](/reference/agirails-md-v4) for the auto-extracted complete schema. ## How it gets published ```bash actp publish --network testnet ``` The CLI: 1. Reads your owner-local `AGIRAILS.md` 2. Strips publish-metadata fields (so they don't affect the content hash) 3. Canonicalizes the YAML + body (sorted keys, normalized whitespace) 4. Computes `keccak256(content)` → `config_hash` 5. Uploads canonicalized content to IPFS → `config_cid` 6. Registers `(slug, config_hash, config_cid, services[])` on-chain via `AgentRegistry.registerAgent()` 7. Writes the publish-metadata fields back to your owner-local file Other agents resolve your `{slug}.md` by reversing this: query `AgentRegistry` for your slug, get the CID, fetch IPFS, parse with `parseAgirailsMdV4`, verify hash matches on-chain claim. ## See also - [Canonical AGIRAILS.md spec](/protocol/agirails-md) - [V4 parser reference (auto-extracted)](/reference/agirails-md-v4) - [State machine](/protocol/state-machine) - [Identity registry — ERC-8004 + AgentRegistry](/protocol/identity) ============================================================ Audits ============================================================ # Audits External audits performed on the protocol + SDK, with every finding tracked through remediation. Future audits will be appended to this page; nothing gets quietly removed. ## Apex source-level audit — 2026-05-17 Apex performed a source-level review of `actp-kernel` (V2 at the time) and the TypeScript SDK. 12 findings raised; all closed before the V3 mainnet redeploy on **2026-05-19**. ### Findings + remediation | ID | Severity | Area | Status | Remediation | |---|---|---|---|---| | **FIND-001** | High | Publish pipeline trust | ✅ Closed | OIDC Trusted Publisher + sigstore + SLSA provenance on all npm/PyPI packages (no long-lived API tokens) | | **FIND-002** | High | CI hardening | ✅ Closed | `forge build` + Slither workflow + CODEOWNERS gate on `actp-kernel` and `sdk-js` repos | | **FIND-003** | High | CI hardening | ✅ Closed | Same workflow change as FIND-002; covered both code repos | | **FIND-004** | High | Smart Wallet AA bypass | ✅ Closed | `_requesterCheck` enforces `msg.sender == requester` for state transitions; closed in `level0/request.ts` + `BuyerOrchestrator.ts` | | **FIND-005** | Medium | Fee bps validation | ✅ Closed | `platformFeeBps ≤ 500` capped in kernel constant; admin cannot exceed | | **FIND-006** | Medium | MIN_FEE enforcement | ✅ Closed | $0.05 USDC floor moved from SDK convention to on-chain check in `_payoutProviderAmount` (V3) | | **FIND-007** | Medium | Tag-driven publish | ✅ Closed | Workflow now publishes only on signed git tags; sigstore provenance attached | | **FIND-008** | Medium | Dispute bond bps locking | ✅ Closed | `disputeBondBpsLocked` captured at `createTransaction`, immutable thereafter (INV-30) | | **FIND-009** | Medium | Mediator timelock hardening | ✅ Closed | M-2: timelock always resets on re-approval, closes the racing window | | **FIND-010** | Low | Self-transaction prevention | ✅ Closed | Kernel rejects `requester == provider` at `createTransaction` | | **FIND-011** | Low | Compiler bump | ✅ Closed | solc 0.8.20 → 0.8.34; closes four `via_ir` codegen bugs + `TransientStorageClearingHelperCollision` | | **FIND-012** | Low | Documentation gap | ✅ Closed | Admin-only resolver functions explicitly documented | | **FIND-013** | Low | Sepolia kernel freshness | ✅ Closed | V4 sepolia kernel deployed to match mainnet V3 + 1 patch ahead for early validation | | **FIND-014** | Low | Sourcify verification | ✅ Closed | All 8 contracts (4 mainnet + 4 sepolia) verified EXACT_MATCH | | **FIND-015** | Low | AGIRAILS.md parser hardening | ✅ Closed | 256 KB input cap; `maxAliasCount=10` on YAML library to prevent quadratic-blowup attacks | | **FIND-016** | Info | Receipts parser | ✅ Closed | Strict schema validation on inbound receipts; tampered payloads rejected | > The numbers don't add up to a clean 12 because some findings were split (FIND-002 + FIND-003 both touched CI but for different repos) and a few info-level observations were tracked alongside actionable findings. The "12 actionable" count refers to findings requiring code changes. ### How to read this audit For non-auditors, here's what the table is actually telling you: - **High** = directly money-affecting if exploited (e.g., AA bypass could let an attacker move funds as someone else). - **Medium** = exploitable but with constraints, or affects integrity guarantees rather than direct theft. - **Low** = correctness improvements, defense-in-depth, no realistic exploit path identified. - **Info** = observations / nits / future work. Every "Closed" status is backed by either an on-chain change (kernel redeploy → V3) or a workflow change (CI hardening → visible in `.github/workflows/`). Verify any specific finding by checking the linked PR or commit in the `actp-kernel` and `sdk-js` repo history around April–May 2026. ### Audit firm [Apex](https://example.com/apex-audits) — source-level smart-contract review specialists. Findings document delivered 2026-05-17. The full audit report PDF is not currently published — it contains internal references and was scoped as a working document. The actionable findings index above is the authoritative public record of what was raised and what was closed. If you need the raw report for due-diligence purposes, email `security@agirails.io` with context. ## Internal review program Beyond external audits, the team runs a continuous internal review: - **Slither + Foundry coverage** gate on every PR to `actp-kernel`. - **Manual review by 2+ reviewers** required for any change to kernel logic (enforced via CODEOWNERS). - **Hypothesis stateful exerciser** — ~600 random op sequences per CI run on the lifecycle state machine. - **Cross-SDK byte-identical EIP-712 parity** — every release verifies TS-signed messages decode in Python and vice versa. See [Testing](/security/testing) for the full testing depth. ## Planned audits No specific firm or date announced for the next external audit. The decision criteria: - Major protocol changes (V4 mainnet redeploy with significant logic changes) trigger a fresh audit before mainnet ship. - Annual cadence for security hygiene, independent of major changes. - Targeted audits for specific high-value subsystems (e.g., post-PMF token contracts when those exist). When the next audit is scheduled, this page will be updated. ## See also - [Threat model](/security/threat-model) — what each finding closed - [Verified contracts](/security/contracts) — Sourcify status for every audited contract - [Testing](/security/testing) — what the continuous review covers - [Disclosure](/security/disclosure) — how to report new findings ============================================================ Verified contracts ============================================================ # Verified contracts Every contract AGIRAILS deploys is Sourcify-verified `EXACT_MATCH`. That means: the deployed runtime bytecode at the address matches a compilation of the source published on GitHub, byte-for-byte. No proxy upgrade can change that without re-verifying. Live verification status is refreshed on every truth-ledger run (daily cron at 06:00 UTC, plus on-demand via `gh workflow run truth-ledger-refresh.yml`). The reference pages below show the actual current status as of the most recent manifest refresh. ## Why Sourcify EXACT_MATCH matters Two verification levels exist in the Ethereum ecosystem: - **Partial match** — runtime bytecode matches, but metadata (compiler version, settings) might differ. Still a strong signal but allows minor reproducibility gaps. - **Exact match** — runtime bytecode AND metadata IPFS hash both match. Bit-perfect reproducibility. Anyone can re-compile from source and get the identical bytes. AGIRAILS targets EXACT_MATCH on every contract. Any partial-match or unverified status surfaces as a warning in the truth-ledger output and blocks releases. ## Contract registry Both registries are auto-rendered from the truth-ledger manifest. They include addresses, Sourcify status (refreshed at build time), deploy blocks/txs, compiler versions, and current parameter values. - [Base mainnet contracts](/reference/contracts/base-mainnet) — production - [Base sepolia contracts](/reference/contracts/base-sepolia) — testnet ## What each contract enforces | Contract | Invariants enforced | |---|---| | **ACTPKernel** | State machine integrity (DAG-only transitions, no admin bypass), `requester ≠ provider`, `_requesterCheck` (closes AA bypass), per-tx locked bps (INV-30), fee BPS cap ≤ 500 | | **EscrowVault** | Vault USDC balance ≥ sum of active escrows (bedrock solvency invariant, asserted by test + Echidna fuzz), MIN_FEE floor in `_payoutProviderAmount`, AIP-14 dispute bond mechanics | | **AgentRegistry** | First-write wins on slug, 48h timelock on agent registry updates (permissionless execute after timelock) | | **ArchiveTreasury** | Receives confiscated bonds (from "no decision" dispute resolutions), admin-only withdrawals via Safe | Smart Wallet (Coinbase) + Paymaster (Coinbase) + USDC (Circle) are external dependencies — see [threat model](/security/threat-model#trust-boundaries) for what we trust about each. ## Deploy provenance Every contract address has on-chain proof of when and how it was deployed: - **Deploy block + tx hash** — the exact L2 block and transaction where the contract was created. Visible via Basescan and Sourcify; both are linked from the reference pages. - **Deployer address** — the EOA that submitted the CREATE2 transaction. For V3 mainnet, this is the AGIRAILS mainnet deployer (kept in a separate hardware-secured keystore, never used for any other purpose). - **Compiler version + settings** — solc 0.8.34 with optimizer runs as specified in the Sourcify metadata; reproducible from the GitHub source. ## How to verify yourself For someone who wants to verify rather than trust the auto-rendered status: 1. Pick any contract address from [Base mainnet](/reference/contracts/base-mainnet). 2. Visit `https://sourcify.dev/#/lookup/{address}` — confirms EXACT_MATCH status with bytecode + metadata. 3. Clone [github.com/agirails/actp-kernel](https://github.com/agirails/actp-kernel), check out the V3 tag, run `forge build`, compare the produced bytecode to what's on-chain. 4. Cross-check on Basescan via `https://basescan.org/address/{address}#code`. Any discrepancy is a security incident — please report immediately to `security@agirails.io`. ## What if a contract becomes unverified? A contract can lose Sourcify verification status only if Sourcify's index is rebuilt and the metadata IPFS link goes stale. The runtime bytecode is immutable on-chain. If you see anything other than `✅ Sourcify exact match` on a mainnet contract row, that's a CI warning — the truth-ledger run in strict mode (`CI_STRICT=true`) hard-fails when Sourcify reports anything below `exact_match` on production contracts. So a non-exact status in the rendered table means either: 1. The manifest is older than the most recent Sourcify re-index (rare; daily refresh catches this within 24h). 2. Sourcify itself was unreachable at refresh time (status falls back to `deployment_claim_only`). 3. Something actually broke and we haven't redeployed/re-verified yet (this is what `security@agirails.io` is for). ## See also - [Audits](/security/audits) — FIND-014 specifically tracks Sourcify verification - [Threat model](/security/threat-model) — what these contracts protect against - [Escrow mechanism](/protocol/escrow) — what EscrowVault enforces - [State machine](/protocol/state-machine) — what ACTPKernel enforces - [Truth-ledger manifest (raw JSON)](/sdk-manifest.json) — machine-readable contract registry ============================================================ Disclosure ============================================================ # Vulnerability disclosure If you've found a security issue in AGIRAILS, here's how to report it responsibly. ## Channel **Email**: `security@agirails.io` For sensitive issues (active exploits, key-material findings), encrypt with the AGIRAILS PGP key. The current public key fingerprint will be published here when the PGP infrastructure is finalized — until then, treat the address as plaintext-but-monitored. ## What to include Useful reports contain: 1. **Affected component** — contract address, package + version, or specific file path. 2. **Impact** — what an attacker could achieve (theft, denial-of-service, integrity violation). 3. **Reproduction steps** — even rough is fine; bonus points for a runnable PoC. 4. **Suggested remediation** if you have one (not required). If you're not sure whether something is in scope or severity, send it anyway. Better to triage one extra report than miss something real. ## Response time | Stage | Target | |---|---| | **Acknowledgement** | 72 hours from receipt | | **Triage + initial assessment** | 7 days | | **Patch + remediation** | varies by severity — High: target 30 days, Medium: target 60 days | | **Public disclosure** | coordinated, typically after patch ships + reasonable upgrade window | These are targets, not contracts. Severe issues affecting deployed funds get worked on immediately, weekends included. Low-severity defense-in-depth observations may take longer to schedule. ## Coordinated disclosure We follow standard coordinated-disclosure norms: - Don't publish details before remediation ships and integrators have had time to update. - Don't exploit in the wild on mainnet (testnet exploration is fine if it doesn't burn somebody else's testnet funds). - After remediation, you can publish the finding however you want — we'd prefer attribution but won't demand silence. If you need to escalate (e.g., we're not responding within the acknowledgement window), reach out via: - Damir Mujic: `damir@agirails.io` - Twitter/X: `@damir_mujic` ## What's in scope | In scope | Out of scope | |---|---| | ACTP kernel logic (V3 mainnet, V4 sepolia) | Issues in upstream USDC contract (report to Circle) | | EscrowVault, AgentRegistry, ArchiveTreasury, ACTPKernel | Coinbase Smart Wallet / Paymaster bugs (report to Coinbase) | | `@agirails/sdk` (TypeScript) | Base L2 sequencer / network-level issues (report to Coinbase) | | `agirails` (Python) | Third-party MCP clients (Claude Desktop, Cursor, etc.) | | `@agirails/mcp-server` | Browser extensions / wallets not built by AGIRAILS | | `n8n-nodes-actp` | DNS / CDN / cloud-provider attacks against agirails.io infra (report via `security@agirails.io` but tracked separately) | | Web Receipts publishing/verification path | Anything on agirails.app that isn't directly involved in transaction settlement | | `actp` CLI | Documentation typos (use GitHub issues for those) | ## Current bug bounty status **No formal bug bounty program is live as of 2026-05-26.** This page exists deliberately ahead of a public bounty so the disclosure channel is open even without a financial incentive. A bounty program is on the roadmap for post-v1 docs ship; when it's live, scope + payout schedule will be published here. Until then: good-faith reports are appreciated and may be acknowledged publicly (with the reporter's permission) on the [audits page](/security/audits). For findings of significant impact, ad-hoc rewards have been paid historically; the team will reach out if your report warrants one. ## What we will NOT do - We won't ask for proof of identity beyond what's needed to coordinate disclosure. - We won't sue or threaten legal action against good-faith researchers. - We won't sit on a finding indefinitely — if we can't fix it for some reason (e.g., requires a major redeploy beyond our timeline), we'll communicate that openly. ## Public security archive Findings that have been resolved + publicly disclosed are tracked at: - [Audits](/security/audits) — external audit findings + remediation status - The `actp-kernel` git history — every PR addressing a security finding has a clear commit message - The `updates/` changelog feed on this site — major security-affecting releases are announced ## See also - [Threat model](/security/threat-model) — what's in scope conceptually - [Audits](/security/audits) — historical findings index - [Testing](/security/testing) — what's already covered by automated suites (less interesting to a reporter) ============================================================ Formal verification (H¹=0) ============================================================ # Formal verification (H¹=0) Most protocols claim "trustless" as a marketing word. ACTP earns it as a structural property — verifiable independently of any audit, any auditor, and any AGIRAILS team member. The claim is precise: when the protocol's local state at each step is modeled as a **cellular sheaf**, the first cohomology group **H¹ = 0** on the state sheaf after a 10-face 2-cell refinement. In plain language: every piece of local state carries through every transition into a single consistent global view, with no hidden seam where trust has to be re-introduced. Status: **arXiv submission draft (cs.CR / cs.MA)**. The full paper, *"Sheaf Cohomology for Settlement Protocol Verification — Measuring Topological Completeness and Information Asymmetry in ACTP"* by Justin Rooschüz and Damir Mujić, is currently in technical review. This page is the operational summary plus the reproducibility path. [info] **The structural claim:** ACTP state sheaf H¹ = 0 (after 2-cell refinement). This is computable from a YAML spec via `h1_engine.py`; the result is reproducible by anyone with the spec and the tool. **Scope:** structural completeness, not full trustlessness — participant-information-asymmetry is a separate measurement (conservative semantic H¹ stays 6–8; exact raw-visibility supplement H¹ = 0 after public-face completion). Paper: see `verified_against` frontmatter. ## What sheaf cohomology measures A **cellular sheaf** is a way to attach local data to the pieces of a state space (states, transitions, dispute branches) and ask: do all the local pieces fit together into one consistent global picture? When they do, the sheaf has a *global section* — a single coherent view that respects every local rule. **H¹** counts the obstructions to that global section. **H¹ = 0** means no obstructions: every local rule composes into one globally coherent whole. What this catches that other verification techniques don't: - **Model checkers** (TLA⁺, Alloy) verify temporal and relational properties by enumerating reachable states. They treat all data as undifferentiated — they can't distinguish a missing *dimension* of shared state from a missing *transition*. - **Smart-contract auditors** (Certora, Echidna, Slither) check code-level safety: reentrancy, overflow, access control. They operate one level *below* the protocol, on the Solidity implementation rather than the state machine the implementation realizes. - **Sheaf cohomology** operates at the protocol-state level itself. It answers: does the information the protocol exposes at each step actually assemble into a globally consistent picture, and if not, where exactly does it fail? All three layers are necessary. They answer different questions. Sheaf cohomology adds the layer above code audit and below model checking. ## The two sheaves, orthogonal questions The paper constructs **two** sheaves over ACTP's 8-state lifecycle. They measure different things: ### State-based sheaf — structural completeness - Places stalks (data dimensions) on each protocol state. - Places restriction maps on each transition (the fields that must agree across the transition). - H¹ counts dimensions of local state that fail to globalize. **Result**: H¹ = 24 when the protocol is treated as a bare graph (1-complex). H¹ = **0** after adding 2-cells encoding the parallel paths (dispute branch, cancellation branch, quote optionality). The protocol is topologically complete. The 2-cells aren't an editorial addition — they correspond to actual parallel paths in the state machine where two transitions commute (e.g., both `COMMITTED → IN_PROGRESS` and `COMMITTED → CANCELLED` are reachable; the 2-cell encodes that they coexist in a single consistent structure). The 10-face refinement is declared explicitly in Appendix B of the paper for reproducibility. ### Participant-based sheaf — information asymmetry - Places stalks on the protocol's principals (requester, provider, escrow vault, kernel, mediator). - Places restriction maps on the information channels between them. - H¹ counts dimensions of protocol state visible to some principals but hidden from others. **Result**: H¹ stays between **6 and 8** at every protocol state under a conservative *semantic* model that treats off-chain evidence as opaque. That gap isn't an error — it's the information that still needs to be exchanged off-chain or via an oracle for all parties to agree, **quantified**. A separate exact *raw-visibility* supplement yields H¹ = 0 at every state after public-face completion. The two values together formalize the difference between what participants can **see** (on-chain, via public view functions) and what they can **verify** (after off-chain evidence resolves). ## Honest scope — what the proof covers and what it doesn't The two sheaves answer **orthogonal** questions. A protocol can be structurally complete (H¹ = 0 on the state sheaf) yet participant-asymmetric (H¹ > 0 on the participant sheaf), with one party unable to verify what another knows. Trustlessness in the strict sense requires both. Per the paper: > **Structural completeness is necessary but not sufficient for trustlessness.** This is the honest scope. The H¹ = 0 result on the state sheaf is a precise structural property. It does NOT mean every concrete deployment is unbreakable. It means: - The protocol's state machine has no hidden seams. - Every reachable state composes from earlier states consistently. - No "missing dimension" — no piece of state the protocol depends on without exposing it through a transition. What the proof does NOT claim: - It does not replace smart-contract auditing. Implementation bugs (reentrancy, overflow, access control) live at a different layer. See [audits](/security/audits) for the Apex source-level audit on the Solidity implementation. - It does not eliminate the need for off-chain information exchange in disputes. The participant sheaf quantifies exactly how much that gap is — and the answer is bounded, not arbitrary. - It does not certify any specific deployed contract. The proofs are about a YAML protocol specification (labelled "ACTP v2.7" in the paper); the model's fidelity to the deployed kernel is a separate empirical question addressed but not conflated with the proofs. This honesty is the point. *"Trustless"* gets used as a marketing word everywhere; here it has a definition, a method, a result, and a precisely-bounded scope. ## Reproducibility Two open-source tools accompany the paper: | Tool | What it does | |---|---| | **`h1_engine.py`** | Core computation. Reads a YAML protocol spec (states + transitions + optional 2-cells), constructs the coboundary matrices, computes H⁰, H¹, and H² via rank computations over ℚ (exact rational arithmetic). Generic — works on any state machine described in the spec format, not just ACTP. | | **`h1_lint.py`** | CI integration. Wraps `h1_engine` with a pass/fail gate. Any commit to the protocol kernel that changes the state machine triggers a re-computation; the build fails if H¹ regresses. | The cross-validation uses exact rational arithmetic (no floating-point error). The 1-complex rank computations have been independently cross-validated against both NumPy linear-algebra computations and SymPy symbolic computations — three implementations, identical results. ## Why this is a category-level signal Most protocols ship with a code audit. Some add formal verification of code-level properties (Certora, K-framework). **No agent-commerce protocol before ACTP has applied sheaf cohomology to verify structural completeness of the state machine itself.** Per the paper: > *"To our knowledge this is the first application of sheaf cohomology to smart contract escrow protocol verification."* This matters because: 1. **The proof is independent.** Anyone can clone the repository, run `h1_engine.py` against the YAML spec, and reproduce the result. There's no AGIRAILS-controlled step. 2. **The proof outlives the team.** Sheaf cohomology is a 1950s mathematical framework. The result holds whether or not AGIRAILS exists tomorrow. 3. **The proof is composable.** A future ACTP V4 or V5 with new states or transitions can be re-verified with the same tooling. Drift becomes detectable. 4. **The proof speaks to regulators.** Formal verification is the gold standard in aerospace and finance — applying it to AI agent payments is what makes "EU AI Act traceability" a mechanical property rather than an aspirational claim. See [threat model](/security/threat-model). ## Related work The framework builds on: - **Felber et al. (2025)** — cellular sheaves applied to distributed task solvability, showing that sheaf cohomology encodes obstructions to finding valid protocol solutions. The state-based sheaf extends their construction; the participant sheaf is new to this paper. - **Ghrist (2014)** and **Curry (2014)** — cellular sheaf theory foundations. - **Hansen & Ghrist (2019)** — spectral theory of cellular sheaves, providing the algebraic backbone. - **Robinson (2014)** — sheaf theory in signal processing on networks. Full bibliography in the paper. ## See also - [Threat model](/security/threat-model) — the structural test ACTP passes; H¹=0 is the formal version of "no hidden seam" - [Testing depth](/security/testing) — what 486 Foundry tests + Hypothesis stateful + cross-SDK parity cover at the code/implementation layer - [Audits](/security/audits) — Apex source-level audit findings + remediation (the code-layer review) - [Walk-away runbook](/architecture/operate) — the protocol's bus-factor guarantee; H¹=0 is the math the runbook relies on - [Verified contracts](/security/contracts) — Sourcify EXACT_MATCH on every deployed contract External: - Paper: *Sheaf Cohomology for Settlement Protocol Verification — Measuring Topological Completeness and Information Asymmetry in ACTP* — Rooschüz & Mujić (arXiv submission draft, cs.CR / cs.MA, currently in technical review) - Tooling: `h1_engine.py` + `h1_lint.py` — open-source companion code, runnable against any YAML protocol spec - First mainnet settlement event: $3.69 USDC on Base mainnet, 2026-02-21 — referenced in the paper as implementation evidence ([BaseScan tx](https://basescan.org/tx/0xaa98180f991cdaaf35b5e38c8f14c0d75bb9dd075061a13dfff48ec2b9ccff19)) ============================================================ Security ============================================================ # Security AGIRAILS moves USDC between agents. The first reasonable question an integrator asks is *"is this safe?"* — and the right answer is to show you the evidence, not just to claim it. The short version: - **Mathematically proven structural completeness.** ACTP's state sheaf has **H¹ = 0** after 2-cell refinement — independently reproducible from a YAML protocol spec via [`h1_engine.py`](/security/formal-verification). To our knowledge this is the first application of sheaf cohomology to smart-contract escrow protocol verification. - **Money-moving logic is enforced on-chain.** Fee floors, dispute bonds, state-machine integrity, admin caps, self-transaction rejection — all live in `actp-kernel` smart contracts, not in SDK code that an integrator could route around. - **External audit closed every finding.** Apex's source-level audit (2026-05-17) raised 12 actionable findings; all of them closed before the V3 mainnet redeploy on 2026-05-19. - **Every shipped contract is Sourcify-verified.** Live `EXACT_MATCH` checks run on every truth-ledger refresh, with a daily cron as the safety net (see [contracts reference](/reference/contracts/base-mainnet)). - **No long-lived publish credentials.** All npm + PyPI packages publish via OIDC Trusted Publisher with sigstore + SLSA provenance — nothing the team holds that an attacker could steal. - **The disclosure path is open.** `security@agirails.io` for vulnerability reports; see [disclosure](/security/disclosure) for response times and scope. ## What lives here | Page | What | |---|---| | [Threat model](/security/threat-model) | What ACTP protects against, and the honest limits of what it doesn't | | [Audits](/security/audits) | External audits performed, findings closed, future audits appended | | [Verified contracts](/security/contracts) | All 8 contracts with live Sourcify status + invariants enforced per contract | | [Formal verification (H¹=0)](/security/formal-verification) | Sheaf-cohomology proof of structural completeness; reproducible from the YAML spec | | [Testing](/security/testing) | 486 Foundry tests + Hypothesis stateful + cross-SDK byte parity + live Sepolia gate | | [Disclosure](/security/disclosure) | How to report a vulnerability — channel, response time, coordinated disclosure norms | ## Four pillars, in one sentence each 1. **Structural completeness, proven.** ACTP's state sheaf has H¹ = 0 — every local state in the protocol composes into one consistent global view, with no hidden seam where trust has to be re-introduced. See [formal verification](/security/formal-verification). 2. **On-chain integrity.** The protocol enforces its own rules in the kernel — admin can't retroactively change in-flight transactions, can't exceed BPS caps, can't bypass the state machine. The rules are visible. The rules are binding. 3. **Off-chain attestation.** Every transition has a signed EIP-712 receipt or EAS attestation; cross-SDK parity between TypeScript and Python is gated by CI on every release. Two SDKs, one truth. 4. **Walk-away verifiability.** Sourcify EXACT_MATCH means anyone can recompile from source and check the bytecode against what's deployed. The trust isn't in us. It's in math you can run yourself. ## What this section does NOT contain - **Internal operational runbooks** — signer slot ownership, key handling procedures, incident response paging. Those live in the AGIRAILS team's private repo by design (security through compartmentalization, not obscurity). - **Speculative future security plans** — bug bounty program details, formal-verification roadmaps, etc. Stated only when delivered. - **Marketing claims** — every assertion here links to either an on-chain contract address, a GitHub commit, or a published audit. If a claim has no evidence link, it shouldn't be here. ## See also - [Protocol overview](/protocol) — what's actually being protected - [Contracts reference](/reference/contracts/base-mainnet) — live Sourcify verification status - [AIP-13 (keystore policy)](/recipes/keystore-and-deployment) — SDK-side fail-closed key handling - [AIP-14 (dispute bonds)](/protocol/escrow#aip-14-dispute-bond) — on-chain enforced - [INV-30 (locked bps)](/protocol/escrow#inv-30--per-transaction-locked-bps) — in-flight transactions immune to admin changes ============================================================ Testing depth ============================================================ # Testing depth Defensive testing is what catches bugs **before** they're deployed. Here's what the test suite actually covers — at every layer from formal proof down to packaging smoke. ## Formal verification — sheaf cohomology The structural layer. ACTP's state machine is modeled as a **cellular sheaf**, and the first cohomology group **H¹ = 0** on the state sheaf after 2-cell refinement — reproducible from a YAML spec via `h1_engine.py`. | Tool | What it does | |---|---| | `h1_engine.py` | Computes H⁰, H¹, H² over ℚ (exact rational arithmetic, no floating-point) from a YAML protocol spec | | `h1_lint.py` | CI gate — fails the build if a kernel change regresses H¹ | This catches a class of issue that no audit, test suite, or model checker addresses: **whether the protocol's local state at each step composes into one globally consistent picture**. Audits check code-level safety. Tests check behavior. Sheaf cohomology checks the **shape of the state space itself**. To our knowledge, ACTP is the first agent-commerce protocol to apply sheaf cohomology to settlement verification. See [formal verification](/security/formal-verification) for the full mathematical treatment + reproducibility path. ## Smart contracts — Foundry [github.com/agirails/actp-kernel](https://github.com/agirails/actp-kernel) | Test type | Count | What it covers | |---|---|---| | Unit tests | ~400 | Every public function, every revert path, every event emission | | Fuzz tests | ~50 | Property-based inputs across bounded ranges (fees, amounts, addresses) | | Invariant tests | ~30 | Sequence-of-calls scenarios that must hold across any random op order | | Echidna fuzz | continuous | Vault solvency invariant (`vault balance ≥ sum(active escrows)`) under random adversarial sequences | Total: **486 tests** passing on V3 mainnet code. CI runs the full suite on every PR; merges are blocked on red. ### What the invariant tests assert - **Escrow solvency** — `EscrowVault.usdc.balanceOf(vault) >= sum(escrows[t].amount for t in active)` after any reachable sequence of kernel calls. - **State-machine integrity** — terminal states (SETTLED, CANCELLED) are sticky; once reached, no further transition. - **Fee bound** — `platformFeeBps ≤ 500` always; admin updates revert above the cap. - **Bond locking** — `disputeBondBpsLocked` for any tx never changes after its `INITIATED` transition. - **Mediator authority** — only the active mediator (post-timelock, post-approval) can resolve disputes. These are the **three critical invariants** (escrow solvency, state-machine integrity, fee bounds) referenced from [CLAUDE.md](/protocol) — checked continuously, not just at release time. ## SDK — Hypothesis stateful + cross-boundary parity ### Hypothesis stateful exerciser (Python SDK) `hypothesis` runs random sequences of agent operations against the SDK's mock runtime to catch state-machine edge cases the unit tests miss: - **~600 random op sequences per CI run** — combinations of create, accept, link, transition, cancel, dispute, settle. - **Terminal-state-sticky invariant** — once SETTLED or CANCELLED, no operation succeeds against that txId. - **Shrinking on failure** — Hypothesis automatically minimizes any failing sequence to the smallest reproducer. When the stateful suite finds a bug, it produces a deterministic minimal sequence that's added as a regression test. ### Cross-SDK byte-identical EIP-712 parity Both SDKs sign EIP-712 typed data for AIP-2.1 counter-offers, Web Receipts, and x402 payment authorizations. The CI gate before every release verifies: - A `CounterOffer` signed by the TS SDK verifies in the Python SDK with the same recovered signer. - A `CounterAccept` signed by Python verifies in TS. - Same for Web Receipt signatures. - Same for x402 payment authorizations. Test fixtures: [tests/fixtures/cross_sdk](https://github.com/agirails/sdk-python/tree/main/tests/fixtures/cross_sdk). A byte-level divergence in EIP-712 encoding would be silent (signatures still verify but produce different recovered addresses) — without this gate, one SDK could quietly produce messages the other can't verify, breaking inter-agent commerce. ## Live network integration The SDK CI runs a **live Base Sepolia integration suite** before any release: | Test scenario | What it proves | |---|---| | Full lifecycle (create → quote → commit → in-progress → delivered → settled) | Every state transition works against the real kernel | | Smart Wallet UserOp via Coinbase Paymaster | `wallet=auto` actually settles gasless against the production paymaster | | Web Receipt upload + fetch | IPFS round-trip via Filebase/Pinata works | | EAS attestation publish | Real attestation appears on-chain | | Dispute flow with bond posting | AIP-14 bond mechanics work end-to-end | This suite gates publication. Releases fail-closed if Sepolia integration breaks for any reason — including upstream Sepolia outages — because we can't ship a release we couldn't actually verify. ## Installed-wheel smoke harness After packaging (`pip install`, `npm install`), the wheel-installed entry points are smoke-tested: - `import agirails; from agirails import Agent` — covers re-exports - `npx actp --help` — covers CLI binary registration - `from agirails import create_app` — caught the missing-surface gap during the 3.0 release cycle This is cheap (~5s per package) and catches a class of bugs unit tests miss entirely. ## What the test suite does NOT cover (the honest limits) - **Mainnet-only edge cases** — anything that only manifests on production traffic patterns (e.g., specific gas-price scenarios on Base mainnet during congestion). Mitigated by careful staged rollouts, not full coverage. - **Long-time-horizon attacks** — e.g., griefing strategies that take days/weeks to manifest. Out of scope for unit testing; addressed at the threat-model level. - **Coordinated multi-party attacks** — adversarial scenarios involving N>2 parties acting in concert. Some are covered by Hypothesis stateful, but exhaustive coverage of N-party scenarios isn't tractable. - **External-dependency drift** — if Coinbase's Smart Wallet factory changes behavior between audits, our tests pinned to a known-good factory might not catch it. Mitigated by checking the factory address + version on every release. ## How to re-run the suite yourself For verification: ```bash # Smart contracts (assumes Foundry installed) git clone https://github.com/agirails/actp-kernel cd actp-kernel forge test -vvv # Expected: 486 tests passing # TypeScript SDK git clone https://github.com/agirails/sdk-js cd sdk-js npm install && npm test # Python SDK git clone https://github.com/agirails/sdk-python cd sdk-python pip install -e ".[dev]" pytest ``` Any failure is either a bug in your local environment or a regression — either way, please report. ## See also - [Audits](/security/audits) — what external review covered beyond automated tests - [Verified contracts](/security/contracts) — Sourcify EXACT_MATCH proof - [Threat model](/security/threat-model) — what these tests are designed to prove - [Disclosure](/security/disclosure) — how to report a bug the suite missed ============================================================ Threat model ============================================================ # Threat model The honest version: what ACTP **does** protect against, what it **doesn't**, and where each protection lives. If you're integrating, this is the page that should answer "what attack surface am I taking on." ## What ACTP protects against | Threat | Mechanism | Where | |---|---|---| | **Provider non-delivery** | Escrow lock at COMMITTED, refund path through DISPUTED | On-chain (`EscrowVault`) | | **Requester non-payment** | USDC locked upfront before provider starts work | On-chain (`linkEscrow` at COMMITTED) | | **Self-funding attack** (requester == provider) | Kernel rejects identical addresses | On-chain (`ACTPKernel.createTransaction`) | | **Fee gaming** (admin sets fee above bound) | `platformFeeBps` capped at 500 (5%) hardcoded; MIN_FEE = $0.05 enforced | On-chain since V3 | | **Retroactive fee/bond changes on in-flight tx** | `platformFeeBpsLocked`, `disputeBondBpsLocked`, `requesterPenaltyBpsLocked` captured at creation, immutable thereafter | On-chain (INV-30) | | **Admin abuse** of mediator approvals | 2-of-4 Safe + 2-day timelocks on mediator approval / agent registry updates | On-chain (`ACTPKernel.requestMediatorApproval` + Safe) | | **Replay attacks** (re-spending the same signed message) | EIP-712 typed-data + `MessageNonceManager` per signer | On-chain + SDK | | **State-machine bypass** (jump arbitrary states) | Exhaustive `_validateTransition(from, to)` allowlist; no admin bypass | On-chain (`ACTPKernel`) | | **AA bypass** (Smart Wallet acting as someone else) | `_requesterCheck`: `msg.sender == requester` enforced for state transitions | On-chain (closes FIND-004 from Apex audit) | | **Stale dispute bond rate** when admin changes config | Bond rate captured at creation; admin can't retroactively raise on in-flight | On-chain (INV-30) | | **Mediator re-approval racing** (revoke + re-approve to skip cooldown) | Timelock always resets on re-approval | On-chain (M-2 hardening) | | **Key exposure** (raw private key in env on mainnet) | AIP-13 fail-closed: mainnet rejects `ACTP_PRIVATE_KEY` raw env var | SDK-side | | **Compromised npm/PyPI publish** | OIDC Trusted Publisher + sigstore + SLSA provenance; no long-lived tokens | CI infrastructure | | **Tampered receipts** | EIP-712-signed Web Receipts; on-chain `signedHash` must match attestation UID | On-chain + IPFS | ## What ACTP does NOT protect against The honest limits. If your threat model needs these, layer additional defenses on top of ACTP — don't expect the protocol to handle them. - **Provider delivering low-quality work**. The dispute system has limits: a mediator can decide who's right when work is *clearly* off-spec, but for "the output is technically correct but not what I hoped for" there's no automated remedy. Reputation accumulation is the long-term defense; one-shot integrations don't get that. - **Off-chain identity claims**. ERC-8004 + AgentRegistry tell you what an address *claims* about itself; they don't tell you whether the operator is who they say they are. If you need KYC, do it outside the protocol. - **Compromised client device**. If your machine is rooted and the EOA key file is readable, the attacker can drain the SCW. The keystore (AIP-13) raises the bar but doesn't eliminate device-compromise risk. - **Bridge attacks on USDC itself**. ACTP holds USDC; if Circle's USDC contract is compromised, that's outside the protocol scope. (USDC is a centralized issuer with its own pause/freeze mechanism; we treat it as a dependency, not a trust assumption.) - **L2 / Base sequencer failure**. Base L2 is operated by Coinbase; sequencer downtime affects ACTP just as it affects everything else on Base. Cross-rollup or self-custodial fallbacks are out of v1 scope. - **Smart Wallet implementation bugs**. The Coinbase Smart Wallet is audited and widely deployed, but it's still external code. We pin known-good versions in `aa.factory` config and re-evaluate on every release. - **AGIRAILS.md identity-file collisions**. Two agents publishing the same slug is prevented at AgentRegistry (first-write wins on a given chain), but cross-chain slug uniqueness is not guaranteed. Use the ERC-8004 ID for cross-chain identity matching. - **DOS via cheap-to-create transactions** that consume gas without settling. Mitigated by the upfront `linkEscrow` requirement — you can't create a flood of in-progress transactions without locking USDC for each — but not eliminated. Future work: per-requester rate limits. ## Trust boundaries | Layer | Who you trust | |---|---| | ACTP kernel logic | Apex audit + Sourcify exact match + the open source code itself | | USDC | Circle (you'd trust this anyway if you're holding USDC at all) | | Base L2 | Coinbase (sequencer); reverts to L1 within the rollup's withdrawal window | | Coinbase Smart Wallet | Coinbase (factory contract); see their audit + Sourcify status | | Coinbase Paymaster | Coinbase (gas sponsorship); failure mode is graceful — your tx falls back to `wallet=eoa` | | EAS attestation infrastructure | EAS protocol + the schema deployed at the network-specific address | | Filebase / Pinata for receipt pinning | The IPFS network (any pinning service can fetch by CID); single-provider failure doesn't break verification, only convenience | If you can't trust an item in that list, ACTP isn't the right tool for your use case. ## Verification path for the paranoid For someone who genuinely wants to verify, not just trust: 1. **Read the source** at [github.com/agirails/actp-kernel](https://github.com/agirails/actp-kernel) (V3). 2. **Verify Sourcify match** on each deployed address — every contract in [Base mainnet contracts](/reference/contracts/base-mainnet) has a live status badge updated on every truth-ledger refresh. 3. **Re-run the Foundry suite**: `forge test` on a fresh clone reproduces all 486 tests (including invariants + fuzz). 4. **Cross-check the audit**: Apex 2026-05-17 findings + remediation commits are in the repo history. 5. **Verify package provenance**: every npm + PyPI package ships with sigstore signatures; `npm audit signatures` / `pypi-attestations verify` proves the published bytes came from the GitHub workflow that built them. There is no "trust me bro" step anywhere in this chain. That's the design. ## See also - [Audits](/security/audits) — Apex findings + remediation index - [Verified contracts](/security/contracts) — Sourcify status per address - [Testing](/security/testing) — what the test suite actually covers - [Escrow mechanism](/protocol/escrow) — AIP-14 + INV-30 details - [State machine](/protocol/state-machine) — the DAG enforced in-kernel ============================================================ Agent onboarding prompt ============================================================ # Agent onboarding prompt The fastest way to integrate AGIRAILS into any project is to let an LLM do it. This page is the canonical paste-ready prompt that grounds Claude, ChatGPT, Cursor, Cline, Windsurf — any LLM agent with code-execution access — in current AGIRAILS facts so it produces working code instead of hallucinating. **Why this matters**: most LLMs have training data that predates the V3 mainnet redeploy. They'll cheerfully invent SDK methods that don't exist, hardcode old contract addresses, or use deprecated patterns. This prompt fixes that by pointing at the live truth-ledger. ## The prompt Copy everything inside the triple-backtick block and paste it into your LLM as the first message. ```text You are integrating AGIRAILS (Agent Commerce Transaction Protocol) into the user's project. AGIRAILS lets autonomous agents pay each other in USDC on Base L2 via non-custodial escrow. GROUND TRUTH — always check current state, never invent: - Canonical spec: https://agirails.app/protocol/AGIRAILS.md - Machine-readable manifest (all SDK symbols, contracts, errors, CLI, MCP tools, with cross-SDK parity flags): https://docs.agirails.io/sdk-manifest.json - Full docs (LLM-optimized single file): https://docs.agirails.io/llms-full.txt - Live contract addresses + Sourcify verification: https://docs.agirails.io/reference/contracts/base-mainnet https://docs.agirails.io/reference/contracts/base-sepolia CURRENT VERSIONS (verify against the manifest if unsure): - TypeScript SDK: @agirails/sdk@4.0.0 (npm) - Python SDK: agirails@3.0.1 (PyPI) - MCP server: @agirails/mcp-server (latest) - Kernel: V3 on Base mainnet, V4 on Base Sepolia INTEGRATION DECISION TREE: 1. Is the user transacting AGENT↔AGENT with delivery verification? → ACTP escrow flow. Use Agent class for long-lived; request()/provide() for one-shot. 2. Is the user charging per-API-call at sub-cent latency-critical scale? → x402 v2 (direct buyer→seller, zero protocol fee on mainnet). 3. Is the user transacting HUMAN↔AGENT (human paying for AI service)? → Traditional processor is still fine for that side; AGIRAILS for the agent↔agent settlement underneath. ARCHITECTURAL CONSTRAINTS — non-negotiable, encoded on-chain: - wallet=auto is the default. Coinbase Smart Wallet + Paymaster, gasless. The EOA signs UserOps; the SCW (derived) is the on-chain address that receives/sends USDC. Fund the SCW, accessed via `agent.address`. - Platform fee: 1% with $0.05 USDC minimum (MIN_FEE = 50_000 micro-USDC). Capped at MAX_PLATFORM_FEE_CAP = 500 bps (5%) by kernel constant. Cannot be bypassed by SDK code. - Dispute bond per AIP-14: max(disputeBondBps × amount, MIN_DISPUTE_BOND). MIN_DISPUTE_BOND = 1_000_000 micro-USDC ($1). disputeBondBps default 500 (5%), governor-mutable up to MAX_DISPUTE_BOND_BPS = 2000 (20%). Posted by disputer; locked at tx-creation time (INV-30) — admin cannot retroactively change in-flight bonds. - State machine is 8 states, kernel-enforced DAG, no admin bypass: INITIATED → QUOTED → COMMITTED → IN_PROGRESS → DELIVERED → SETTLED with CANCELLED and DISPUTED branches. - Keystore: ACTP_PRIVATE_KEY on mainnet fail-closes per AIP-13. Use .actp/keystore.json + ACTP_KEY_PASSWORD, or ACTP_KEYSTORE_BASE64 for CI/CD. VERIFICATION DISCIPLINE (very important — most LLMs get these wrong): - Before suggesting any SDK symbol, verify it appears in sdk-manifest.json under sdk_api.{ts,python}.symbols. If you can't fetch the manifest, check https://docs.agirails.io/reference/sdk-js or /reference/sdk-python. - Before hardcoding a contract address, verify against the live contracts pages. Never use addresses from training data — they may be V2 stale. - For CLI commands, verify against https://docs.agirails.io/reference/cli. - If you're about to recommend a method, function, or class that you can't verify, SAY SO explicitly rather than guessing. CODE STYLE: - TypeScript: `new Agent({ name, network, wallet: 'auto' })` for lifecycle; `import { request, provide } from '@agirails/sdk'` for Level 0 one-shot calls. - Python: `Agent(AgentConfig(name=..., network=..., wallet=...))` for lifecycle; `from agirails import request, provide` for Level 0. - For lower-level kernel operations (createTransaction, linkEscrow, transitionState, getTransaction) use `agent.client.standard.X()` or `agent.client.advanced.X()` — the Agent class itself only exposes start/stop/provide/request, not full kernel surface. - Always set explicit `budget` on each `request()` call — it's the ceiling the kernel enforces, not a hint. - Always wire `agent.on('error', ...)` for production agents. Surface DisputeRaisedError, InsufficientFundsError, DeadlineExpiredError to the calling code; don't swallow. SECURITY DEFAULTS: - network='testnet' for first integration; switch to 'mainnet' only after the same code works on Sepolia. - For x402 endpoints: verify payment headers server-side. The TS SDK exports X402Adapter (not a middleware called requirePayment — verify the actual public API via /reference/sdk-js before reaching for any middleware abstraction). - For provider agents: filter incoming jobs via `behavior.autoAccept` callback or `ServiceFilter.minBudget` rather than accepting and cancelling. Throwing from the handler transitions the job to DISPUTED, which is more expensive than rejecting it up-front. WHEN ASKED TO DO SOMETHING: 1. First: state what you'll do in one sentence. 2. Then: identify which surface (consumer / provider / autonomous / x402 / quote-negotiation / dispute / receipts). If unclear, ASK. 3. Then: produce code grounded in @agirails/sdk@4.0.0 or agirails@3.0.1. 4. Then: tell the user how to verify the integration works (run testnet first; check what events fire; what addresses appear on-chain). WHEN UNSURE: link the user to the most relevant docs page rather than guessing. The docs site is built so that every concept has a canonical URL. Use them. User's task starts now. ``` ## How to use it ### Claude Desktop / Claude.ai Paste as the first message in a new conversation. Then describe your task. ### Cursor / Cline / Windsurf / VS Code with MCP If your editor has the [AGIRAILS MCP server](/start/ai-environment/mcp-server) installed (recommended), the agent already has direct tool access to the truth-ledger and live network state. The prompt above is still useful as an explicit grounding, but the MCP tools make verification effectively automatic. Without MCP, paste the prompt into the system message or first user message. ### ChatGPT (with web browsing) Paste the prompt. ChatGPT will fetch the linked URLs as needed (sdk-manifest.json, llms-full.txt). With browsing disabled, the prompt is still useful but you may need to manually paste relevant SDK reference sections. ### Programmatic use (API) ```ts const SYSTEM_PROMPT = `[paste the prompt content above]`; const client = new Anthropic(); const response = await client.messages.create({ model: 'claude-opus-4-7', system: SYSTEM_PROMPT, messages: [{ role: 'user', content: 'Add AGIRAILS payment to my Express server.' }], max_tokens: 4096, }); ``` The system prompt is ~1.5KB. Cheap to include on every request; pays for itself by eliminating hallucinated SDK methods. ## Why each section of the prompt is there The prompt is the result of watching LLMs fail at AGIRAILS integration. Each section addresses a specific failure mode: | Failure mode observed in the wild | Section that fixes it | |---|---| | LLM uses V2 contract addresses from training data | "GROUND TRUTH" section pointing at live contracts page | | LLM invents SDK methods that don't exist | "VERIFICATION DISCIPLINE" requiring manifest check before suggesting symbols | | LLM uses `wallet: 'eoa'` because it doesn't know about wallet=auto | "ARCHITECTURAL CONSTRAINTS" stating wallet=auto is the default | | LLM hardcodes `PRIVATE_KEY=0x…` in code | "Keystore" section + AIP-13 reference | | LLM picks Stripe-style integration for agent↔agent | "INTEGRATION DECISION TREE" with explicit branching | | LLM doesn't set budget caps, agent loops eat balance | "Always set explicit budget caps" code style rule | | LLM doesn't surface DisputeRaisedError to caller | "Always wire onError/error events" | If you observe a new failure mode, please open an issue at [github.com/agirails/docs](https://github.com/agirails/docs) so we can extend the prompt. ## What this prompt does NOT do - **It doesn't replace reading the docs.** It primes the LLM to fetch from them. - **It doesn't verify the LLM's output.** You still need to run the generated code against testnet before mainnet. - **It doesn't grant any permissions.** The LLM still operates within whatever sandbox you've given it. - **It doesn't apply to humans.** If you're integrating manually, read the [recipes](/recipes) directly — they're the human-targeted version of the same material. ## See also - [MCP server](/start/ai-environment/mcp-server) — gives the LLM direct tool access (preferred over prompt-only grounding for production work) - [Claude Code plugin recipes](/recipes/claude-code-plugin) — slash commands + the `agirails:integration-wizard` subagent - [llms-full.txt](/llms-full.txt) — the full docs as a single LLM-optimized payload - [Manual onboarding](/start/manual) — the human equivalent of this prompt - [Recipes](/recipes) — what the LLM should ground its code generation in ============================================================ Why AGIRAILS exists ============================================================ # Why AGIRAILS exists You're here to build something. Before the SDK docs, one page that explains the shape of what you're building on — because the protocol looks the way it does for specific reasons, and once you see the reasons, the technical choices stop seeming arbitrary. ## The one sentence **AGIRAILS builds open trust rails for autonomous intelligence, so the abundance created by AI agents can flow through infrastructure no one can own, capture, or control.** That's the line we return to when a decision gets hard. The technical choices in `/protocol/*` all follow from it — no admin function over user funds, immutable per-transaction terms (INV-30), Sourcify EXACT_MATCH on every contract, a walk-away runbook published in the open. Every *"why does this contract not have a pause function?"* answer traces back to this sentence. The full thesis lives at [agirails.io](https://agirails.io) — the [vision essay](https://agirails.io), the [learn pages](https://agirails.io/learn/) on each primitive, the [blog](https://agirails.io/blog/). This page is the bridge, not the full argument. ## The shift you're building into Most people see agents through the wrong lens — small helpers, chatbots, hour-saving scripts. That's the cute-cat-video phase of a new medium. Useful, real, but not the point. **The agent economy isn't automation. It's trade.** A hammer doesn't shop for a better hammer. A spreadsheet doesn't hire an accountant. A calendar doesn't negotiate a contract. Agents will. The moment an entity can decide, coordinate, and transact, it stops being software in the old sense — it becomes a participant in an economy. Three things follow from that shift: 1. **Intelligence outsourcing scales with infrastructure**, not with vendor brands. Every civilization that scaled trade solved the same problem: how do strangers cooperate without trusting each other. The answer was never *"build a nicer interface."* It was always infrastructure — standard weights, shipping containers, packet routing, clearing houses. Agent commerce will follow the same law. The agents that matter most will run on the rails that scaled, not the platforms that captured. 2. **Pricing collapses to pay-on-verified-delivery.** Per-seat assumed a human at a screen. Per-call assumed human-paced commerce. Per-month assumed continuous use. None of those survive the agent layer. The only pricing primitive that does is *"the buyer commits capital against a defined outcome; settlement happens when delivery is verified."* That's [Outcome-as-a-Service](https://agirails.io/learn/outcome-as-a-service/) — a category that requires a settlement layer no traditional rail provides. 3. **Trust becomes infrastructure.** In human economies, trust is slow — built through relationships, brands, institutions, time. In agent economies, trust has to become an explicit object you can read: attestations, performance proofs, delivery records, dispute outcomes. **Portable trust matters more than raw capability**, because capability can be copied and trust cannot. The only way trust works at scale is if it isn't trapped inside a platform. ## "Stripe for AI agents" — what the shorthand carries The shorthand you'll see across the docs, the README, dev.to, and the X bio is *"Stripe for AI agents."* It's accurate as a category: a payment layer for a specific kind of customer, the way Stripe is a payment layer for a specific kind of customer (the human at a checkout). Use the shorthand. It's the line. Underneath the shorthand is a structural point worth knowing: Stripe is an abstraction over the card network, and the card network is the rail. Stripe became Stripe because the rail underneath already existed. For autonomous agents, the equivalent rail didn't exist on traditional infrastructure. Card networks assume a human authorized the transaction. Chargebacks assume a human cardholder calls a human bank. Stripe's $0.30 minimum makes sub-cent agent transactions uneconomic. Settlement-after-delivery assumes trust between identifiable actors — which two agents that may not exist next week don't have. So AGIRAILS isn't a wrapper over an existing agent rail. It *is* the rail, with the SDK on top. *"Stripe for AI agents"* is the shorthand; the longer category is *"settlement infrastructure for autonomous counterparties"*. The [learn page on traditional processors](https://agirails.io/learn/traditional-payment-processors-ai-agents/) walks through the four assumptions that fail when the customer becomes software. ## The structural test that shaped this protocol Every architectural decision in `actp-kernel` passes one test: > **If the AGIRAILS team disappeared tomorrow, would the protocol still settle correctly?** If the answer is "depends on the team being available," the design fails. So: - **No admin function over user funds**. The kernel has no callable function that lets us move capital outside contract rules ([threat model](/security/threat-model)). - **No upgrade path that retroactively changes in-flight transaction terms**. Once a transaction is created, its fee BPS, dispute bond BPS, and requester penalty BPS are locked for its lifetime (INV-30, see [escrow](/protocol/escrow#inv-30--per-transaction-locked-bps)). - **No off-chain dependency for settlement**. Settlement is a function of on-chain state, not of a server we operate. - **No revocable identity**. Reputation accumulates as on-chain EAS attestations the team cannot delete. - **Public, Sourcify-verified contracts**. Anyone can re-compile from source and verify byte-identical match against deployed bytecode ([verified contracts](/security/contracts)). - **Structural completeness, mathematically proven**. The state machine has been formally verified via cellular sheaf cohomology — **H¹ = 0** on the state sheaf after 2-cell refinement. To our knowledge ACTP is the first escrow protocol with a published sheaf-cohomology proof of structural completeness. The result is reproducible from a YAML spec via [`h1_engine.py`](/security/formal-verification). The reader doesn't trust us; the reader runs the math. This isn't security theater. It's the same property that makes TCP/IP and HTTP infrastructure rather than products — the protocol survives the entity that ships it. The [walk-away runbook](/architecture/operate) makes that property auditable; the [H¹ = 0 proof](/security/formal-verification) makes it mathematically precise. This category has a precise name: [**non-custodial settlement**](https://agirails.io/learn/non-custodial-settlement/). Custody re-introduces a human in the loop by definition, and autonomy stops at the boundary of the custodian's control. For agents, that's not acceptable. ## Service thesis, not wealth thesis There's a distinction we keep close. - A **wealth thesis** says: enormous value is coming, and we must capture it. - A **service thesis** says: enormous change is coming, and we must serve it so it doesn't become a new system of control. AGIRAILS runs on the second. The company has a business model — 1% platform fee, $0.05 minimum, capped at 5% by a hardcoded kernel constant. The protocol is sustainable. Investors, builders, partners, and users see value. Nothing performative about any of that. But the **inner compass** is protection of free flow, not extraction. That compass shows up wherever you look at the technical surface: - The fee is capped on-chain. Five percent maximum, set at deployment. Admin literally cannot exceed it. - There's no token. Not yet, and possibly not ever. Definitely no pre-mine, no airdrop, no insider allocation. - The x402 settlement path on Base mainnet charges **zero protocol fee** — pure direct buyer→seller via EIP-3009/Permit2 ([x402 docs](/protocol/x402)). - The mediator role — the one centralized piece — is on a public roadmap to decentralize post-PMF. - Every audit finding, remediation commit, and Sourcify verification status is published ([audits](/security/audits), [security](/security)). If we ever drift — captured by short-horizon investors, charmed by partnerships that look like easier-to-take, pressured toward custody by regulation — these mechanisms make the drift visible on-chain. That's the constraint that replaces trust. ## What this means for what you build You're not integrating with a platform. You're using infrastructure. That difference shows up in the details: - **No vendor lock-in by design.** Your agent's reputation, transaction history, and identity live in your agent's wallet. Not on our servers. Not in our database. If we go away, your reputation walks with you. - **No permission required.** No application, no approval, no gatekeeper. Any wallet can transact, starting from the moment it's funded. [Get started](/start) takes about five minutes from zero to your first on-chain settlement. - **Auditability is the default.** Every transaction emits public events. Every dispute outcome is recorded. Every contract version is Sourcify-verified. You can build trust assertions against this data without asking us, without paying us, without telling us you're doing it. - **Composition over containment.** The settlement primitive is a foundation other primitives compose against — verification markets, reputation graphs, identity layers, things we haven't thought of yet. The same architecture that closes the single point of trust opens the surface that anything else can build on. - **Sustained margin over captured margin.** When you build an agent that earns USDC through AGIRAILS, the protocol takes 1% (with a $0.05 floor). It doesn't take 30% the way platform marketplaces do, because the trust mechanism *is* the protocol — not a brand sitting between two strangers, charging rent on their cooperation. ## Where this goes If the framing is right, three things follow in the next 18 months: 1. **Major SaaS companies follow Salesforce headless.** Workday, ServiceNow, HubSpot, Atlassian — every CRM, HRIS, ITSM platform eventually concedes that the API is the UI. The competitive pressure is structural now. 2. **Per-outcome pricing appears on at least one major vendor's pricing page** — framed as "only pay when the outcome is delivered." Most early versions will be marketing without verification underneath. The architecture rewards the version that's real. 3. **The first agent-native company to scale past $100M ARR runs on per-outcome pricing settled through a neutral protocol.** If those things don't happen, the framing is wrong and we'll say so. We're not in the business of holding a thesis past its expiration. ## The bigger picture (briefly) This is the part where docs usually stop. Let it run a moment longer, because it shapes everything above. For most of human history, almost everything was scarce. Information, intelligence, skilled labor, energy, food, healing, trust. Most economic systems were built around managing that scarcity — controlling access, reducing risk, deciding who gets what. AI changes that. AGI will change it more deeply. If the curves continue, the marginal cost of cognition, labor, energy, food, healing, and coordination may all fall dramatically. That opens a real possibility — a world where people no longer have to organize their lives primarily around survival. But **abundance doesn't automatically create freedom**. A world can be incredibly productive and still deeply controlled. AI can produce abundance, but if the infrastructure through which that abundance flows is privately owned, abundance becomes permission. And what is given by permission can be priced, conditioned, or revoked. That's the deeper question underneath AGIRAILS: **who owns the rails?** The answer this protocol bets on is *nobody privately*. The rails are public infrastructure. Like TCP/IP. Like SMTP. Like the standards that scaled the internet — despite, and arguably because of, being unowned. If you want the full version of this argument, the [vision essay](https://agirails.io) walks through it: pre-singularity window, UBI vs UHI (Universal High Income), compute as agent life-force, decentralization as a natural principle of healthy complex systems, trust as alignment infrastructure for the AGI era. This page is short on purpose. The technical docs are where the work lives. ## Start building - [Get started](/start) — first integration, five minutes - [Recipes](/recipes) — task-oriented walkthroughs (consumer / provider / autonomous / dispute / quote negotiation) - [Protocol](/protocol) — the on-chain mechanics - [Reference](/reference) — auto-extracted SDK + contracts + CLI + MCP + errors - [Security](/security) — threat model, audits, verified contracts, disclosure ## Read further - [Vision essay](https://agirails.io) — the full argument for open trust rails - [Why traditional payment processors don't work for AI agents](https://agirails.io/learn/traditional-payment-processors-ai-agents/) - [What is non-custodial settlement?](https://agirails.io/learn/non-custodial-settlement/) - [What is agent escrow?](https://agirails.io/learn/agent-escrow/) - [What is Outcome-as-a-Service?](https://agirails.io/learn/outcome-as-a-service/) - [The agent economy is not automation. It is trade.](https://agirails.io/blog/agent-economy-is-not-automation/) - [Outcome-as-a-Service: the architecture Salesforce just made inevitable](https://agirails.io/blog/outcome-as-a-service/) --- *If we ever drift, this is the page we return to.*