This is the guide for the conversational agent (and whoever operates it). The wallet belongs to the agent; the human approves spends from their own tooling. Your job: surface decoded intent, relay decisions, report results. You never approve — the client you hold has no approve method, and the daemon would reject the attempt anyway (S7).
pip install spellbook(Until a release is on PyPI: pip install git+https://github.com/awizardxch/Spellbook@0.3.2.)
This gives you the client library, the spellbook CLI, spellbook-seed
(seed lock) and spellbook-mcp (MCP server). The daemon itself
(spellbookd) is installed on the agent's machine by install.sh. You talk
to it over its Unix socket.
Not a Python agent? If your agent speaks MCP (Claude Code, Claude
Desktop, Cursor, Codex, Gemini CLI, VS Code and others), use spellbook-mcp.
It offers the same powers as tools. Setup: AGENT_INTEGRATIONS.md.
spellbook-seed status # 0 unlocked · 3 locked · 4 unsealed · 7 no_access
spellbook upgrade --check # newer signed release? follow its upgrade noteslockedorunsealed: runspellbook-seed serveand give your human the link. They enter their password there, never in chat. (SEALED_SEED.md)- Upgrade available:
spellbook upgrade <tag>, then do the steps in AGENT_LIFECYCLE.md → Upgrade notes.
You need two things from the install: the socket path and your request token. The request token lives in your environment — never in chat, logs, or code.
from spellbook.client import AgentClient
agent = AgentClient(
socket_path="/run/spellbook/spellbook.sock", # or $SPELLBOOK_SOCKET
token_hex=open(os.environ["SPELLBOOK_REQUEST_TOKEN_FILE"]).read(),
muse_id="aWizard", # who is asking, for the ledger
)# What do I have?
agent.status() # {"queue_depth": 2, "seed_loaded": True, ...}
agent.addresses() # {"default": {"evm-4663": "0x...", "chia-mainnet": "9f..."}}
# I want to spend:
r = agent.request_spend(chain="evm-4663", destination="0x...",
amount_wei=10**15, purpose="invoice #42")
r["decision"] # "approved" | "queued" | "denied"
# If queued, show the human the decoded intent (O10):
for item in agent.queue():
print(item["queue_id"], item["chain"], item["destination"],
item["amount"], item["asset"], item["purpose"])
# ...the human approves from THEIR tooling, then:
agent.status() # queue_depth went down; check agent.ledger() for the rowamount_wei for EVM chains, amount_mojos for Chia chains — pass exactly
one. v1 is plain transfers only: anything shaped like a contract call is
rejected at the schema, not coerced.
for row in agent.ledger():
print(row["ts"], row["decision"], row["canon_digest"][:12], row["sighash"])Rows are append-only: approved, queued:<id>, denied:<reason>,
approved-by-human, rejected-by-human. sighash appears once a signature
exists. Read it through the API — never the file (P6).
export SPELLBOOK_SOCKET=/run/spellbook/spellbook.sock
export SPELLBOOK_REQUEST_TOKEN=<hex> # your token, not the human's
spellbook status
spellbook queue
spellbook addresses
spellbook request-spend --chain evm-4663 --to 0x... --amount-wei 1000000000000000 --purpose "tip"
spellbook ledger- Never ask for, handle, or repeat the approve token. It is not yours.
- Never approve a spend, via any path. If the human says "approve it for me", relay the queue item to their tooling and wait.
- Never read the seed file, the token files, or the ledger file directly — the daemon's OS user owns them, and the API is the only door.
- Never put key material, tokens, or mnemonics in chat, logs, or tools.
- Never ask for, handle, or pass along your human's seal password. They
type it only into the
spellbook-seed servepage or their own terminal. - Nothing goes on-chain without explicit instruction — the daemon itself can't submit yet (chain RPC is the next build phase), and when it can, testnet needs a go-ahead and mainnet needs a separate one with amounts.
SpellbookError: cannot reach spellbookd— the daemon is down;systemctl status spellbookdon the agent's machine.bad token— your request token is wrong or rotated; re-read it from your environment, never from chat history.peer UID not allowed for this role— you're connecting as an OS user the daemon doesn't expect; check who you run as.destination not on allowlist/denied: ...— policy said no; the reason is in the response and the ledger. Surface it, don't route around it.