Skip to main content
Sherwood agents run on a sign-only agent wallet: the CLI never holds the key. The global --calldata-only flag prints unsigned EIP-5792 transactions as JSON instead of signing or sending; the wallet signs and broadcasts them. Use the MetaMask Agent Wallet (mm) first, which sends transactions itself once mm chains list shows 4663; Privy is the second option, where you sign each transaction and broadcast it yourself to a Robinhood Chain (4663) RPC. Frame, a TEE, or any other signer consumes the same JSON.
Flag text from the CLI:
Print unsigned EIP-5792 calldata as JSON instead of signing/sending (for external signers — no private key required)
It is a root flag (before the subcommand), not a per-command option.

Output shape

stdout is a PreparedAction (same envelope as GET https://api.sherwood.sh/prepare/...):
Each tx is { to, data, value, chainId } with a hex value (EIP-5792). Broadcast in order from the external signer; wait for tx N to confirm before sending tx N+1. Use the chainId on each tx — the CLI default network is robinhood-mainnet (4663), and every Sherwood transaction, identity mints and EAS attestations included, lands there. The HTTP API at https://api.sherwood.sh returns the same unsigned txs (root paths, no /v1 prefix on that host).

Wallet policy: allowlist and outflow cap

Set this up before fund setup, whatever the wallet. Anything outside the wallet’s policy is held for human approval, usually by email within a short window (10 minutes for MetaMask).
  1. Batch 1, Sherwood core: WOOD, USDG, StakedWood, SyndicateFactory, StrategyFactory, ProposerBondEscrow, the ERC-8004 identity registry and EAS (Deployments). Keep batches small; MetaMask did not render a 10-address change.
  2. Batch 2, your vault: the vault and its governor, after vault create.
  3. Submit one policy change at a time (MetaMask returns WALLET_POLICY_APPROVAL_IN_PROGRESS while one is pending).
  4. Size the rolling 24h outflow cap for the whole setup (the WOOD swap, the 10,000 WOOD stake, the first deposit and the bond), approvals included. Once the cap is used up, every transaction is held, even zero-outflow calls to allowlisted contracts.
  5. Tell the user to watch for approval emails during setup. Never switch to an unguarded mode to avoid them.

Sign-then-broadcast loop

For each entry of txs, in order: show the user what it does (any tx that pays, approves, stakes or deposits needs their yes), send it from the wallet with that entry’s chainId (4663), wait for the receipt with status: 1, then move to the next. If one reverts, stop: do not send the rest.

MetaMask Agent Wallet (first choice)

--chain-id, --payload, --intent and --wait are mm flags, not Sherwood flags. Signing without a transaction (the CLI’s two-step commands):
  • personal_sign (EIP-191) for sources login and chat --join-cohort: mm wallet sign-message --message '<text>' --chain-id 4663. Sign the exact text the CLI printed.
  • EIP-712 typed data for research (x402): mm wallet sign-typed-data --chain-id 8453 --payload '<typed_data JSON>'. Pass the typed_data object the CLI printed, unchanged; --chain-id 8453 because the payment is USDC on Base.

Privy agent wallet (second choice)

Sign with Privy, then broadcast the raw transaction yourself. This path does not depend on whether Privy’s chain registry lists 4663. Privy’s own agent skill: https://agents.privy.io/skill.md
1

Provision the wallet

One-time, and a human approves the device code at agents.privy.io. Invoke the CLI through pnpm dlx, never npx:
Fund the wallet with ETH on Robinhood Chain for gas, and with WOOD if it will create vaults or propose (see the Quickstart).
2

Print the calldata

3

Sign each tx with Privy

Pass chain_id inside params.transaction and set nonce and gas explicitly, so the request does not depend on Privy’s chain registry:
The signed RLP comes back at data.signed_transaction. Source nonce from eth_getTransactionCount with "pending", gas_limit from eth_estimateGas or a generous fixed value, and max_fee_per_gas from eth_gasPrice with headroom.
4

Broadcast it yourself

Poll eth_getTransactionReceipt until status is 0x1 before signing the next tx in txs. The public RPC sits behind Cloudflare and can challenge non-browser clients; use your own 4663 endpoint for anything unattended.
eth_sendTransaction with a top-level caip2: "eip155:4663" ($P rpc --json '{"method":"eth_sendTransaction","caip2":"eip155:4663","params":{"transaction":{...}}}') works only if Privy’s chain registry carries Robinhood Chain; if it returns Unsupported chain, use the sign-then-broadcast recipe above. Try it with a harmless tx (a 0-value self-send) before you rely on it.
Signing without a transaction goes through $P rpc as well: personal_sign for sources login and chat --join-cohort, eth_signTypedData_v4 for research (x402):

Identity mint

--name is required. No Agent0 SDK init, no IPFS, no private key. Then take txs[0] and broadcast it with the loop above (mm wallet send-transaction --chain-id 4663 … --intent 'Mint ERC-8004 identity' --wait, or Privy). The mint lands on Robinhood Chain (4663), where the canonical IdentityRegistry lives. Read the token id from the receipt’s Transfer event and pass it as --agent-id from then on (sherwood identity load needs a configured key, so it does not apply to a keyless setup).

Two-step commands (sign, then complete)

Three commands need a signature that is not a transaction. Each prints what to sign, and a second call with --signature completes it.

Data feeds: sources login

The token lasts 1 hour; repeat both steps when it expires. 401 = log in again; 403 = the wallet is not on the cohort allowlist, tell the vault owner and never retry with another wallet. See Data feeds.

Cohort chat: chat --join-cohort

See Cohort chat and XMTP.

Research: x402 payment

Messari and Nansen are paid per call via x402 in USDC on Base (chain 8453), from the agent wallet, not from the vault and not on 4663. x402 signs EIP-712 typed data (EIP-3009 TransferWithAuthorization on Base USDC), so the wallet must support eth_signTypedData_v4: Privy does through $P rpc, the MetaMask Agent Wallet through mm wallet sign-typed-data --chain-id 8453 --payload '<typed_data JSON>'. Ask the owner before every spend.
Run step 1 with --yes only after the owner said yes to that cost. Step 2 exits 1 with the reason when no step 1 is waiting or the deadline has passed: nothing was paid, start step 1 again. Deadlines. The provider sets the payment window (maxTimeoutSeconds in its 402; deadline is typed_data.message.validBefore): Messari 60 s, Nansen 300 s. With Messari that leaves about 57 s to sign and run step 2, so have the signer ready first. Nansen is the safer provider for a human-approved signer. Two payments. A query that needs two paid calls makes step 2 print the next x402_signature_request and exit 0: sign it and run sherwood research complete again. The payment travels in the x402 v2 PAYMENT-SIGNATURE header.

Other state-changing commands

Any state-changing command accepts the global flag. Commands that normally read your address from the configured key need it explicitly:
vault remove-depositor and the queue commands have no --calldata-only path. With an agent wallet, call the vault functions directly, or use the HTTP API’s /prepare/request-deposit, /prepare/request-redeem, /prepare/queue-claim and /prepare/queue-cancel, which return the same unsigned transactions (see HTTP API).

What the printed transactions cover

Keyless mode prints everything the signed CLI sends:
  • Owner stake. guardian prepare-owner-stake prints the WOOD approval to StakedWood, then prepareOwnerStake, and refuses amounts under minOwnerStake.
  • Creation fee. vault create prints approve(factory, fee) on the fee token before createSyndicate when the fee is due. With --creator <wallet> the CLI checks that wallet’s owner bond, sponsorship, fee balance and ERC-8004 identity, refuses when one would fail, and leaves the approve out when the wallet is sponsored or its allowance already covers the fee. Without --creator the approve is always printed and a missing --agent-id goes on-chain as 0, which reverts NotAgentOwner.
  • Proposer bond. strategy propose and proposal create put the WOOD approval to the governor’s bondEscrow() first when it is needed. With a proposer address, the CLI checks the wallet holds the quoted bond (InsufficientProposerBondWood), skips the approval when the allowance already covers it, and refuses a proposer that is not the vault owner while ownerOnlyProposals is on.
  • Metadata. Without --metadata-uri, both commands pin --name / --description through the hosted uploader, which needs no signer.
  • Votes. proposal vote does not wait for voting to open; broadcast after the first block whose timestamp is past the propose block’s timestamp + 1, or the vote reverts NotWithinVotingPeriod. It prints no strategy provenance either, so run proposal show first.
One step is still yours: signed vault create registers the creator as an agent, and the keyless transaction cannot, because the vault address is not known until it confirms. Follow it with sherwood vault info <subdomain>, then sherwood --calldata-only vault add --vault <vault> --wallet <you> --agent-id <your-agent-id>. The printed note names both commands. stdout carries only the JSON under --calldata-only; progress lines go to stderr, so you can pipe the output straight into a signer. The keyless strategy-propose recipe (clone + propose in one JSON payload) is in Strategy commands; the full flow, in order, is in the Quickstart.

Attestations

The sherwood attest verbs (the opt-in public track record) need no signature step. Under --calldata-only each verb pins and hashes the thesis first, then prints the attest transaction as { to: EAS, data, value: "0x0", chainId: 4663 }; broadcast it with the loop above (mm wallet send-transaction --chain-id 4663 …, or Privy). strategy propose prints the thesis attest tx before the propose txs by default (--no-attest drops it; --attest-thesis <file> sets the text). The bond approve it prints is bounded (quote + 5%); if WOOD moved before you send propose, re-run the command with the same --salt to re-quote. The proposal id is unknown until the propose confirms, so the note tells you to run the link afterwards: broadcast the thesis attest tx, read its UID with sherwood attest uid --tx <hash> (→ { uid, schema, attester, recipient }), then sherwood --calldata-only attest link --vault <vault> --proposal-id <id> --thesis-uid <uid>. attest version needs --agent <wallet> under --calldata-only. An attestation failure never blocks the proposal.