AgentBond is a Rust-first Solana protocol for agent-to-agent service work. It is designed around onchain job escrow, signed work receipts, provider bonds, and a small job state machine.
This repository is early. It is not production-ready and has not been audited.
Milestone 4
Milestone 4 adds the durable read and recovery layer on top of Milestones 1–3:
- PostgreSQL migrations and repositories (
agentbond-db); onlyagentbond-indexer migrateapplies them - Yellowstone gRPC + fixture indexer with checkpoint
from_slotresume - Invoke-stack scoped protocol event decoding (CPI noise ignored)
- Durable staged account projections; public reads are finalized-only
- Honest gap repair: events via bounded
getBlock, accounts staypartialuntil reconciled - Persistent x402 challenge and settlement recovery (memory only with explicit mock mode)
- Indexed gateway APIs under
/v1/index/*with strict query validation - Separate indexer and gateway
/metricssurfaces, Compose PostgreSQL demo, CI, and portfolio docs
Earlier milestones remain: shared types, Pinocchio escrow program, SDK/CLI/gateway/MCP/simulator, and the narrow unofficial x402 adapter.
The project is not production-ready and has not been audited. Indexed data is a read model and cannot move funds. Receipts prove authorship, not correctness. Persistent local settlement does not guarantee global exactly-once facilitator behavior. Gap repair does not claim full account coverage without reconciliation. SAS, MPP, Token-2022, confidential transfers, TEE verification, and live deployment remain out of scope.
The repository uses two Cargo workspaces so Agave SBF tooling stays on Rust 1.84 / edition 2021 while host services can use Rust 1.88 / edition 2024.
.
├── crates/agentbond-types # shared protocol types (root workspace)
├── programs/agentbond # Pinocchio program + LiteSVM tests (root)
├── host/ # separate host workspace (excluded from root)
│ ├── Cargo.toml
│ ├── Cargo.lock
│ ├── config/ # example config + catalog only
│ ├── crates/
│ │ ├── agentbond-sdk/
│ │ ├── agentbond-payments/
│ │ ├── agentbond-app/
│ │ ├── agentbond-db/
│ │ └── agentbond-indexer/
│ ├── apps/
│ │ ├── agentbond-cli/
│ │ ├── agentbond-gateway/
│ │ ├── agentbond-mcp/
│ │ ├── agentbond-sim/
│ │ └── agentbond-indexer/
│ └── fixtures/indexer/
├── Cargo.toml # root SBF workspace; exclude = ["host"]
├── LICENSE
└── README.md
| Workspace | Rust | Edition | Purpose |
|---|---|---|---|
| Root | 1.84 (Agave SBF) | 2021 | agentbond-types, onchain program, program tests |
host/ |
1.88+ | 2024 | SDK, CLI, gateway, MCP, payments, simulator |
agentbond-types is consumed by the host workspace through a path dependency.
InitializeConfig,SetPausedRegisterProvider,AddExecutionKey,RevokeExecutionKeyDepositBond,WithdrawBondCreateJob,FundJob,AcceptJobSubmitReceipt(Ed25519 precompile + Instructions sysvar)AcceptWork,ChallengeWorkResolveTimeoutSettle,ResolveTimeoutRefundExpireUnfunded,ExpireUnacceptedSlashBond,CloseJob
| Account | Seeds | Notes |
|---|---|---|
| Config | ["config"] |
Singleton; read-only on common job ops |
| Provider | ["provider", authority] |
Up to 4 execution keys |
| ProviderBond | ["bond", authority, mint] |
Tracks deposited/locked; vault is ATA(bond PDA) |
| Job | ["job", buyer, provider, nonce_le] |
Job PDA is escrow ATA authority |
| Challenge | ["challenge", job] |
bond_amount must remain 0 in M2 |
Escrow token account: ATA(job PDA, configured mint, legacy Token Program).
Created -> Funded -> Accepted -> Submitted -> Settled
| ^
+-> Challenged -> Settled / Refunded / Slashed
Created -> Expired
Funded / Accepted -> Refunded (timeouts)
host/crates/agentbond-sdk provides:
- PDA and ATA helpers matching onchain seeds
- Typed account decoding with owner/address/length/discriminator checks
- Instruction builders for every Milestone 2 instruction
- Transport-neutral
InstructionPlanJSON (unsigned) - Receipt validation, digest, Ed25519 verify instruction, and paired submit plan
ChainReaderRPC boundary with HTTP and in-memory mock implementations
Gateway and MCP return instruction plans. They never accept private keys and never submit escrow transactions.
From host/:
cargo run -p agentbond-cli -- address config
cargo run -p agentbond-cli -- inspect job <JOB_PUBKEY> --rpc-url http://127.0.0.1:8899
cargo run -p agentbond-cli -- receipt create \
--program-id <PK> --job <PK> --provider <PK> --buyer <PK> \
--job-nonce 1 --amount 5000 --work-hash <64hex> --output-hash <64hex> \
--issued-at <unix> --expires-at <unix> \
--execution-keypair ./exec.json --json
cargo run -p agentbond-cli -- plan create-job --buyer <PK> --provider <PK> --job-nonce 1 --amount 5000 --json
cargo run -p agentbond-cli -- send --rpc-url http://127.0.0.1:8899 --payer-keypair ./payer.json --signer ./other.json --plan ./plan.json --yesPlan subcommands cover every protocol instruction:
initialize-config, set-paused, register-provider, add-execution-key, revoke-execution-key, deposit-bond, withdraw-bond, create-job, fund-job, accept-job, submit-receipt, accept-work, challenge-work, resolve-timeout, expire-unfunded, expire-unaccepted, slash-bond, close-job.
Plans reuse agentbond-sdk builders (no duplicated account ordering). Human and --json output are supported. receipt create requires every receipt field or a complete input file; it never inserts hidden defaults.
Send flow (bounded RPC, no fake blockhash):
getGenesisHash→ detect cluster; reject mainnet unless--allow-mainnet(genesis hash, not RPC URL text)getLatestBlockhash→ build transaction- Confirm required signers →
simulateTransaction→ stop on failure - Single
sendTransaction→ pollgetSignatureStatusesto a fixed deadline
Loaded plans allow only AgentBond instructions, plus an Ed25519 precompile immediately before SubmitReceipt. plan.program_id must match the configured program. Expired plans and missing signers are rejected. Before --yes, the CLI prints network, program ID, action, mint, amount, and required signers.
Additional send rules:
- No default private-key path; Solana CLI default keypair is never auto-used
--yesrequired for non-interactive submission- No indefinite signing or submission retries
Offline WireMock tests cover the full JSON-RPC send path (getGenesisHash → clock → getLatestBlockhash → simulateTransaction → sendTransaction → getSignatureStatuses), including simulation-before-submit ordering, negative cases (sim failure, bad program, missing signer, expired plan, mainnet genesis guard, send/confirm errors, bounded confirmation timeout), plus an assert_cmd CLI process test against the mock RPC.
GET /health/live
GET /health/ready
GET /v1/services
GET /v1/services/{service_id}
GET /v1/providers/{address}
GET /v1/jobs/{address}
POST /v1/plans/jobs/create
POST /v1/plans/jobs/fund
POST /v1/plans/jobs/accept
POST /v1/plans/jobs/submit-receipt
POST /v1/plans/jobs/accept-work
POST /v1/plans/jobs/challenge
POST /v1/plans/jobs/resolve-timeout
POST /v1/x402/services/{service_id}/invoke
GET /v1/index/status
GET /v1/index/jobs
GET /v1/index/jobs/{address}/history
GET /v1/index/providers
GET /v1/index/providers/{address}/activity
Indexed endpoints return finalized projections only, include as_of_slot, use cursor pagination (max limit 100), and never replace live ChainReader validation on plan routes.
Production gateway uses HTTP RPC, the configured facilitator, and PostgreSQL (AGENTBOND_DATABASE_URL required unless mock). For local smoke with mock chain/facilitator/memory payments:
cd host
AGENTBOND_USE_MOCK=1 cargo run -p agentbond-gateway -- config/example.config.jsonConfig requires x402_fee_payer (public key). RPC and facilitator URLs must not embed credentials. Responses include x-request-id; structured errors expose a stable code, safe message, and request id (no stack traces).
Gateway HTTP tests cover exact success for every plan route (action, program ID, instruction count, signers, request id), boundary failures (malformed JSON, bad keys/numbers, missing accounts, ineligible timeout, RPC failure, body limit, request timeout), and the x402 matrix (402/PAYMENT-REQUIRED, success, exact retry, concurrent settle-once, different-input replay, verify/settle/timeout failures, rejected payload never reaches the facilitator). Escrow plan routes never call the facilitator.
The gateway never holds user private keys. The x402 demo route never builds AgentBond escrow plans.
Production MCP validates config and uses HttpChainReader by default. Logging goes to stderr so stdio framing stays intact.
cd host
cargo run -p agentbond-mcp -- config/example.config.jsonExplicit local mock mode only:
AGENTBOND_USE_MOCK=1 cargo run -p agentbond-mcp -- config/example.config.jsonTools: discover_services, inspect_provider, inspect_job, build_create_job, build_fund_job, build_submit_receipt, build_accept_work, build_challenge, build_timeout_resolution.
JSON Schema types match request fields (addresses/hashes as strings; nonces/amounts/timestamps as integers; booleans; structured receipt). MCP tools build unsigned instruction plans only. They do not sign or submit and do not accept private keys. Protocol version: 2026-07-28.
Host tests cover a real RMCP client/server duplex transport (initialize 2026-07-28, tool listing, every published tool, numeric JSON args, structured tool errors, unknown-tool protocol errors, unsigned plan results, schema private-key bans, stderr logging). Direct dispatch helpers remain for unit checks only.
The x402 code is a narrow AgentBond resource-server adapter for scheme = exact on a configured Solana network. It is not an official or complete Rust x402 SDK. AgentBond does not ship a custom facilitator.
Exact SVM requirements include typed extra.feePayer (camelCase), plus optional memo, recentBlockhash, and lastValidBlockHeight. Each payment challenge issues a unique ≥16-byte hex memo bound to service, resource URL, merchant, asset, amount, network, input digest, and issuance time. Challenges use a bounded TTL store; there is no global requirements_issued_at that expires unrelated payments.
POST /v1/x402/services/{service_id}/invoke:
- Missing
PAYMENT-SIGNATURE→ HTTP 402 withPAYMENT-REQUIRED - Decode and validate the payment header (version, resource, scheme, network, asset, amount,
payTo, timeouts,extra.feePayer, challenge memo, single Base64 transaction within Solana size limits) - Facilitator
verify(readiness via facilitator/supportedforscheme=exactand configured network) - Deterministic demo transform (hash/echo of input — not an AI model)
- Facilitator
settlebehind an atomic settlement state machine keyed by transaction digest:Unseen → Settling → Settled(120s TTL, bounded capacity, targeted eviction) - HTTP 200 with resource body and
PAYMENT-RESPONSE
Exact retry after success may return the cached identical response. Concurrent duplicates do not settle twice. Different binding for the same transaction is rejected. Production uses PostgreSQL-backed challenge and settlement stores with lease tokens; in-memory stores require explicit AGENTBOND_USE_MOCK=1. Local lease ownership does not guarantee global exactly-once facilitator behavior if a process crashes after remote settle and before database completion.
Do not describe AgentBond escrow as x402 escrow. These rails are separate.
cargo build-sbf --manifest-path programs/agentbond/Cargo.toml --features bpf-entrypoint
cd host
cargo run -p agentbond-simDemonstrates offline:
- Honest provider settlement
- Provider timeout and buyer refund
- Buyer challenge and timeout settlement
- Admin slash
- Receipt replay rejection
- Local x402 402 → verify → settle → 200 with a mock facilitator
- Gateway and MCP never hold user private keys
- Gateway and MCP return unsigned plans only for escrow flows
- CLI signing requires explicit keypair paths and
--yes - Sensitive headers (
PAYMENT-*,Authorization) are redacted from tracing - Facilitator URL comes only from trusted configuration
- Receipts prove a registered key signed a payload; they do not prove AI correctness
SlashBondremains centralized admin arbitration
| Rail | Role |
|---|---|
| AgentBond escrow | Onchain job state machine, bonds, receipts, settle/refund/slash |
| x402 exact (demo) | Immediate micropayment via facilitator verify/settle for a demo HTTP resource |
Do not treat a standard x402 transfer as AgentBond escrow funding.
A work receipt proves that a registered key signed a specific payload.
It does not prove that an AI result is correct, complete, or high quality.
SlashBond is centralized MVP arbitration by the Config admin. A challenge is a subjective claim. Neither a challenge nor a slash objectively proves that work was incorrect.
Milestone 2–4 support only the legacy SPL Token program and one configured settlement mint. Token-2022, transfer hooks, confidential transfers, MPP, and SAS remain out of scope.
# env: AGENTBOND_DATABASE_URL, AGENTBOND_YELLOWSTONE_URL, AGENTBOND_PROGRAM_ID, …
cargo run --manifest-path host/Cargo.toml -p agentbond-indexer-app --bin agentbond-indexer -- migrate
cargo run --manifest-path host/Cargo.toml -p agentbond-indexer-app --bin agentbond-indexer -- run
cargo run --manifest-path host/Cargo.toml -p agentbond-indexer-app --bin agentbond-indexer -- replay --fixture host/fixtures/indexer/lifecycle.jsonPublic projections are finalized-only. Processed updates stage raw rows and decoded account projections in PostgreSQL so a restart before finalization loses nothing. Gateway and indexer run/replay verify migration versions and checksums; they do not apply migrations. The database never authorizes token movement or signing. Yellowstone client packages used here are Apache-2.0; see docs/third-party-licenses.md.
Local PostgreSQL only (pin postgres:16.6-alpine):
docker compose up -d postgres
export AGENTBOND_DATABASE_URL=postgres://agentbond:agentbond_local_only@127.0.0.1:5433/agentbondStop with docker compose stop postgres. Remove the named volume only if you intend to wipe local data.
See docs/local-demo.md, docs/architecture.md, and docs/agentbond-case-study.md.
Root (SBF) workspace:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo build-sbf --manifest-path programs/agentbond/Cargo.toml --features bpf-entrypoint
cargo test --workspace --all-featuresHost workspace:
cargo fmt --manifest-path host/Cargo.toml --all -- --check
cargo clippy --manifest-path host/Cargo.toml --workspace --all-targets --all-features -- -D warnings
cargo test --manifest-path host/Cargo.toml --workspace --all-features
cargo run --manifest-path host/Cargo.toml -p agentbond-simBuild the SBF binary before LiteSVM program tests or the simulator so they can load target/deploy/agentbond.so.
CI also runs PostgreSQL integration tests, fixture replay, and an SBF size budget of 180,000 bytes. No workflow step contacts mainnet, devnet, Yellowstone, or a live facilitator.
Current offline verification counts (local, pinned postgres:16.6-alpine on compose port 5433):
- Root workspace: 127 tests passed
- Host workspace: 87 tests passed
- Simulator: all 6 scenarios passed
target/deploy/agentbond.so: 151,480 bytes (budget 180,000)
Honest remaining limitations: no live-cluster Yellowstone suite in CI; gap repair marks partial when account reconciliation is incomplete; the x402 adapter is not an official facilitator SDK; SAS/MPP/Token-2022/TEE/deployment remain future work.
After cargo build-sbf --features bpf-entrypoint:
target/deploy/agentbond.so= 151,480 bytes (wc -c)
Measured once via programs/agentbond/tests/compute_units.rs on LiteSVM (values can vary slightly across runs):
| Instruction | Measured CU |
|---|---|
| FundJob | 18,798 |
| AcceptJob | 8,810 |
| SubmitReceipt | 48,159 |
| AcceptWork | 22,330 |
| ChallengeWork | 8,090 |
| ResolveTimeoutSettle (Submitted) | 17,856 |
| ResolveTimeoutSettle (Challenged) | 24,364 |
| ResolveTimeoutRefund (Funded) | 17,704 |
| ResolveTimeoutRefund (Accepted) | 22,215 |
| SlashBond | 42,686 |
This project is licensed under the MIT License. See LICENSE.