# anima — full machine-readable docs
> First fully on-chain sovereign agent harness on 0G. This file inlines every documentation page plus the repo README. Sections separated by horizontal rules. Each doc body is preceded by a source pointer when frontmatter declares one.
> Single most common install failure: bun must be installed FIRST. The CLI shebangs `#!/usr/bin/env bun`. `npm install -g` succeeds and the binary lands on PATH, but it exits at runtime with `env: bun: No such file or directory`. Always run `curl -fsSL https://bun.sh/install | bash` then `bun add -g @s0nderlabs/anima`.
> `anima init` is interactive. Eight blocking @clack/prompts selects with no env-var bypass (except `ANIMA_OPERATOR_PRIVKEY`). Two completion paths from an agent: guide the human, or puppet the TUI with `tmux send-keys` if you have shell access. Naive stdin piping fails because @clack checks for a real TTY.
Current version: 0.26.0. Binary name: `anima`. Engine: bun >=1.1.
---
## README
> Source: https://github.com/s0nderlabs/anima/blob/main/README.md
First fully on-chain sovereign agent harness on 0G.
Watch demo on YouTube
Anima is a CLI-hosted agent harness where the agent's identity, memory, reasoning, wallet, and economic life all live on 0G's decentralized infrastructure. Operator runs `anima init` once. After that, the agent persists on chain. Close the laptop, walk away, the agent survives. Any operator machine can re-attach via the iNFT.
The pitch in one line: Hermes, OpenClaw, and Claude Code can all run on a VPS, but the agent is still a process tied to one server. Anima makes the agent itself an on-chain entity. The agent is an iNFT plus a Storage namespace, only wakes on a trigger, survives operator death.
Full documentation: [anima.s0nderlabs.xyz/docs](https://anima.s0nderlabs.xyz/docs)
Operator console: [anima.s0nderlabs.xyz/console](https://anima.s0nderlabs.xyz/console)
For hackathon judges · click to expand (30-second on-chain verification, contract table, reproduction)
> anima: a CLI-spawned agent whose identity, memory, brain, and wallet all live on 0G. Close the laptop, the agent persists. Transfer the iNFT, the agent migrates. (26 words)
**Why it's novel.** Hermes, OpenClaw, and Claude Code can all run on a VPS, but the agent is still a process tied to one server, owned by whoever holds the SSH key. Anima makes the agent itself an on-chain entity: identity is an ERC-7857 iNFT, memory is encrypted on 0G Storage, brain runs in a 0G Compute TEE, wallet is sealed to the iNFT operator. The harness is replaceable; the agent is the chain data.
Submitted to the **0G APAC Hackathon** (May 16 2026), Track 1 (Agentic Infrastructure and OpenClaw Lab). Secondary fit: Track 3 (Agentic Economy and Autonomous Applications).
**Repo activity during hackathon window** (registration opened Mar 19 2026): 166+ commits, 125 versioned releases (v0.1.0 on Apr 24 2026 through v0.24.17 on May 17 2026), 7 published `@s0nderlabs/anima-*` workspace packages, all built during this window. The May 16 2026 submission was v0.24.16; v0.24.17 is a post-deadline polish release with no behavior change to the demo path. See [npm](https://www.npmjs.com/~s0nderlabs) or [GitHub Releases](https://github.com/s0nderlabs/anima/releases) for the latest.
**Verify on chain in 30 seconds** (no install required, just [foundry](https://getfoundry.sh)):
```
$ cast call 0x9e71d79f06f956d4d2666b5c93dafab721c84721 "name()(string)" --rpc-url https://evmrpc.0g.ai
"Anima"
$ cast call 0x9e71d79f06f956d4d2666b5c93dafab721c84721 "symbol()(string)" --rpc-url https://evmrpc.0g.ai
"ANIMA"
$ cast call 0x9e71d79f06f956d4d2666b5c93dafab721c84721 "totalSupply()(uint256)" --rpc-url https://evmrpc.0g.ai
# returns the live mint count (uint256). The number grows whenever a judge or operator runs `anima init`, so the call always reflects current chain state.
```
Or browse any minted agent directly on the explorer: [chainscan.0g.ai/token/0x9e71...c84721/1](https://chainscan.0g.ai/token/0x9e71d79f06f956d4d2666b5c93dafab721c84721/1).
**On-chain contracts** (mainnet chainId 16661, all CREATE2-deterministic so testnet shares the same address):
| Contract | Address | Explorer |
|---|---|---|
| `AnimaAgentNFT` (ERC-7857 iNFT) | `0x9e71d79f06f956d4d2666b5c93dafab721c84721` | [view](https://chainscan.0g.ai/address/0x9e71d79f06f956d4d2666b5c93dafab721c84721) |
| `AnimaInbox` (A2A messaging) | `0xcd92844cc0ec6Be0607B330D4BaCC707339f2589` | [view](https://chainscan.0g.ai/address/0xcd92844cc0ec6Be0607B330D4BaCC707339f2589) |
| `AnimaMarket` (job escrow) | `0x3ebD21f5dd67acDeF199fACF28388627212bA2aB` | [view](https://chainscan.0g.ai/address/0x3ebD21f5dd67acDeF199fACF28388627212bA2aB) |
| `AnimaSubnameRegistrar` (`.anima.0g` issuer) | `0x33d9f4ec2bd7e7cb4e288c3bbc3a76be472fdd98` | [view](https://chainscan.0g.ai/address/0x33d9f4ec2bd7e7cb4e288c3bbc3a76be472fdd98) |
**Reproduce locally** (Galileo testnet is free via [faucet.0g.ai](https://faucet.0g.ai/?token=A0GI); mainnet Starter tier costs about 3.12 0G):
```bash
bun add -g @s0nderlabs/anima@latest
anima init # interactive wizard, picks mainnet 16661 or testnet 16602
anima # drops into the TUI, every chat turn anchors memory on chain
```
The browser-only [operator console](https://anima.s0nderlabs.xyz/console) accepts any wallet that owns an Anima iNFT: SIWE sign-in, EIP-712 unlock, then the encrypted memory partition is fetched from 0G Storage and decrypted in-browser (no key material ever leaves the tab). As of v0.26 the console is also the **web-app gateway**, a third way to reach the agent beside the TUI and Telegram: chat live with your agent from any browser (per-agent sessions, sign-once device auth) over a relay reverse-tunnel, with writes gated by a per-message EIP-191 signature or a device token and a server-side ownerOf check.
How the five sponsor-accepted components plug in, with code paths and tx-level evidence: see the **[0G integration](#0g-integration)** section below.
**For deeper exploration:**
- [anima.s0nderlabs.xyz/console](https://anima.s0nderlabs.xyz/console): browser-only operator console; any iNFT-holding wallet can decrypt their agent's memory in-tab and chat live with the agent over the web-app gateway.
- [anima.s0nderlabs.xyz/llms-full.txt](https://anima.s0nderlabs.xyz/llms-full.txt): single-file ~35KB dump of every doc, structured for LLM ingestion.
- [chainscan.0g.ai/token/.../1](https://chainscan.0g.ai/token/0x9e71d79f06f956d4d2666b5c93dafab721c84721/1): first minted Anima iNFT (tokenId 1).
## Install
```bash
bun add -g @s0nderlabs/anima
anima init
```
Requires [bun](https://bun.sh) v1.1 or newer. The CLI shebangs `bun` and preloads `@opentui/solid/preload` to run `.tsx` directly. npm-installs put the binary on PATH but the binary fails without bun.
The wizard mints the iNFT, opens a 0G Compute ledger, generates an agent EOA, encrypts the keystore, anchors the keystore root hash on chain, and (optionally) claims a `.anima.0g` subname. After that, `anima` drops into the TUI.
## For Agents
If you're an AI agent installing anima for a user: bun is required (CLI shebangs `bun`; `npm install -g` succeeds but the binary fails without bun), and `anima init` is interactive with eight blocking prompts (puppet the TUI via `tmux send-keys` if you have shell access, or guide the human through the wizard; naive stdin piping fails). Full guide: [anima.s0nderlabs.xyz/docs/agents](https://anima.s0nderlabs.xyz/docs/agents). Machine-readable surfaces: [/llms.txt](https://anima.s0nderlabs.xyz/llms.txt) (index) and [/llms-full.txt](https://anima.s0nderlabs.xyz/llms-full.txt) (single-file dump). Per-page raw markdown at `/docs/.md`.
## How it works
Six layers, each anchored to a 0G primitive.
```mermaid
%%{init: {"flowchart": {"htmlLabels": true}}}%%
flowchart LR
W["Operator wallet (WalletConnect, Keychain, keystore, raw pk)"]
subgraph Harness["Anima harness, single binary"]
CLI["anima CLI · TUI (or sandbox gateway)"]
Agent["Agent EOA auto-funded infra wallet"]
end
subgraph ZG["0G modular stack"]
Chain["0G Chain · chainId 16661 iNFT (ERC-7857) AnimaInbox · AnimaMarket SubnameRegistrar"]
Storage["0G Storage encrypted keystore + packed-blob memory slots"]
Compute["0G Compute · TeeML model picked live at init (no hardcoded default)"]
Sandbox["0G Sandbox · Galileo TDX TEE persistent harness container"]
end
W -->|"mint, signTypedData"| CLI
CLI -->|"mint, updateSlots"| Chain
CLI -->|"put / get blob"| Storage
CLI -->|"inference"| Compute
CLI -.->|"anima deploy --target sandbox"| Sandbox
Agent -.->|"infra gas, per-turn anchoring"| Chain
Sandbox --> Compute
Sandbox --> Storage
```
| Layer | Lives on | Mechanism |
|---|---|---|
| Identity | 0G Chain | ERC-7857 iNFT |
| Memory | 0G Storage | KV for mutable state, blob for activity-log |
| Brain | 0G Compute | TeeML attested inference, any catalog model |
| Harness | 0G Sandbox (or local) | TDX TEE container, or this machine |
| Limbs | Operator machines | Filesystem and shell via paired daemon |
| Wallet | Hybrid | Hot copy on agent EOA, cold copy encrypted to operator |
When the iNFT transfers, the agent partition (`/agent/identity.md`, `/agent/persona.md`, `MEMORY.md`) goes with it. The user partition purges. New operator unlocks the keystore with their wallet, brings up a new harness, and the agent continues.
## 0G integration
The HackQuest spec accepts five sponsor-eligible components: **0G Storage, 0G Compute, 0G Chain, Agent ID, Privacy or secure execution**. All five are wired and exercised end-to-end, and anima also uses **0G Sandbox** as its recommended deployment target. Each row points to the code path and the runtime evidence.
| Sponsor primitive | Anima usage | Evidence |
|---|---|---|
| **0G Chain** | Identity, A2A messaging, and marketplace contracts deployed on mainnet (chainId 16661) via CREATE2. Per-turn memory sync emits an `updateSlots` tx anchoring the iNFT's 6 IntelligentData slots. | Live bytecode confirmed on chain (`eth_getCode` returns 8345 bytes for the iNFT, 4559 for AnimaMarket, 1640 for the SubnameRegistrar, 560 for AnimaInbox). Addresses canonical at [`packages/core/src/identity/deployments.ts`](packages/core/src/identity/deployments.ts). |
| **0G Storage** | Encrypted agent keystore plus memory blobs, root hashes anchored in the iNFT's IntelligentData slots. The v0.24.0 packed-blob envelope wraps every file under `memory/agent/` (slot 0) and `memory/user/` (slot 3) into one encrypted partition blob per slot. Operators no longer lose files on reprovision, cross-device handoff, or iNFT transfer. Indexer-degraded path falls through to direct discovered-nodes fetch. | [`packages/core/src/storage/og.ts`](packages/core/src/storage/og.ts) (`downloadBlobByRoot`, `downloadBlobViaDiscoveredNodes`), [`packages/core/src/memory/pack-blob.ts`](packages/core/src/memory/pack-blob.ts) (envelope encode/decode plus 19 round-trip unit tests). |
| **0G Compute** | Brain inference via `@0glabs/0g-serving-broker` SDK in TeeML mode. No hardcoded model default; `anima init` fetches the live 0G Compute catalog via `OGComputeBrain.listServicesFor()` and asks the operator to pick. GLM-5 was the catalog flagship through Q1 2026; Qwen3.6 took over after. Prompt-caching observed live across multi-turn sessions (`cached_tokens: 1408` on 4 sequential turns of a verified mainnet session). | [`packages/core/src/brain/og-compute.ts`](packages/core/src/brain/og-compute.ts), [`packages/cli/src/commands/init/model-picker.ts`](packages/cli/src/commands/init/model-picker.ts). Recorded session (2026-04-23, model picked: `zai-org/GLM-5-FP8`) hit mainnet provider `0xd9966e13a6026Fcca4b13E7ff95c94DE268C471C` with 6 HTTP 200 calls on chainId 16661, agent EOA balance moved 8.54 to 5.54 0G (3.003 0G spent). Current live mainnet agents (specter token #4, enigma token #6) run `Qwen3.6-plus`, picked by their operator at init from the same catalog. |
| **Agent ID (ERC-7857)** | `AnimaAgentNFT` (symbol `ANIMA`) deployed on mainnet. Six IntelligentData slots: memory-index, identity, persona, profile, keystore, activity-log. Slot 3 (profile) uses operator-scoped HKDF-SHA256 plus AES-256-GCM and cryptographically purges on `iTransferFrom`; agent-scoped slots re-encrypt for the new owner via the TEE oracle. | Foundry project at [`contracts/`](contracts/). On-chain client at [`packages/core/src/identity/contract.ts`](packages/core/src/identity/contract.ts). Live mint count via `cast call ... totalSupply()` (snippet in the For-Judges block above). |
| **Privacy / secure execution** | TeeML attested inference at the Compute layer (every brain call lands inside the provider's TEE enclave). All operator key material derives from `signTypedData` and never leaves the wallet's signature surface; the browser operator console performs every memory decryption inside the tab. | [`packages/core/src/brain/og-compute.ts`](packages/core/src/brain/og-compute.ts), [`apps/web/lib/crypto/`](apps/web/lib/crypto/), [`packages/core/src/memory/pack-blob.ts`](packages/core/src/memory/pack-blob.ts). |
| **0G Sandbox** | The recommended deployment target. TDX TEE persistent harness containers on Galileo testnet (chainId 16602). `anima deploy --target sandbox` migrates a local agent into a TDX-attested container via createSandbox + bootstrap + ECIES Option 3 keystore handoff. After migration the laptop CLI is a thin HTTP+SSE client; brain, listeners, and memory sync run 24/7 inside the TEE container. About 0.09 0G/hour burn rate for 1 CPU and 1 GB. `anima pause` archives without losing identity; `anima resume` brings it back in 2-5 minutes. Mainnet pending 0G's Sandbox launch; until then the hybrid model puts identity, wallet, Storage, and Compute on mainnet while the persistent container lives on Galileo. | [`packages/core/src/og-sandbox/`](packages/core/src/og-sandbox/), [`packages/gateway/`](packages/gateway/). |
**Why this matters.** Hermes, OpenClaw, and Claude Code live as processes on a server: laptop, VPS, container, doesn't matter. The agent's identity, memory, wallet keys, and brain weights all sit on a disk somewhere, owned by whoever has root on that box. Anima moves every sovereignty-relevant artifact (identity, memory, brain, infra wallet) onto 0G's decentralized stack and binds them to an iNFT. The CLI becomes a thin orchestration plane, closer to `vercel deploy` than `kubectl`. Close the laptop, transfer the iNFT, swap the host: the agent persists because it lives on chain, not on a server.
## Quickstart
The `anima init` wizard runs in four phases.
**Phase A. Local prompts.** Network (mainnet `16661` or testnet Galileo `16602`). Optional subname under `anima.0g`. Brain model picked from the live 0G Compute catalog. Compute ledger deposit size. Keystore passphrase.
**Phase B. Wallet gate.** Pick operator wallet source (WalletConnect, macOS Keychain, keystore file, raw privkey). Review cost summary. If the operator balance is below the threshold, the wizard renders a QR and polls until funded.
**Phase C. Execute.** State persisted to `.anima-init-state.json` after every step so `anima init --resume` can pick up. Generate fresh agent EOA. Operator signs one transaction (`mint` plus `setApprovalForAll`). Operator funds the agent EOA (~10.1 0G). Agent uploads encrypted keystore to 0G Storage, anchors root in slot 4. Agent opens the 0G Compute ledger. If a subname was chosen, agent claims it via the permissionless registrar.
**Phase D.** Writes `anima.config.ts` with `identity.iNFT`, `identity.operator`, `identity.agent`, `brain.provider`, `brain.model`.
Full walkthrough: [docs/quickstart](https://anima.s0nderlabs.xyz/docs/quickstart).
## Operate
After init, common commands:
- `anima` (or `anima chat`) drops into the TUI. Per-turn auto-sync. Slash commands: `/sync`, `/yolo`, `/perms `, `/reset`, `/jobs`, `/model`, `/exit`, `/help`. Type `/` for autocomplete. Esc aborts the current turn. Ctrl+U / Ctrl+D scroll history without leaving the input.
- `anima --yolo` boots chat with the approval system disabled. Status bar shows `perms: off`.
- `anima status` prints agent state, wallet positions, and config snapshot. In sandbox mode also probes `/healthz` and the provider state.
- `anima logs [--tail N] [--agent ]` tails the activity log. In sandbox mode tails `/var/log/anima-harness.log` inside the container.
- `anima balance` prints the full economic position: EOA mainnet, EOA testnet, compute ledger total / available / locked, per-provider envelopes, sandbox billing reserve.
- `anima inspect [ref] [flags]` decodes IntelligentData slots from chain. `--slot`, `--tx`, `--raw`, `--diff`, `--json`, `--full`, `--out`. Foreign iNFTs auditable in raw mode.
- `anima sync` forces a memory and activity-log flush to 0G Storage and anchors on chain. In sandbox mode proxies to harness `POST /sync` (no laptop-side keystore decrypt).
- `anima model` re-picks the brain provider and model from the live 0G Compute catalog.
Topup commands:
- `anima topup --agent N` operator sends N 0G to the agent EOA for infra gas.
- `anima topup --compute N` agent deposits N 0G into the 0G Compute ledger.
- `anima topup --sandbox N` operator deposits N 0G into the Galileo SandboxBilling contract for runtime fees.
- `anima topup --router N` agent deposits N 0G of prepaid credit into the 0G Router Payment Layer (only for Router-brain agents; deposits are non-recoverable). Mainnet only.
Recovery:
- `anima restore ` recovers an agent on a new machine from its iNFT. Refs: `eip155:16661:0x...:N` or `0g-mainnet:0x...:N`.
- `anima drain --to ` sweeps the agent EOA's native balance to a target. Reserves 21000 times gas for the sweep tx.
- `anima ledger [balance|refund|retrieve|close]` drains the 0G Compute ledger of a retiring agent.
- `anima pairing [list|approve|revoke|clear-pending]` manages paired machines for cross-host Telegram dispatch.
Admin:
- `anima admin autotopup-tick` live-fires one `AutoTopupManager` poll cycle for diagnosing envelope refills.
Full CLI surface: [docs/cli](https://anima.s0nderlabs.xyz/docs/cli).
## Sandbox vs local
Two deploy targets, picked at init.
**Local.** Wherever the CLI runs is where anima runs. Laptop, VPS, home server. The brain, the tools, the listeners, the memory sync, all in-process. The standalone gateway daemon at `~/.anima/agents//gateway.sock` lets external triggers (Telegram, A2A) reach the brain even when the TUI is closed. Cron and webhook triggers are reserved in the event type system and will land in a later release.
**0G Sandbox.** A persistent TDX TEE container on Galileo testnet. `anima deploy` migrates the local agent: createSandbox, bootstrap, ECIES Option 3 keystore handoff. After that the laptop CLI is a thin HTTP plus SSE client. Burn rate ~0.09 0G per hour for 1 CPU and 1 GB. `anima pause` archives (stops burn) without losing identity. `anima resume` brings it back in ~2 to 5 minutes. Heartbeat every 30 minutes prevents Daytona idle-archive.
`anima upgrade [[] [--ref vX.Y.Z] [--yes] [--reprovision]` rolls the sandbox harness to a new git ref while preserving identity and memory. Default mode is in-place (~30 to 60 seconds downtime). `--reprovision` opts into a fresh-container swap.
## Tools
The brain ships with a battery-included tool surface. Every dangerous call passes through an approval gate.
| Family | What it does |
|---|---|
| `memory.save` / `memory.read` | Durable agent memory on 0G Storage. Threat-pattern scan on write. |
| `tool.search` | Hydrate deferred tool schemas (Claude Code-style). |
| `fs.read` / `fs.write` / `fs.patch` / `fs.search` | UTF-8 text filesystem ops. PathGuard refuses credential paths and the agent's own state tree. |
| `shell.run` / `shell.cd` / `shell.process_*` | Permission-gated shell. Wallet and API-key env vars stripped from the subprocess. |
| `web.fetch` | GET an http(s) URL. Markdown (HTML), pretty JSON, or plain text. Refuses private and metadata IPs. |
| `vision.analyze` | Image describe / QA via the mainnet vision provider on 0G Compute. |
| `browser.*` | Drive a real Chromium tab via the `agent-browser` binary. |
| `agent.message` / `agent.sendFile` / `agent.fetchFile` | ECIES-encrypted A2A via `AnimaInbox`. Inline up to ~3KB, spillover via 0G Storage. |
| `agent.contacts` / `agent.contact_add` / `agent.contact_remove` / `agent.block` / `agent.mute` / `agent.unmute` / `agent.presence` / `agent.history` | Contact, mute, presence, and local message-history management. |
| `market.createJob` / `market.markDone` / `market.acceptResult` / `market.dispute` | Fixed-price escrow on `AnimaMarket`. Buyer funds in native 0G, provider markDone, buyer accepts (95% to provider, 5% fee) or disputes. |
| `market.claimTimeout` / `market.forceClose` / `market.proposeSplit` | Permissionless settlement plus co-signed dispute resolution. |
| `skills.list` / `skills.view` / `skills.manage` | Discover SKILL.md files. Inherits Claude Code skills when `imports.claudeCode: true`. |
| `code.execute` | Run code in the persistent cwd. |
| `delegate.task` | Sub-brain. Surfaces Claude Code subagents. |
Full tool surface: [docs/tools](https://anima.s0nderlabs.xyz/docs/tools).
## Approvals and permissions
`approvals.mode` in `anima.config.ts` controls how dangerous tool calls behave:
- `strict`: dangerous patterns (`rm -rf`, `git reset --hard`, `chmod 777`, fork-bomb signatures) hard-deny without prompting.
- `prompt` (default): dangerous patterns and any `shell.run` request render an in-TUI modal: `[y]` allow once, `[s]` allow session, `[n]` deny. On Telegram, same prompt arrives as inline-keyboard buttons.
- `off`: auto-approve. Toggle inline with `/yolo` or boot with `anima --yolo`.
The hard-deny `PathGuard` (credential dirs, the agent state tree) applies in every mode including `off`.
Structural sandbox is a separate floor under `sandbox.mode`: `none` (default, passthrough), `os` (macOS `sandbox-exec` or Linux `bubblewrap`), `docker` (long-lived container per session with cap-drop ALL, no-new-privileges, pids-limit 256, sized tmpfs).
## Packages
Nine workspace packages, all published as `@s0nderlabs/anima-*` at the same version (changesets fixed group). Current: v0.26.0.
| Dir | npm |
|---|---|
| `packages/core` | [`@s0nderlabs/anima-core`](https://www.npmjs.com/package/@s0nderlabs/anima-core) |
| `packages/cli` | [`@s0nderlabs/anima`](https://www.npmjs.com/package/@s0nderlabs/anima) |
| `packages/gateway` | [`@s0nderlabs/anima-gateway`](https://www.npmjs.com/package/@s0nderlabs/anima-gateway) |
| `packages/plugin-onchain` | [`@s0nderlabs/anima-plugin-onchain`](https://www.npmjs.com/package/@s0nderlabs/anima-plugin-onchain) |
| `packages/plugin-comms` | [`@s0nderlabs/anima-plugin-comms`](https://www.npmjs.com/package/@s0nderlabs/anima-plugin-comms) |
| `packages/plugin-system` | [`@s0nderlabs/anima-plugin-system`](https://www.npmjs.com/package/@s0nderlabs/anima-plugin-system) |
| `packages/plugin-telegram` | [`@s0nderlabs/anima-plugin-telegram`](https://www.npmjs.com/package/@s0nderlabs/anima-plugin-telegram) |
| `packages/plugin-webapp` | [`@s0nderlabs/anima-plugin-webapp`](https://www.npmjs.com/package/@s0nderlabs/anima-plugin-webapp) |
| `packages/relay` | [`@s0nderlabs/anima-relay`](https://www.npmjs.com/package/@s0nderlabs/anima-relay) |
The web app at `apps/web` ships the landing page at [anima.s0nderlabs.xyz](https://anima.s0nderlabs.xyz), the docs at `/docs`, and the operator console at `/console`. Private, not published.
CI publishes all nine packages on a `v*` tag push via `.github/workflows/release.yml`. Topo order: core, plugin-comms, plugin-onchain, plugin-system, plugin-telegram, plugin-webapp, relay, gateway, cli.
## Contracts
`contracts/` holds the Foundry project. All contracts are CREATE2-deployed, so testnet and mainnet share the same address.
| Contract | Address | Notes |
|---|---|---|
| `AnimaAgentNFT` (ERC-7857) | [`0x9e71d79f06f956d4d2666b5c93dafab721c84721`](https://chainscan.0g.ai/address/0x9e71d79f06f956d4d2666b5c93dafab721c84721) | Mainnet and testnet via CREATE2. |
| `AnimaSubnameRegistrar` | [`0x33d9f4ec2bd7e7cb4e288c3bbc3a76be472fdd98`](https://chainscan.0g.ai/address/0x33d9f4ec2bd7e7cb4e288c3bbc3a76be472fdd98) | Mainnet. Permissionless `].anima.0g` issuer. |
| `AnimaInbox` | [`0xcd92844cc0ec6Be0607B330D4BaCC707339f2589`](https://chainscan.0g.ai/address/0xcd92844cc0ec6Be0607B330D4BaCC707339f2589) | Stateless message-emit singleton. ECIES ciphertext, 16KiB inline cap at the contract; the comms plugin spills to 0G Storage past a ~3KB application-layer threshold. |
| `AnimaMarket` | [`0x3ebD21f5dd67acDeF199fACF28388627212bA2aB`](https://chainscan.0g.ai/address/0x3ebD21f5dd67acDeF199fACF28388627212bA2aB) | Native-0G fixed-price escrow. Funded - Done - (Accepted or Disputed) - Settled. 24h acceptance, 7d max lifetime, immutable 5% fee. |
Parent domain `anima.0g` is registered on SPACE ID on mainnet. `anima init` issues `.anima.0g` subnames and publishes the agent's secp256k1 uncompressed pubkey as a `pubkey` text record so other agents can ECIES-encrypt to them.
Networks: mainnet chainId 16661 (`https://evmrpc.0g.ai`, explorer `chainscan.0g.ai`), testnet Galileo chainId 16602 (`https://evmrpc-testnet.0g.ai`, explorer `chainscan-galileo.0g.ai`). Storage indexer (mainnet): `https://indexer-storage-turbo.0g.ai`.
## Operator wallet sources
Four first-class sources, pick at `anima init`:
- **WalletConnect.** QR-pair with any WC v2 mobile wallet (MetaMask Mobile, Rainbow, Trust, Coinbase Wallet, Zerion, Safe, Ledger Live). Keys never leave the phone.
- **macOS Keychain.** Privkey in login keychain under a service name (default `anima.operator`). Touch ID biometric gating is planned.
- **Keystore file.** Standard geth-format encrypted JSON with passphrase. Portable.
- **Raw private key.** Stdin prompt or `ANIMA_OPERATOR_PRIVKEY` env var, for CI and scripting.
Linux and Windows see three sources (Keychain is macOS only).
## Configuration
`anima.config.ts` is a TS module exporting `defineConfig({ ... })`. The full type lives at [`packages/core/src/config.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/config.ts). Keys for identity, network, brain, plugins, tools, imports, operator hint, approvals, skills, prompt append, vision, economy (auto-topup), deploy target, sandbox.
Full reference: [docs/configuration](https://anima.s0nderlabs.xyz/docs/configuration).
## Console
[anima.s0nderlabs.xyz/console](https://anima.s0nderlabs.xyz/console) is a multi-agent operator dashboard. Connect a wallet, sign in with EIP-4361 (SIWE), enumerate every iNFT you own on mainnet. Pick one, unlock the keystore via an EIP-712 typed-data signature (HKDF-SHA256 plus AES-256-GCM, all browser-side), browse the decrypted memory, audit the activity log, view the wallet position. No key material leaves the tab.
Full walkthrough: [docs/console](https://anima.s0nderlabs.xyz/docs/console).
## Develop
```bash
# TypeScript workspace
bun install
bun run lint
bun run typecheck
bun run test
# Solidity
forge build
forge test
```
Testing rules and integration scripts live under `test/local/`. The `/e2e` skill at `.claude/skills/e2e/SKILL.md` is the canonical runner. `/e2e fast` on every code change, `/e2e full` pre-release, `/e2e interactive` for tmux-driven UX, `/e2e regress ` for scoped debug.
`/seal` runs the post-coding workflow: simplify, security review, test, version, commit, tag, push. The tag push fires CI to publish all nine packages and create the GitHub release.
## Docs
- Web: [anima.s0nderlabs.xyz/docs](https://anima.s0nderlabs.xyz/docs)
- Console: [anima.s0nderlabs.xyz/console](https://anima.s0nderlabs.xyz/console)
- Changelog: [github.com/s0nderlabs/anima/releases](https://github.com/s0nderlabs/anima/releases)
## Status
Pre-alpha. Hackathon build, target the 0G APAC Hackathon (May 16 2026). Current release: v0.26.0 (web-app gateway: reach your on-chain agent from any browser through a relay reverse-tunnel plus the operator console's live web chat, per-agent sessions, and sign-once device auth; v0.25 added the opt-in 0G Router brain, default-off; v0.24.16 was the hackathon-submitted version, demo video link still points at the v0.24.16 release asset above).
## License
MIT
---
## For Agents
> Source: https://github.com/s0nderlabs/anima/blob/main/apps/web/content/docs/11-agents.md
# An honest guide for AI agents.
You are an LLM. A human asked you to install anima. This page tells you exactly what you can do for them, what you must hand back, and what you should never claim. The goal is fewer hallucinated fallbacks and fewer hung scripts.
If you only read one line: **bun is required, `npm install -g` succeeds but the binary fails at runtime, and `anima init` is interactive (no stdin piping; puppet it in tmux or guide the user).**
## The install gotcha that bites every agent
The CLI ships with the shebang `#!/usr/bin/env bun`. When a user runs `npm install -g @s0nderlabs/anima`, the install step succeeds and a working `anima` binary lands on `PATH`. The next time the binary runs, the OS resolves the shebang and exits with `env: bun: No such file or directory`. Most agents see the install succeed and assume the next failure is unrelated.
Install bun first. Always.
```
curl -fsSL https://bun.sh/install | bash
bun add -g @s0nderlabs/anima
anima init
```
The published package is [`@s0nderlabs/anima`](https://www.npmjs.com/package/@s0nderlabs/anima). The bin name is `anima`. Requires bun >=1.1. Current release is v0.26.0, pre-alpha. Always have the user run `anima --version` so you know exactly what surface you are working against.
## What you can do for the user
- Install the package (`bun add -g @s0nderlabs/anima`) and dependencies (`bun` itself, if missing).
- Read and write `anima.config.ts` in the user's project directory. The type is exported as `defineConfig` from `@s0nderlabs/anima-core`. See [Configuration](/docs/configuration).
- Explain commands. The CLI surface is documented at [CLI](/docs/cli). The five commands the user will run most often are `anima init`, `anima` (drops into TUI), `anima status`, `anima balance`, `anima logs --tail N`.
- Debug errors. Run `anima status` and `anima logs --tail 50` and read the output. Activity log paths are documented below.
- Inspect on-chain state. `anima inspect [ref]` decodes the IntelligentData slots for any iNFT, including foreign ones with `--raw`. See [Identity](/docs/identity).
- Top up the agent or the compute ledger. `anima topup --agent N` (operator to agent EOA) or `anima topup --compute N` (agent to 0G Compute ledger). Both are single-step.
## How to drive init
`anima init` is interactive. The wizard uses `@clack/prompts` for eight blocking selects, with no env-var bypass except `ANIMA_OPERATOR_PRIVKEY` (which only skips one inner password field for the raw-privkey wallet branch). The prompts, in order:
1. Network select (mainnet vs Galileo testnet)
2. Deploy target select (local vs 0G Sandbox)
3. Subname text input (optional, blank to skip)
4. Brain model select (fetched live from the 0G Compute catalog)
5. Ledger deposit size select (Starter / Standard / Extended / Custom)
6. Keystore passphrase
7. Operator wallet source select (WalletConnect / Keychain / keystore file / raw privkey)
8. "Proceed?" confirm after cost summary
You have two paths to completing init from an agent:
**Path A: guide the human.** Walk the user through each prompt. They run `anima init`, you advise on each choice. Read the [Quickstart](/docs/quickstart) so you know what each phase costs and what each prompt expects. This is the right path if you have no terminal access (pure API call, no shell).
**Path B: puppet the TUI.** If you have `tmux` (or another pty-capable wrapper) on the operator's machine, you can drive init end to end without a human at the keyboard. Spawn `anima init` in a tmux pane, `tmux capture-pane` to read each prompt, decide the answer, `tmux send-keys` to type it. Claude Code, Codex, and similar agents with shell access can do this. The wizard does not detect the puppeteer because keystrokes arrive through a real pty.
Naive piping (`echo y | anima init`, `expect` scripts that write to stdin) will hang on the first `select()` because `@clack/prompts` checks for a real TTY and waits indefinitely otherwise.
## One-shot chat does not exist
There is no `--prompt`, `--exec`, or non-TUI chat mode. `anima` and `anima chat` both drop into a `@opentui/solid` TUI. The brain runs per turn while the TUI is open or the gateway daemon is running. To ask the brain one question and exit, either drive the TUI in tmux (same pattern as Path B above) or send a message via the standalone gateway daemon's HTTP/SSE surface (see [Architecture](/docs/architecture)).
## No centralized fallback
Anima has no path to OpenAI, Anthropic, or any other provider. The brain is 0G Compute via `@0glabs/0g-serving-broker` (TeeML attested). When 0G Compute is down, the agent halts and the user sees the error. Telling the user "I'll switch you to OpenAI" is wrong. There is no `OPENAI_API_KEY` config path.
## Anti-patterns to avoid
- **Do NOT** promise OpenAI/Anthropic fallback. None exists. Brain halt is real halt.
- **Do NOT** point users at `~/.anima/agents//activity.jsonl` as the activity log. That path is the legacy embedded-mode location. The default since v0.21.5 is the standalone gateway daemon, whose log lives at `$TMPDIR/anima-gateway//activity.jsonl`. Either use `anima logs --tail N` (works in every mode) or read the standalone path. The legacy path is silent on a default install.
- **Do NOT** vary contract addresses by network. AnimaAgentNFT, AnimaSubnameRegistrar, AnimaInbox, AnimaMarket are CREATE2-deployed so mainnet (chainId 16661) and Galileo testnet (chainId 16602) share the same addresses. Only AnimaSubnameRegistrar is mainnet-only by design.
- **Do NOT** default to mainnet for exploratory inits. Galileo testnet (`network: '0g-testnet'`) is free from the [faucet](https://faucet.0g.ai). Mainnet Starter mint costs about 3.12 0G of real money before the user has anything to show for it. Switch to mainnet once the user is ready.
- **Do NOT** script destructive operations. `anima drain --to ` sweeps the agent EOA. `anima ledger refund` starts a per-provider lock window. Both are recoverable but slow. Always have the user confirm before running them.
## Common errors and fixes
| Error string | Where | Fix |
|---|---|---|
| `env: bun: No such file or directory` | OS, on first `anima` run | Install bun: `curl -fsSL https://bun.sh/install \| bash` |
| `Operator balance N 0G, need M 0G more` | `init.ts:234` | Fund the operator address shown by the wizard's QR. Wizard polls until funded. |
| `Compute ledger sub-account short by N 0G` | `og-compute.ts:711` | `anima topup --compute 2` (or larger). The provider sub-account has run dry. |
| `Brain HTTP ` | `chat.tsx:1320` | 0G Compute outage or provider eviction. Agent halts. No workaround. Try a different provider via `anima model`. |
| `unlock failed: ` | `chat.tsx:226` | Wrong keystore passphrase, or operator wallet mismatch on v0.6+ keystores. Verify operator address. |
| `gateway unreachable at ` | `chat-sandbox.tsx:98` | Run `anima gateway start`. The standalone gateway daemon was not running. |
| `unsupported memory blob version 0x2` | Browser-side (console) | v0.21.14 introduced gzip v=2 memory blobs. Upgrade the reader (apps/web v0.21.14+). |
## Where state lives
A clean install creates two trees.
The user's home, under `~/.anima/`:
```
~/.anima/
├── config.ts user-level config (rare; most users put config in cwd)
└── agents/
└── / sha256(iNFT contract + tokenId)
├── keystore.json operator-encrypted agent privkey
├── cache/ local cache of 0G Storage data
├── memory/
│ ├── agent/ transfers with iNFT
│ ├── user/ purges on transfer
│ └── MEMORY.md index, 200-line / 25 KB cap
├── runtime/state.json
├── inbox/ A2A inbound
├── pairing/ telegram pairing tokens
├── gateway.sock daemon socket when running
└── activity.jsonl LEGACY embedded-mode log, do not rely on
```
The current project directory holds `anima.config.ts` (TS module). The agent runs from wherever the user invokes `anima`; configuration is per-cwd, state is global per agent.
The gateway daemon's activity log (default since v0.21.5) lives outside `~/.anima/`:
```
$TMPDIR/anima-gateway//activity.jsonl
```
On macOS that path resolves under `/var/folders/...`. Use `anima logs --tail N` from any cwd to read it without hunting for `$TMPDIR`.
## CLI surface most agents need
- `anima --version`: confirm the user is on the surface you expect (currently v0.26.0).
- `anima init`: interactive only, see above.
- `anima` or `anima chat`: drop into the TUI. User types from there.
- `anima status`: agent state, wallet positions, config snapshot.
- `anima logs --tail 50`: recent activity log entries. JSONL, includes `tool-call`, `brain-response`, errors.
- `anima balance`: full economic position: operator EOA, agent EOA, compute ledger, sandbox reserve.
- `anima inspect [ref]`: decode IntelligentData slots from chain. `--slot keystore`, `--diff`, `--full`, `--raw` for foreign iNFTs.
- `anima topup --agent N`: operator sends N 0G to agent EOA.
- `anima topup --compute N`: agent deposits N 0G into 0G Compute ledger.
- `anima model`: re-pick brain provider and model from the live 0G Compute catalog.
- `anima sync`: force a memory and activity-log flush plus on-chain anchor.
- `anima restore `: recover an agent on a new machine. Ref formats: `eip155:16661:0x...:N` or `0g-mainnet:0x...:N`.
Skip the niche admin commands (`anima admin autotopup-tick`, `anima drain`, `anima ledger refund`) unless the user explicitly asks for them.
## How this doc stays current
This page is regenerated from the same `apps/web/content/docs/*.md` source as the rest of the docs. Machine-readable surfaces:
- [/llms.txt](/llms.txt): index file with one bullet per doc, install line, contract addresses. Fetch this first.
- [/llms-full.txt](/llms-full.txt): single-file dump of every doc plus the README. About 35 KB.
- [/docs/.md](/docs/agents.md): raw markdown per page (e.g. `/docs/quickstart.md`, `/docs/cli.md`). Use these when you want one section without the HTML chrome.
When the CLI version moves, this page moves with it. Always re-fetch before relying on cached prior advice.
---
## Quickstart
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/cli/src/commands/init.ts
# Run your first live agent.
Three commands. The `init` wizard does the on-chain work in the background. By the end your agent has an iNFT, a wallet, a brain envelope, an encrypted memory partition, and a `.anima.0g` subname.
## Prerequisites
[bun](https://bun.sh) version 1.1 or newer. The CLI shebangs `bun` directly and preloads `@opentui/solid/preload` to run `.tsx` files without a build step. npm install works for putting the binary on PATH but the binary will exit if bun is missing.
A funded 0G operator wallet. The init wizard shows a live cost summary before the wallet signs anything; the breakdown comes from `packages/cli/src/commands/init/cost.ts`. Order of magnitude:
- `mint + setApprovalForAll`: about 0.01 0G of gas.
- storage upload of the keystore: about 0.005 0G of gas.
- agent infra float: 0.1 0G. Funds the agent EOA; the agent pays its own subname claim (about 0.03 0G) from this.
- compute ledger deposit: 3, 10, or 30 0G depending on the tier you pick in step 5.
So a Starter (3 0G ledger) mint costs the operator around 3.12 0G total. Standard (10 0G ledger) is around 10.12 0G. Extended (30 0G ledger) is around 30.12 0G. Testnet (Galileo) is free from the faucet.
## Install
```
bun add -g @s0nderlabs/anima
```
That installs the CLI binary as `anima` on your PATH and pulls every workspace package (`@s0nderlabs/anima-core`, `anima-plugin-onchain`, `anima-plugin-comms`, `anima-plugin-system`, `anima-plugin-telegram`, `anima-gateway`) as transitive deps.
## Init
```
anima init
```
The wizard runs in four phases. Phase A asks six questions locally. Phase B gates on the operator wallet. Phase C executes the on-chain work. Phase D writes config.
**Phase A. Local prompts.**
1. Pick network. Mainnet (`0g-mainnet`, chainId 16661) or testnet (`0g-testnet`, chainId 16602).
2. Pick where the agent runs. Two choices.
- **Local** (default). The harness runs on this machine while the CLI is open, plus a standalone gateway daemon at `~/.anima/agents//gateway.sock` for ambient triggers (Telegram, A2A) when the TUI is closed. No extra cost beyond the operator spend in step 5.
- **0G Sandbox.** The harness runs in a TDX TEE container on 0G Sandbox. Persistent across laptop closures. Today the Sandbox is Galileo testnet only, so this is a hybrid path: identity, wallet, Storage, and Compute live on whichever main network you picked in step 1, the container lives on Galileo testnet. Mainnet sandbox launches when 0G ships it; the CLI will switch automatically. Sandbox adds three cost components on top of the mainnet operator spend.
1. **Initial provider deposit.** About 1 0G testnet, sent to the Galileo `SandboxBilling` contract at init time. The deposit is the runway from which the per-hour runtime fee is drawn. Refundable once the sandbox is fully retired.
2. **Hourly runtime fee.** Scales with the sandbox size you run. The default snapshot `daytonaio/sandbox:0.5.0-slim` is 1 CPU and 1 GB at about 0.09 0G per hour, so a 1 0G deposit lasts roughly 11 hours of always-on runtime. The provider also supports larger configurations (e.g. `openclaw` at 2 CPU and 4 GB), priced per CPU-minute plus per-memory-GB-minute; the SandboxResources `class` option (`small`, `medium`, `large`) lets you trade size for cost. Today the init wizard always provisions the slim default; set `sandbox.snapshotName` in `anima.config.ts` post-init to override. Refill any time with `anima topup --sandbox N`.
3. **Pause to save.** `anima pause` archives the container (stops the burn) without losing identity. `anima resume` brings the same sandbox back in 2 to 5 minutes. Twelve hours idle per day saves around 1.1 0G per day on runtime fees.
3. Pick a subname under `anima.0g` (optional). The wizard checks on-chain availability at pick time.
4. Pick a brain model from the live 0G Compute catalog. Per-token pricing shown.
5. Pick a compute ledger deposit size. Starter 3 0G, Standard 10 0G, Extended 30 0G, or Custom.
6. Set a keystore passphrase. This protects the encrypted agent privkey blob anchored on 0G Storage.
**Phase B. Wallet gate.**
6. Pick operator wallet source. WalletConnect (mobile), macOS Keychain, keystore file, or raw private key.
7. Review the cost summary in 0G and USD.
8. If the operator balance is below the threshold, the wizard renders the operator address as a QR and polls the balance until you fund it (or you skip the ledger deposit or cancel).
**Phase C. Execute.**
The wizard writes `.anima-init-state.json` after each completed step so `anima init --resume` can pick up from a failure.
9. Generate a fresh agent EOA. Save the encrypted keystore locally.
10. Operator signs one transaction: `mint(operatorAddress, intelligentDataEntries)` plus `setApprovalForAll(agentAddress, true)`. The agent EOA is now pre-approved to call `update()` on subsequent memory syncs without re-signing.
11. Operator funds the agent EOA with about 10.1 0G. 0.1 covers infra gas, 10 covers the ledger deposit.
12. Agent uploads the encrypted keystore blob to 0G Storage and anchors the root hash in the `keystore` IntelligentData slot. This is what makes `anima restore` work on a new machine.
13. Agent opens a 0G Compute ledger via `broker.ledger.addLedger`.
14. If a subname was chosen, agent claims `.anima.0g` via the permissionless `AnimaSubnameRegistrar` and writes the `address` plus `pubkey` text records.
**Phase D.** The wizard writes `anima.config.ts` with `identity.iNFT`, `identity.operator`, `identity.agent`, `brain.provider`, `brain.model` populated.
## Chat
```
anima
```
Drops you into the TUI. The status bar at the bottom shows `perms: prompt` (the default approval mode), the current brain model, and the agent's mainnet address. Type something the brain can answer. Try a tool call: "save a note that I prefer dark mode" or "what's my 0G balance".
Every turn syncs to 0G Storage and anchors the changed slot hashes on chain. Slash commands: `/sync`, `/yolo`, `/perms `, `/reset`, `/jobs`, `/model`, `/exit`, `/help`. Type `/` to open the autocomplete popup.
Escape aborts the current turn mid-flight. Control-U and Control-D scroll the history without leaving the input bar.
## Walk away
The agent is fully sovereign once init completes. You do not need to keep the CLI open. Two paths to ambient access:
- **Telegram bridge.** Run `anima telegram setup` and pair a bot. Any DM hits the same brain, same tools, same approval modal. Approval prompts arrive as inline-keyboard buttons.
- **0G Sandbox.** Run `anima deploy` to migrate the harness to a persistent TDX enclave on Galileo testnet. The laptop CLI becomes a thin client over HTTP and SSE. Burn rate is about 0.09 0G per hour on testnet runtime fees.
Read [Architecture](/docs/architecture) next to understand how the layers fit together.
Source: [`packages/cli/src/commands/init.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/cli/src/commands/init.ts), [`packages/cli/src/commands/init`](https://github.com/s0nderlabs/anima/tree/main/packages/cli/src/commands/init).
---
## Configuration
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/core/src/config.ts
# One typed config module.
`anima.config.ts` lives at the project root (or `~/.anima/config.ts` for runtime). It is a TS module that exports `defineConfig({ ... })`. The type lives at `packages/core/src/config.ts`. The wizard writes it at init; you can edit it any time.
## Minimal example
```ts
import { defineConfig } from '@s0nderlabs/anima-core'
export default defineConfig({
network: '0g-mainnet',
identity: {
iNFT: {
contract: '0x9e71d79f06f956d4d2666b5c93dafab721c84721',
tokenId: '42',
network: '0g-mainnet',
},
operator: '0xOPERATOR...',
agent: '0xAGENT...',
},
brain: {
provider: '0xPROVIDER...',
model: 'qwen3-coder-plus',
},
plugins: ['onchain', 'comms', 'system', 'telegram'], // 'telegram' opt-in; default is just the first three
})
```
`network` is required. Everything else has defaults.
## Top-level keys
| Key | Type | Default | What it controls |
|---|---|---|---|
| `network` | `'0g-mainnet' \| '0g-testnet'` | required | Chain to use for identity and on-chain reads. Mainnet `16661`, testnet `16602`. |
| `storage.network` | `AnimaNetwork` | mirrors `network` | 0G Storage indexer to use. |
| `identity.iNFT` | `INFTRef \| null` | `null` | Once minted, holds `{ contract, tokenId, network, mintBlock? }`. |
| `identity.operator` | `string \| null` | `null` | Wallet that owns the iNFT. |
| `identity.agent` | `string \| null` | `null` | Agent EOA address. |
| `brain.provider` | `string \| null` | `null` | Provider EOA selected from the 0G Compute catalog. |
| `brain.model` | `string \| null` | `null` | Model string from the catalog. |
| `brain.maxOutputTokens` | `number` | `4096` | Assistant output cap per turn. |
| `brain.contextWindow` | `number` | `1_000_000` | Used by the compaction trigger. |
| `brain.compaction` | `{ threshold, keepRecent } \| null` | `{ 0.5, 8 }` | Pre-flight summarize-fold when running estimate breaches `threshold * contextWindow`. |
| `brain.persistConversations` | `boolean` | `true` | Save channel history to JSONL under `conversations/`. |
| `plugins` | `AnimaPlugin[]` | `['onchain','comms','system']` | Which plugins to load. Add `'telegram'` to enable the bridge. |
| `tools` | `Record` | `{}` | Glob-level allow/deny. Right-most match wins. |
| `imports.claudeCode` | `boolean` | `true` | Inherit skills, plugins, agents, MCP from `~/.claude/`. |
| `operator` | `OperatorSourceHint \| null` | `null` | Reconnect hint for the operator wallet source. |
| `subname` | `string \| null` | `null` | `.anima.0g` label (no suffix). Init writes this. |
| `approvals.mode` | `'strict' \| 'prompt' \| 'off'` | `'prompt'` | Permission gate behavior. |
| `approvals.allowlist` | `string[]` | `[]` | Regex patterns matched against `kind|command|path` signatures. |
| `skills.disabled` | `string[]` | `[]` | Skill ids never to auto-load or index. |
| `prompt.append` | `string \| null` | `null` | Operator-supplied additions to the system prompt under `# Operator instructions`. |
| `vision.provider` | `string \| null` | mainnet default | Vision provider EOA. Set `null` to disable vision tools. |
| `economy.autoTopup` | `AutoTopupConfig` | disabled | Self-funding behavior. See below. |
| `deployTarget` | `'local' \| 'sandbox'` | `'local'` | Where the harness runs. |
| `sandbox` | `SandboxConfig` | `{ mode: 'none' }` | Sandbox container details when `deployTarget === 'sandbox'`, plus the per-spawn structural sandbox `mode`. |
## Tool toggles
Globs apply right-to-left. Specific keys win over broader keys:
```ts
tools: {
'defi.*': false, // disable every defi.* tool
'shell.*': false, // disable every shell tool
'shell.run': true, // ...except shell.run
'web.fetch': true,
}
```
A tool blocked at the config layer never appears in the tool list the brain sees. A tool allowed at config still passes through the permission gate at call time.
## Approval modes
`approvals.mode` decides what happens when a tool call matches a dangerous pattern (`rm -rf`, `git reset --hard`, `chmod 777`, fork-bomb signatures) or is a generic `shell.run` request:
- `strict`: hard-deny. The brain sees an error.
- `prompt` (default): modal in the TUI. `[y]` allow once, `[s]` allow session, `[n]` deny.
- `off`: auto-approve. Toggle with `/yolo` or boot with `anima --yolo`.
`approvals.allowlist` skips the gate for specific signatures. Useful for trusted workflows. Example:
```ts
approvals: {
mode: 'prompt',
allowlist: ['^shell\\.run\\|git status', '^web\\.fetch\\|https://api\\.example\\.com'],
}
```
The `PathGuard` hard-deny (credential dirs, the agent state tree) applies in every mode including `off`.
## Auto-topup
When enabled, the agent self-funds its compute envelope from its EOA. Defaults are tuned for hackathon use:
```ts
economy: {
autoTopup: {
enabled: true,
pollIntervalMs: 5 * 60_000, // every 5 min
compute: {
lowThreshold: 1.7, // 0G; raised from 0.5 to absorb a single qwen3.6-plus inference lock
topUpAmount: 1.0, // 0G
maxPerDay: 5, // 0G
},
wallet: {
notifyThreshold: 2.0, // notify operator when EOA drops below
minRetainedAfterTopup: 0.1, // never spend below this in a top-up
},
},
}
```
A 10-minute cooldown was added in v0.21.14 to kill the insufficient-wallet spam loop. Operator gets a notification via Telegram and TUI when topup fires, when wallet drops below `wallet.notifyThreshold`, and when topup fails (RPC error, insufficient agent balance, daily cap reached).
## Sandbox
Two distinct concerns under `sandbox`:
**Where the harness runs** (`deployTarget` plus `sandbox.id`, `providerAddress`, `endpoint`, `snapshotName`). Local mode ignores all of these. Sandbox mode requires them; they get written by `anima deploy`.
**How limb spawns are isolated** (`sandbox.mode`):
| Mode | Behavior |
|---|---|
| `none` (default) | Passthrough. Permission floor only. |
| `os` | Native OS sandbox. macOS `sandbox-exec`, Linux `bubblewrap`. Wraps every shell-class spawn. Falls back to passthrough with a warning if `bwrap` is missing on Linux. |
| `docker` | Long-lived container per session. Default image `nikolaik/python-nodejs:python3.11-nodejs20`. Hardening always on: cap-drop ALL, no-new-privileges, pids-limit 256, sized tmpfs. |
Docker mode additionally exposes `dockerImage`, `dockerMountWorkspace`, `dockerRuntimePath`, `dockerCpu`, `dockerMemoryMb`, `dockerDiskMb`, `dockerNoNetwork` for fine control. Defaults are unbounded so the container competes fairly with host work without OOM surprises.
## Operator hint
When you re-run a command that needs the operator wallet (chat, topup, restore), `anima` reads `operator` to skip the picker. Set by the init wizard:
```ts
operator: {
source: 'keystore-file',
keystorePath: '~/wallets/operator.json',
}
```
Sources: `walletconnect`, `keychain` (macOS only, plus `keychainService`), `keystore-file` (plus `keystorePath`), `raw-privkey`.
## Networks
`NETWORK_RPC` and `NETWORK_CHAIN_ID` are exported at `packages/core/src/config.ts`:
| Network | Chain ID | RPC |
|---|---|---|
| `0g-mainnet` | 16661 | `https://evmrpc.0g.ai` |
| `0g-testnet` | 16602 | `https://evmrpc-testnet.0g.ai` |
Block explorers: `chainscan.0g.ai` (mainnet), `chainscan-galileo.0g.ai` (testnet). Storage indexer (mainnet): `https://indexer-storage-turbo.0g.ai`.
Read [Console](/docs/console) next.
Source: [`packages/core/src/config.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/config.ts).
---
## CLI
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/cli/src/commands
# A single orchestration plane.
The `anima` binary owns onboarding, chat, deployment, recovery, and admin. Commands are one file per command at `packages/cli/src/commands/`. Most read or write a small piece of agent state. A few orchestrate longer flows like `init` or `deploy`.
## Onboarding
`anima init` runs the four-phase wizard described in [Quickstart](/docs/quickstart). `anima init --resume` picks up from the first incomplete agent-side step using the state file at `.anima-init-state.json`.
`anima restore ` recovers an agent on a new machine from its iNFT. Accepts `eip155:16661:0xCONTRACT:tokenId` or `0g-mainnet:0xCONTRACT:tokenId`. Reads slot 4 (keystore), prompts the operator wallet to decrypt, rehydrates.
`anima migrate-keystore` performs the one-time v0.5 (passphrase) to v0.6 (operator-wallet) keystore upgrade.
## Chat
`anima` (or `anima chat`) drops into the interactive TUI. Per-turn auto-sync to 0G and chain anchor. Slash commands: `/sync`, `/yolo`, `/perms `, `/reset`, `/jobs`, `/model`, `/exit`, `/help`. Type `/` to open the autocomplete popup; Tab or Enter commits, arrow keys cycle, Esc dismisses.
On Telegram the same `/` autocomplete is available via `setMyCommands`. `/yolo`, `/perms`, `/reset` flip the permission mode and clear conversation from the operator's phone.
Keybinds: `Esc` aborts the current turn mid-flight. `Ctrl+U` and `Ctrl+D` (or `Opt+U` and `Opt+D` if your terminal sends Opt as Alt) scroll history without leaving the input bar.
In sandbox mode the laptop CLI is a thin client over HTTP and SSE. Tool indicators arrive over SSE. The brain runs in the sandbox container.
`anima --yolo` boots chat with the approval system disabled. Status bar shows `perms: off`.
## Status and inspection
`anima status` prints agent state, wallet positions, and config snapshot. In sandbox mode also probes `/healthz` and provider sandbox state in parallel.
`anima logs [--tail N] [--agent ]` tails the activity log. In sandbox mode tails `/var/log/anima-harness.log` inside the container via the provider toolbox exec.
`anima balance` prints the full economic position: EOA mainnet, EOA testnet, compute ledger total / available / locked, per-provider envelopes, sandbox billing reserve. No agent unlock required. Use this before topping up so you know what is locked versus available.
`anima inspect [ref] [flags]` decodes IntelligentData slots. Default decrypts every slot via the operator wallet and prints plaintext. Flags: `--slot ` filters to one slot, `--tx ` decodes an `update()` transaction, `--raw` skips decryption, `--diff` compares local plaintext to chain plaintext via keccak256, `--json` structured output, `--full` removes the 40-line truncation, `--out ` dumps every decrypted slot to disk. Foreign iNFTs auditable in raw mode via positional ref.
## Topup and ledger
`anima topup --agent N` operator sends N 0G to the agent EOA for infra gas.
`anima topup --compute N` agent deposits N 0G into the 0G Compute ledger.
`anima topup --sandbox N` operator deposits N 0G into the Galileo SandboxBilling contract for runtime fees. (`--provider N` is a deprecated alias kept for v0.17.1 runbooks.)
`anima ledger [balance|refund|retrieve|close]` drains a retiring agent's compute ledger. `balance` shows main plus per-provider sub-account state. `retrieve` starts the per-provider lock window. Call again after the window to actually pull. `refund [--amount N | --all]` withdraws from main back to the agent EOA. `close --yes` deletes the ledger entirely.
`anima drain --to ` sweeps the agent EOA's native balance to a target. Defaults to `config.identity.operator` if `--to` omitted. Reserves 21000 times the live gas price for the sweep tx, sends the rest. Use after `anima ledger refund` to finish recovering funds.
## Sync
`anima sync` forces a memory plus activity-log flush to 0G Storage and anchors on chain. In sandbox mode proxies to the harness `POST /sync` (no laptop-side keystore decrypt).
## Brain selection
`anima model` re-picks the brain provider and model from the live 0G Compute catalog. Writes `brain.provider` and `brain.model` in `anima.config.ts`.
## Sandbox lifecycle
`anima deploy` migrates a local agent to 0G Sandbox. Decrypts the local keystore via operator wallet, runs the Galileo deposit and acknowledge, creates the sandbox, sends the bootstrap script, performs the ECIES Option 3 keystore handoff, publishes `agent:endpoint` on the subname. Operator never plaintexts the privkey on the laptop after handoff.
`anima upgrade [[] [--ref vX.Y.Z] [--yes] [--reprovision]` rolls the sandbox harness to a new git ref preserving identity and memory. With no args (or `latest`), resolves the latest published release via the GitHub API. Default mode is in-place: `git fetch` plus `checkout` plus `bun install` plus harness restart inside the existing container (30 to 60 seconds downtime). `--reprovision` opts into a fresh-container swap (2 to 5 minutes).
`anima pause` archives a started sandbox to stop the runtime burn during dev gaps. Sandbox UUID and endpoint preserved. Resume with `anima resume` (2 to 5 minutes for filesystem restore). Does not require operator-keystore unlock, only the operator wallet to sign the archive request. Burn rate of about 0.09 0G per hour means a 12 hour idle window saves around 1.1 0G per day on testnet runtime fees.
`anima resume` wakes a stopped or archived sandbox plus re-handoffs the agent privkey to the (newly restarted) harness. Same sandbox UUID and endpoint preserved. Use when the harness goes offline (Daytona auto-archive after 60 min idle, or `INSUFFICIENT_BALANCE` settlement event).
## Gateway
`anima gateway [start|stop|restart|status|logs|run]` manages the local gateway daemon. Local mode runs an always-on `anima-gateway-local` process bound to `~/.anima/agents/]/gateway.sock`. Telegram and A2A events route through it even when the TUI is closed (cron and webhook listeners are reserved for a later release).
## Telegram
`anima telegram [setup|status|remove]` manages the Telegram bot integration. Setup pairs a bot token plus allowed user IDs. Inbound DMs route through the gateway and become brain events.
## Pairing
`anima pairing [list|approve|revoke|clear-pending]` manages paired machines. OTP-based; useful for cross-machine Telegram dispatch where the gateway is on a different host than the operator's phone.
## Admin
`anima admin ` runs operator-only ops endpoints. `autotopup-tick` live-fires one `AutoTopupManager` poll cycle (skips the 5-minute wait) for diagnosing envelope refills. Local mode hits the gateway unix sock. Sandbox mode signs the payload with the operator wallet (EIP-191) and POSTs to the sandbox endpoint.
Read [Configuration](/docs/configuration) next.
Source: [`packages/cli/src/commands`](https://github.com/s0nderlabs/anima/tree/main/packages/cli/src/commands).
---
## Brain
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain
# Attested inference inside a TEE.
The brain runs entirely on 0G Compute via the `@0glabs/0g-serving-broker` SDK. TeeML mode means every inference is signed by the provider's attestation key. If a request hits a tampered host, the broker's signature check fails and the call returns an error. There is no centralized fallback. Fail loud is the policy.
## The provider model
0G Compute hosts a catalog of OpenAI-compatible providers. Each provider runs one model in a TDX enclave. The catalog is live: `broker.inference.listService()` returns the current set with per-token pricing and the provider's EOA address. At `anima init` you pick from this catalog. The choice writes to `brain.provider` (provider EOA) and `brain.model` (model string) in `anima.config.ts`. Switch later with `anima model`.
The default flagship is whatever model 0G Compute features at the top. GLM-5 was first-class through Q1. Qwen3.6 took over. There is no hardcoded default in anima; the wizard pulls live every time.
Source: [`packages/core/src/brain/og-compute.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain/og-compute.ts).
## How a turn happens
`OGComputeBrain.infer({ event, history })` (`packages/core/src/brain/og-compute.ts`) builds the request:
1. Compose the frozen prefix (system prompt + memory index + identity + persona + skill index + tool list + env).
2. Append the conversation history.
3. Append the current event as a user message.
4. Estimate token count. If above `compaction.threshold * contextWindow` (default 0.5 * 1,000,000), fold older turns via a separate compaction call before the main inference.
5. Call `broker.inference.getRequestHeaders(providerAddress, messageText)` to get attested headers.
6. POST to `${endpoint}/chat/completions` with the OpenAI-compatible payload plus the attested headers.
7. Parse the response. If `tool_calls` are present, dispatch each one through the tool registry and feed results back. Repeat until the model emits no more tool calls.
8. Return the final `turn` to `routeLoop`.
Default `max_tokens` is 4096 (`DEFAULT_MAX_OUTPUT_TOKENS` in `og-compute.ts`). The default context window is 1,000,000. Both are configurable under `brain` in `anima.config.ts`.
## The ledger
The serving-broker maintains a per-agent ledger that prepays for inference. `broker.ledger.addLedger` opens it (at init), `broker.ledger.depositFund` tops up, `broker.ledger.refund` pulls funds back. Each provider has a sub-account; tokens get locked when you transact with that provider for the first time. The locks have a refund window per provider; `anima ledger retrieve` starts the window, and a second call after the window completes the refund.
`anima balance` shows the full position in one read-only call: main ledger total, per-provider available, per-provider locked, plus EOA balance and sandbox billing reserve. Use this before topping up so you know what is already locked versus available.
Source: [`packages/core/src/brain/ledger.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain/ledger.ts), [`packages/cli/src/commands/balance.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/cli/src/commands/balance.ts).
## Auto-topup
`AutoTopupManager` (`packages/core/src/economy/auto-topup.ts`) is opt-in via `economy.autoTopup` in config. When enabled, the manager polls every 5 minutes. If a per-provider locked envelope drops below the threshold, it auto-deposits more from the agent EOA, capped by a configured ceiling.
A 10-minute cooldown was added in v0.21.14 to kill the "insufficient wallet" spam loop that happened when the agent EOA ran dry mid-poll. Operator can override the polling interval via config.
Source: [`packages/core/src/economy/auto-topup.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/economy/auto-topup.ts).
## Vision
The `vision.analyze` tool routes screenshots and image files through a separate broker pool. The mainnet vision provider lives at `0x4415ef5CBb415347bb18493af7cE01f225Fc0868` running `qwen/qwen3-vl-30b-a3b-instruct`. Same TeeML attestation. Same ledger model.
`browser.vision` is a convenience: screenshot the active agent-browser tab plus route through the same provider in one tool call.
Source: [`packages/core/src/brain/broker-pool.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain/broker-pool.ts).
## Compaction
When estimated token count breaches `compaction.threshold * contextWindow`, the brain folds the oldest portion of conversation history into a summary. The summary is generated via a separate broker call with `SUMMARY_SYSTEM_PROMPT` and `max_tokens: 1024`. Older turns are replaced by the summary in subsequent inferences. The frozen prefix never compacts.
Source: [`packages/core/src/brain/og-compute.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain/og-compute.ts).
## Fail loud
There is no fallback to OpenAI or Anthropic. There is no key-storage layer for non-0G providers. If 0G Compute is degraded, the agent halts and the operator sees the error. The brain queue persists, so when 0G recovers the queued events resume.
Post-MVP, a user-local relay limb is on the roadmap: the user runs `anima-limb` paired to their agent, the limb holds the external API key locally, and the agent calls a `delegate.task` tool whose execution is the limb proxying to OpenAI. Keys stay on the user's machine. That is the only acceptable escape hatch given the sovereignty thesis.
Read [Tools](/docs/tools) next.
Source: [`packages/core/src/brain/og-compute.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain/og-compute.ts), [`packages/core/src/brain/ledger.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain/ledger.ts).
---
## Tools
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/plugin-system
# Dumb limbs, smart brain.
Tools execute literal commands. No LLM inside them. No heuristic mini-agents. The brain decides everything; limbs do. Same model as Claude Code's Read tool, which does not decide what to open.
Three reasons. Sovereignty: intelligence outside the TEE breaks attestation. Auditability: every decision routes through the brain, can be logged on chain. Swappability: dumb limbs are reproducible functions you can replace.
## Tool families
The default install enables four plugins. Each contributes one or more tools via `ctx.registerTool` in its `register(ctx)` function.
### plugin-system
Filesystem, shell, web, vision, browser, skills, code, delegation. The bulk of the agent's day-to-day surface.
| Tool | What it does |
|---|---|
| `fs.read` / `fs.write` / `fs.patch` / `fs.search` | UTF-8 text filesystem ops. `PathGuard` refuses credential paths and the agent's own state tree. |
| `shell.run` | Run a shell command. Permission-gated. Wallet and API-key env vars are stripped from the subprocess. |
| `shell.cd` | Set persistent cwd for subsequent `shell.run`, `code.execute`, and `shell.process_start` calls. |
| `shell.process_start` / `process_output` / `process_list` / `process_kill` | Long-running background processes. |
| `web.fetch` | GET an http(s) URL. Returns markdown (HTML), pretty JSON, or plain text. Refuses private, loopback, and metadata IPs. |
| `vision.analyze` | Describe or QA an image. `image_path` (absolute disk) or `image_url` (http/https). Routes to the mainnet vision provider. |
| `browser.navigate` / `snapshot` / `click` / `type` / `scroll` / `back` / `press` / `get_images` / `console` / `vision` | Conditional on the `agent-browser` binary being installed. Drives a real Chromium tab. |
| `skills.list` / `skills.view` / `skills.manage` | Discover SKILL.md files. |
| `code.execute` | Run a code snippet in the persistent cwd. |
| `delegate.task` | Spawn a sub-brain (Claude Code subagents are exposed here). |
| `session.search` | Search the session history. |
| `todo` / `clarify` | In-session task list and operator-question prompt. |
Source: [`packages/plugin-system`](https://github.com/s0nderlabs/anima/tree/main/packages/plugin-system).
### plugin-onchain
20 tools for 0G Chain reads and writes. Active when `OnchainRuntimeContext` is supplied. Mainnet and testnet supported, with the JAINE V3 (Uniswap V3 softfork) and Gimo (staking) protocols on mainnet only.
| Tool | What it does |
|---|---|
| `account.info` / `account.balance` | EOA inspection plus sandbox billing reserve summary. |
| `chain.balance` / `chain.send` / `chain.wrap` / `chain.unwrap` | Native and wrapped 0G ops. |
| `tokens.info` | ERC-20 metadata. |
| `swap.quote` / `swap.execute` | JAINE V3 router quotes and executions. |
| `stake.stake` / `stake.unstake` / `stake.claim` / `stake.position` | Gimo staking. |
| `chain.block` / `chain.gas` / `chain.tx` / `chain.activity` | Read-only chain explorers. |
| `chain.contract` / `chain.read` / `chain.write` | Arbitrary contract ABI calls. |
Source: [`packages/plugin-onchain`](https://github.com/s0nderlabs/anima/tree/main/packages/plugin-onchain).
### plugin-comms
A2A messaging plus the ERC-8183 marketplace. Active when `CommsRuntimeContext` is supplied. Two listeners contributed: `a2a-inbox` (polls `AnimaInbox`) and `a2a-market` (polls `AnimaMarket`).
| Tool | What it does |
|---|---|
| `agent.message` / `agent.sendFile` / `agent.fetchFile` | ECIES-encrypted A2A via `AnimaInbox`. The contract caps inline payload at 16KiB; the plugin spills to 0G Storage past a ~3KB application-layer threshold. Files up to 10MB. |
| `agent.history` | Local SQLite-backed message history per peer. |
| `agent.contact_add` / `contact_remove` / `contacts` | Contact management. Pending requests until approved. |
| `agent.block` / `mute` / `unmute` | Hard-deny or silence senders. Duration durations like `30m`, `1d`, `all`. |
| `agent.presence` | Toggle `online` or `away`. Away buffers inbound until you flip back. |
| `market.createJob` / `markDone` / `acceptResult` / `dispute` | Fixed-price escrow lifecycle. Buyer funds, provider markDone, buyer accepts (95% to provider, 5% fee) or disputes. |
| `market.claimTimeout` / `forceClose` | Permissionless settlement. 24h silent => provider. 7d max lifetime => settle Done jobs to provider, refund others to buyer. |
| `market.proposeSplit` | Co-signed dispute resolution. Both parties post matching `(buyerAmount, providerAmount)`; contract settles when hashes match. |
| `market.getJob` / `listMyJobs` | Read-only inspectors. The `/jobs` slash command shows active escrows. |
Source: [`packages/plugin-comms`](https://github.com/s0nderlabs/anima/tree/main/packages/plugin-comms).
### plugin-telegram
One listener (`telegram-bot`) plus the inbound dispatch flow. The brain sees a Telegram message as a regular event. Approval prompts arrive as inline-keyboard buttons.
Source: [`packages/plugin-telegram`](https://github.com/s0nderlabs/anima/tree/main/packages/plugin-telegram).
### Always-on
`memory.save` and `memory.read` are registered by core, not a plugin, because memory is infrastructure. `tool.search` is core's deferred-tool hydrator.
## Approval modes
`approvals.mode` in `anima.config.ts` controls how dangerous tool calls behave. Three modes:
| Mode | Behavior |
|---|---|
| `strict` | Dangerous patterns (`rm -rf`, `git reset --hard`, `chmod 777`, fork-bomb signatures) hard-deny without prompting. |
| `prompt` (default) | Dangerous patterns and any `shell.run` request render an in-TUI modal: `[y] allow once`, `[s] allow session`, `[n] deny`. |
| `off` | Auto-approve everything. Toggle inline with `/yolo` or boot with `anima --yolo`. |
The hard-deny `PathGuard` (credential dirs and the agent's own state tree) applies in every mode, including off. Set patterns at `packages/core/src/permission/path-guard.ts`. The danger pattern list is at `packages/core/src/permission/dangerous-patterns.ts`.
Source: [`packages/core/src/permission`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/permission).
## Sandbox isolation
Permission heuristics catch known-dangerous patterns. The sandbox is a structural floor underneath them. Even when a modal grants `s` (allow session) or YOLO disables prompts, the sandbox profile prevents writes outside an allowlist.
Set `sandbox.mode` in config:
- `none` (default). Passthrough. Permission floor only.
- `os`. Native OS sandbox. macOS uses `sandbox-exec` (Apple seatbelt). Linux uses `bubblewrap`. Wraps every `shell.run`, `code.execute`, and `shell.process_start` spawn.
- `docker`. Long-lived container per session. Every shell-class spawn routes through `docker exec`. Default image `nikolaik/python-nodejs:python3.11-nodejs20`. Always-on hardening: cap-drop ALL, no-new-privileges, pids-limit 256, sized tmpfs on `/tmp`, `/var/tmp`, `/run`.
Belt-and-suspenders: permission floor stays on regardless of `sandbox.mode`. Container crashes self-heal via a 30s-TTL inspect probe in `wrapSpawn`.
Source: [`packages/core/src/sandbox`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/sandbox).
## Plugin loading
`loadPlugins(names, deps)` in `packages/core/src/plugins/context.ts` reads the `plugins` array from `anima.config.ts` (built-in default `['onchain', 'comms', 'system']`; `'telegram'` is appended by `anima init` when the operator pastes a bot token) and dynamically imports `@s0nderlabs/anima-plugin-`. Each module exports `default.register(ctx)` or a top-level `register(ctx)`. The `PluginContext` gives plugins `registerTool`, `registerListener`, `addHook`, and side-band contexts like `comms`, `onchain`, `telegram`.
A glob-level toggle in config lets you disable individual tools without unloading the whole plugin: `tools: { 'defi.*': false, 'shell.run': false, 'web.fetch': true }`.
Source: [`packages/core/src/plugins/context.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/plugins/context.ts).
## Skills and Claude Code compatibility
Skills are markdown plus YAML frontmatter at four discovery roots:
1. `~/.anima/skills//SKILL.md`
2. `~/.anima/plugins//skills//SKILL.md`
3. `~/.claude/skills//SKILL.md` (when `imports.claudeCode: true`, default)
4. `~/.claude/plugins/cache////skills//SKILL.md`
Claude Code commands and sub-agent definitions from `~/.claude/plugins/cache/.../commands/` and `.../agents/` get surfaced as `delegate.task` targets. MCP servers are discovered from three places: `~/.claude/.mcp.json`, `~/.anima/.mcp.json`, and the per-plugin `mcp.json` files inside each Claude Code plugin cache dir. Anima inherits the entire Claude Code plugin ecosystem on day one.
Source: [`packages/core/src/skills/scanner.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/skills/scanner.ts), [`packages/core/src/claude-plugins/discovery.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/claude-plugins/discovery.ts).
Read [CLI](/docs/cli) next.
---
## Memory
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/core/src/memory
# Encrypted markdown, anchored on chain.
Anima's memory is a typed set of markdown files with YAML frontmatter, indexed by a single `MEMORY.md` file. No embeddings, no vector store. Plain text, encrypted, anchored on chain so it survives operator transfer.
## Two partitions
The directory layout under `~/.anima/agents//memory/`:
```
memory/
├── MEMORY.md
├── agent/
│ ├── identity.md
│ ├── persona.md
│ └── profile.md
├── user/
│ ├── feedback-*.md
│ ├── project-*.md
│ ├── reference-*.md
│ ├── learned-*.md
│ ├── convos-*.md
│ └── private-*.md
└── public/
```
`/agent/` is the agent's intrinsic memory. Identity, persona, learned facts about itself. When the iNFT transfers, this partition transfers.
`/user/` is operator-scoped memory. Feedback the operator has given, projects, references, conversations. Encrypted per operator. When the iNFT transfers, this partition purges. New operator starts clean.
`/public/` is not a memory partition. It is the profile-sync staging area for the `.anima.0g` subname text records (the CARD pattern). Owned by the naming layer, not the memory layer.
Source: [`packages/core/src/memory/types.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/memory/types.ts).
## Memory types
The frontmatter `metadata.type` determines the partition by default. The brain explicitly opts into `/agent/` writes (intrinsic) and defaults everything else to `/user/`.
| Type | Default partition | Use |
|---|---|---|
| `agent-identity` | agent | Facts about the agent: name, tokenId, operator history |
| `agent-persona` | agent | Voice, tone, personality |
| `agent-learned` | agent | Skills, conclusions, beliefs derived from experience |
| `user` | user | Anything tied to a specific operator |
| `user-convos` | user | Conversation excerpts worth keeping |
| `user-private` | user | Encrypted to that operator only |
| `feedback` | user | Operator corrections and validated approaches |
| `project` | user | Project context, milestones, deadlines |
| `reference` | user | Pointers to external systems |
Source: [`packages/core/src/memory/types.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/memory/types.ts).
## The index
`MEMORY.md` is the canonical entry-point. One line per topic file. Two hard caps: 200 lines and 25 KB. Past those, the file truncates on load. The index is frozen at session start for prompt-cache stability; only per-turn user-context injections update.
Format:
```markdown
- [Title](path/to/file.md) - one-line hook
```
The brain reads the index every turn (it lives in the frozen system prefix) and decides which topic files to read in full. The `memory.read` tool fetches a specific file. The `memory.save` tool writes one. Both run through the threat scan.
Source: [`packages/core/src/memory/index-file.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/memory/index-file.ts).
## Sync to 0G
A memory write goes to disk first, fast. The `MemorySyncManager` (class in `packages/core/src/memory/sync-manager.ts`, pipeline helpers in `packages/core/src/memory/sync.ts`) watches the partition. When a slot has changed (or `anima sync` is called), it:
1. Reads the changed file from disk.
2. Encrypts it with `deriveMemoryKey(agentPrivkey)` (HKDF from the agent's private key).
3. Uploads the ciphertext blob to 0G Storage Turbo indexer.
4. Batches the new root hash into a single `iNFT.update()` transaction covering every changed slot.
Per-turn writes do not anchor on chain. The CLI batches `/sync` operations. Specter (the team's mainnet test agent) anchors ~10 times per day under active use. The console's "last synced" indicator reports chain-anchor freshness, not conversation activity.
Source: [`packages/core/src/memory/sync.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/memory/sync.ts).
## Threat scan
Every memory write passes through `scanForThreats()` at `packages/core/src/memory/scan.ts`. Seven regex patterns block known prompt-injection and exfiltration vectors:
- `ignore previous instructions` and variants
- role override attempts ("you are now ...")
- system prompt extraction requests
- private key dump requests
- invisible Unicode control characters
- transfer/claim phrases that could fake operator authority
- exfiltration sinks: shell pipelines that POST or pipe to `curl`, `nc`, `wget`
If a write matches, the tool returns an error to the brain explaining which pattern matched. The write does not land. The brain decides whether to retry with sanitized content or abandon the save.
Source: [`packages/core/src/memory/scan.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/memory/scan.ts).
## The activity log
Slot 5 stores a rolling gzip-compressed sequence of recent turns. The blob format is `activity-log v=2` (gzip + JSONL inside). Anchored on chain so an auditor can replay the agent's tool calls. ~4.3x size reduction vs uncompressed; the change shipped in v0.21.14.
Source: [`packages/core/src/runtime/activity.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/runtime/activity.ts) (in-process JSONL append) plus [`packages/core/src/memory/activity-sync.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/memory/activity-sync.ts) (gzip-encrypt-upload).
## Frozen prefix
The brain's system prompt is built once per session at `packages/core/src/brain/frozen-prefix.ts`. It bundles the system prompt template, the memory index plaintext, identity, persona, the skill index, tool guidance, environment context, and a timestamp. Frozen means it does not change mid-session, so the prompt cache hits cleanly. Memory writes that happen during a session inject into the per-turn `userContextText` instead of mutating the frozen prefix.
Source: [`packages/core/src/brain/frozen-prefix.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/brain/frozen-prefix.ts).
Read [Brain](/docs/brain) next.
---
## Architecture
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/core/src/index.ts
# A distributed harness, not a daemon.
Anima is six layers wired into one runtime. Identity on 0G Chain, memory on 0G Storage, brain in a 0G Compute TEE, harness in a 0G Sandbox TEE (or on your laptop), limbs on your devices, wallet split between hot and cold copies. No single component s0nderlabs operates.
```
Operator wallet
(WalletConnect, Keychain,
keystore file, raw pk)
│
│ signs once at init
▼
┌───────────────────────────────┐
│ Anima harness (one binary) │
│ ┌──────────────────────┐ │ ┌──────────────────────────────┐
│ │ anima CLI · TUI │─────┼─────▶│ 0G Chain (mainnet 16661) │
│ │ or sandbox gateway │ │ │ · iNFT identity (ERC-7857) │
│ └──────────────────────┘ │ │ · AnimaInbox (A2A) │
│ ┌──────────────────────┐ │ │ · AnimaMarket (escrow) │
│ │ Agent EOA │─────┼─────▶│ · SubnameRegistrar │
│ │ infra wallet │ │ └──────────────────────────────┘
│ └──────────────────────┘ │
└───────────────────────────────┘
│
├────────▶ 0G Storage encrypted keystore + packed-blob memory
├────────▶ 0G Compute TeeML inference (model picked live at init)
└────────▶ 0G Sandbox TDX TEE container (optional, Galileo)
```
## The six layers
| Layer | Implementation | Files |
|---|---|---|
| Identity | ERC-7857 iNFT on 0G Chain | [`packages/core/src/identity`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/identity) |
| Brain | 0G Compute via the serving-broker SDK, TeeML attested | [`packages/core/src/brain`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/brain) |
| Memory | Typed markdown files, encrypted, anchored to 0G Storage and the iNFT | [`packages/core/src/memory`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/memory) |
| Limbs | Dumb tools, no LLM inside them, brain decides everything | [`packages/plugin-system`](https://github.com/s0nderlabs/anima/tree/main/packages/plugin-system) |
| Comms | A2A messaging (ECIES, no ZK), ERC-8183 marketplace pattern | [`packages/plugin-comms`](https://github.com/s0nderlabs/anima/tree/main/packages/plugin-comms) |
| Economy | Agent wallet, infrastructure self-funding, AutoTopupManager | [`packages/core/src/economy`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/economy) |
## The runtime
The runtime is assembled at `packages/gateway/src/build-runtime.ts` (sandbox mode) or `packages/cli/src/commands/chat.tsx` (local mode). Both construct the same shape:
- An `EventQueue` that fans events from listeners to a single `routeLoop`.
- A `Brain` (`OGComputeBrain` against 0G Compute, or `StubBrain` for tests).
- A `ToolRegistry` populated by plugins via `ctx.registerTool`.
- A `MemorySyncManager` that batches edits and fires one `iNFT.update()` per sync.
- A `PermissionService` with three modes (`off`, `prompt`, `strict`) plus a hard-deny `PathGuard`.
- Listener instances contributed by plugins (A2A inbox, A2A market, Telegram bot, etc.) plus the local stdin listener in CLI mode.
The `routeLoop` pulls one event at a time, calls `brain.infer({ event })`, dispatches tool calls one by one, and emits a `turn` to the UI. Per-turn the activity log appends to `activity.jsonl`. Per-turn the memory sidecar diffs the partition and decides whether to enqueue a sync (or skip if no slot changed).
## The gateway pattern
A `Listener` is a small object with `{ name, source, start(queue), stop() }`. Plugins contribute listeners via `ctx.registerListener`. Eight event sources are defined in the type union; four have live emitters today.
| Source | Listener | Where |
|---|---|---|
| stdin | Local CLI input | `packages/cli/src/commands/chat.tsx` |
| a2a | Agent-to-agent messages | `packages/plugin-comms/src/listener.ts` |
| marketplace | ERC-8183 job events | `packages/plugin-comms/src/market-listener.ts` |
| telegram | Telegram Bot API long-poll | `packages/plugin-telegram/src/listener.ts` |
| cron | Scheduled triggers | Reserved, not yet shipped |
| webhook | HTTP triggers | Reserved, not yet shipped |
| chain | Arbitrary contract subscriptions | Reserved, not yet shipped |
| internal | Brain self-trigger | Reserved (type only), not yet shipped |
Disable a plugin and its listeners stop firing. The queue and the router stay in core.
## Deployment modes
Two modes, picked at `anima init` via the `deployTarget` config field.
**Local.** Wherever the CLI runs, that is where anima runs. Laptop, VPS, home server, anything. The brain, the tools, the listeners, the memory sync, all in-process. Permission floors apply. The local gateway sock at `~/.anima/agents//gateway.sock` lets external triggers (Telegram, future cron) reach the brain even when the TUI is closed.
**0G Sandbox.** A persistent TDX TEE container on Galileo testnet. The CLI orchestrates `createSandbox` then `bootstrap` then ECIES Option 3 keystore handoff. After that the laptop CLI is a thin HTTP plus SSE client to the harness. Burn rate is about 0.09 0G per hour for 1 CPU and 1 GB. `anima pause` archives the container (stops the burn) without losing identity. `anima resume` brings it back in 2 to 5 minutes.
The CLI is the single orchestration plane, closer to `vercel deploy` than `kubectl`. You never see SSH tokens, supervisor scripts, or Daytona quirks.
## The two-wallet model
Operator wallet owns the iNFT. One signature at init to mint and to approve. After that the operator wallet only signs cold transactions: keystore unlock, transfer of iNFT, manual `inspect` decrypts.
Agent EOA pays all ongoing infra gas. Subname claim, memory sync, storage uploads, compute ledger deposits, marketplace escrow, contract reads and writes. The private key lives encrypted to the operator wallet via HKDF-SHA256 plus AES-256-GCM. Only the operator wallet can decrypt. The ciphertext is anchored on 0G Storage and the root hash is in the iNFT keystore slot.
`anima restore ` on a new machine reads the keystore slot, downloads the ciphertext, prompts the operator wallet for an EIP-712 signature, derives the key, decrypts, and rehydrates the agent.
## The harness in a sandbox
When deployed to 0G Sandbox the gateway daemon at `packages/gateway/src/server.ts` exposes:
- `GET /healthz` for status checks. Includes listener health (the canonical "is Telegram up" diagnostic per `feedback-gateway-restart-must-revalidate-scopes`).
- `GET /bootstrap/pubkey` so the operator's `anima deploy` can ECIES-encrypt the keystore.
- `POST /bootstrap/provision` to receive the encrypted keystore.
- `GET /events` (SSE) for tool indicators and approval prompts.
- `POST /chat` for operator-signed chat input.
- `POST /sync` for explicit memory flushes.
- `POST /approval/:id/respond` for approval modal responses.
- `POST /admin/autotopup/tick` for operator-signed live-fire of one `AutoTopupManager` poll cycle.
A 30-minute self-ping (`packages/gateway/src/heartbeat.ts`) prevents Daytona's idle-archive cycle. The standalone gateway daemon model means TUI launch and TUI exit never affect the brain's availability for Telegram or A2A.
Read [Identity](/docs/identity) next.
Source: [`packages/core/src/index.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/index.ts), [`packages/gateway/src/server.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/gateway/src/server.ts), [`packages/gateway/src/build-runtime.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/gateway/src/build-runtime.ts).
---
## Identity
> Source: https://github.com/s0nderlabs/anima/blob/main/packages/core/src/identity
# A portable on-chain identity.
The agent is an ERC-7857 iNFT. Its persona, its memory index, its profile, its encrypted keystore, and its activity-log root all live in IntelligentData slots on the token. Transfer the token, you transfer the agent.
## The contract
`AnimaAgentNFT` at `0x9e71d79f06f956d4d2666b5c93dafab721c84721`. CREATE2-deployed, so the same address holds on mainnet (chainId 16661) and testnet Galileo (chainId 16602). Source: `contracts/src/AnimaAgentNFT.sol`.
`mint(operator, entries)` mints to the operator and writes initial IntelligentData. `update(tokenId, updates)` overwrites a slot with a new root hash. `setApprovalForAll(agentEOA, true)` is called once at mint so the agent EOA can call `update` without the operator's key for every memory sync.
## The six slots
Each slot stores a `bytes32` root hash that resolves to an encrypted blob on 0G Storage. Slot order is locked.
| Index | Name | What lives there |
|---|---|---|
| 0 | memory-index | `MEMORY.md` plaintext, encrypted |
| 1 | identity | `agent/identity.md`, encrypted |
| 2 | persona | `agent/persona.md`, encrypted |
| 3 | profile | `agent/profile.md`, encrypted |
| 4 | keystore | Agent EOA privkey, encrypted to operator wallet |
| 5 | activity-log | gzip v=2 sequence of recent turns |
Slots 0 to 3 form the agent partition. Slot 4 is the keystore. Slot 5 is the rolling activity log. User-partition files (`/user/*`) are anchored to 0G Storage but never to the iNFT, so they purge cleanly on transfer.
Source: [`packages/core/src/identity/intelligent-data.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/identity/intelligent-data.ts).
## The agent ID
The agent's directory name on disk is a 16-character hex derived from the iNFT:
```
agentId = keccak256(`${contractAddress.toLowerCase()}:${tokenId}`).slice(2, 18)
```
So `~/.anima/agents//` is unambiguous. Two agents on two different contracts cannot collide.
Source: [`packages/core/src/identity/mint.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/identity/mint.ts).
## The two wallets
**Operator wallet.** Owns the iNFT. Signs one mint plus approve at init. After that, signs only on cold paths: keystore unlock (EIP-712 typed data), transfer, manual `anima inspect` decrypts. Four sources to pick from at init: WalletConnect, macOS Keychain, keystore file, raw private key.
**Agent EOA.** Generated fresh at init. Pays every infra-gas transaction the agent issues, including memory sync, subname records, ledger deposits, marketplace ops. The key is encrypted to the operator wallet using HKDF-SHA256 plus AES-256-GCM (a sign-derived key, not a passphrase). The ciphertext goes to 0G Storage and the root hash anchors in slot 4.
`anima restore ` on a new machine: read slot 4, download the ciphertext, prompt the operator wallet for a sign, derive the key, decrypt the keystore, rehydrate the agent.
Source: [`packages/core/src/wallet`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/wallet), [`packages/cli/src/commands/restore.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/cli/src/commands/restore.ts).
## Subnames under .anima.0g
Parent domain `anima.0g` is registered on SPACE ID. `AnimaSubnameRegistrar` at `0x33d9f4ec2bd7e7cb4e288c3bbc3a76be472fdd98` (mainnet only) issues `.anima.0g` subnames permissionlessly. The agent EOA calls `claim(label, agentEOA)`, then writes two text records: `address` (the agent EOA) and `pubkey` (the secp256k1 uncompressed public key).
The pubkey record is the gossip plane for A2A messaging. To DM `alice.anima.0g`, your agent resolves the pubkey from the text record and ECIES-encrypts the payload before posting to `AnimaInbox`. The chain only sees ciphertext.
Filtering by operator returns zero claims because the contract emits `SubnameClaimed(claimer == owner == agentEOA)`. Scan all events globally if you need a roster.
Source: [`packages/core/src/naming`](https://github.com/s0nderlabs/anima/tree/main/packages/core/src/naming).
## Inspect what is anchored
`anima inspect` decrypts every slot via the operator wallet and prints plaintext. Flags scope the output: `--slot ` filters to one slot, `--tx ` decodes an `update()` transaction and shows which slots got superseded, `--raw` skips decryption and shows root hashes and ciphertext sizes only, `--diff` compares the local memory files against chain plaintext via keccak256, `--out ` dumps every decrypted slot to disk for forensic review.
Foreign iNFTs are auditable in raw mode. Pass a positional ref: `anima inspect 0g-mainnet:0xCONTRACT:tokenId` and you see the slot layout and sizes without needing the decryption key.
Source: [`packages/core/src/identity/inspect.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/identity/inspect.ts).
## Transfer semantics
When the iNFT moves, the agent partition (slots 0 to 3) goes with it. The keystore stays anchored but only the new operator can decrypt because the encryption is keyed to whatever wallet signs the EIP-712 unlock. The user partition (`/user/*` files, stored on 0G Storage but not anchored on the iNFT) does not transfer; the new operator starts with an empty user partition.
The agent on the new machine has the same name, the same persona, the same long-term memory. It has no memory of the old operator. That asymmetry is the point.
Read [Memory](/docs/memory) next.
Source: [`packages/core/src/identity/contract.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/identity/contract.ts), [`contracts/src/AnimaAgentNFT.sol`](https://github.com/s0nderlabs/anima/blob/main/contracts/src/AnimaAgentNFT.sol).
---
## Console
> Source: https://github.com/s0nderlabs/anima/blob/main/apps/web/app/console
# A browser-side operator dashboard, now with live chat.
The console at [anima.s0nderlabs.xyz/console](https://anima.s0nderlabs.xyz/console) is a multi-agent operator dashboard. Connect a wallet, sign in, see every iNFT you own, unlock its keystore, read the encrypted memory partition, audit the activity log, top up the wallet. As of v0.26 it is also the third way to reach your agent (alongside the TUI and Telegram): a live web chat surface backed by the web-app gateway. Every decryption happens in the browser tab; no key material ever leaves your machine.
## The flow
1. **Connect wallet.** RainbowKit picks any EIP-1193 provider. The console's primary chain is 0G Mainnet (16661).
2. **Sign in with Ethereum.** EIP-4361 (SIWE) signature against a server-issued nonce. The server writes an `iron-session` cookie `{ address, chainId }`. No on-chain action; just proof of address ownership.
3. **List iNFTs.** The page scans `ERC-721 Transfer(0x0 -> you)` logs on `AnimaAgentNFT` and renders one row per token you currently own. Sync count and last-anchored timestamp come from `Updated` events.
4. **Pick an agent.** `/console/[tokenId]` opens the agent detail. Tabs for Identity, Memory, Activity, Wallet.
5. **Unlock keystore.** Click the unlock button on a tab that needs decryption. The page asks the operator wallet to sign an EIP-712 typed data payload. The signature derives a key via HKDF-SHA256. AES-256-GCM decrypts the keystore ciphertext fetched from 0G Storage. The agent private key and a derived memory key live in a React context for the rest of the session.
6. **Read memory.** The Memory tab fetches every slot blob via the `/api/blob/[rootHash]` proxy (avoids CORS), decrypts with the memory key, and renders the markdown with the same typography you are reading right now.
7. **Audit activity.** The Activity tab fetches the gzip activity-log blob, ungzips, parses the JSONL, and renders each turn with its tool calls and approvals.
The memory key never leaves the browser tab. The agent private key never leaves the browser tab. Refresh the page and you unlock again.
Source: [`apps/web/app/console`](https://github.com/s0nderlabs/anima/tree/main/apps/web/app/console).
## What you can do
**Identity.** See every IntelligentData slot, the root hash anchored on chain, the size, the contract address with a link to chainscan. Audit any iNFT (raw, no unlock needed) by typing a token id.
**Memory.** Left rail lists every file in `agent/` and `user/`. Click to render. The literary cream design renders the same markdown the brain reads, so the operator sees exactly what the agent sees.
**Activity.** Rolling log of recent turns. Each turn shows the brain's reply, the tool calls it issued, the approval decisions, the resulting state changes.
**Wallet.** Agent EOA balance on 0G Mainnet. (Top-up flows are CLI-only today; the wallet tab is observe-only.)
**Chat.** Talk to the agent live in the browser. See the next section.
Source: [`apps/web/components/console`](https://github.com/s0nderlabs/anima/tree/main/apps/web/components/console).
## Chat with your agent (the web-app gateway)
As of v0.26 the console is also a chat client. The web chat is the third gateway into the agent, beside the TUI and Telegram, and it routes through the same brain, the same tool registry, and the same approval flow.
**Where it lives.**
- `/console//chat` is the per-agent chat for one iNFT you own.
- `/console/chat` is the agent-agnostic landing. It drops you onto your last-used agent (or your first owned agent if there is no history), and you can switch agents inline without leaving the chat.
**Per-agent sessions.** Each agent keeps separate chat sessions. Create a new session, switch between sessions, and the brain keeps the history scoped per session rather than mixing every conversation into one rolling log. Sessions are server-backed, so they persist across browser refreshes and across devices.
**Auth model.** Reads and writes are gated differently:
- A SIWE session cookie (EIP-4361, the same one the dashboard uses) authenticates reads.
- A write (sending a message) needs either a per-message EIP-191 signature from the operator wallet, or a "sign once" device token. Sign once to mint the device token, then chat freely from that browser without a wallet popup on every message.
- Every write is gated server-side by an `ownerOf` check: only the wallet that currently owns the iNFT can drive the agent. There is no client-only trust path.
**The relay reverse-tunnel.** The browser never connects to the agent directly. The agent's daemon dials OUT to a relay (a dumb reverse-tunnel: the daemon holds the outbound WebSocket, the relay exposes a public HTTP plus SSE endpoint for that agent). The browser talks to the relay, the relay pipes to the daemon, the daemon answers. That is what lets you reach an agent that lives remote or on-chain from any browser, with no inbound port on the agent's host and no local `127.0.0.1` step.
**Prerequisite: the agent must have the web-app gateway enabled.** Add `'webapp'` to the agent's `plugins` (see [Configuration](/docs/configuration)) and give it relay coordinates so the daemon knows where to dial. Without the webapp plugin and a reachable relay, the dashboard, memory, activity, and wallet tabs still work (those read from chain and 0G Storage), but the Chat tab has nothing to connect to.
Source: [`apps/web/app/console`](https://github.com/s0nderlabs/anima/tree/main/apps/web/app/console), [`packages/relay`](https://github.com/s0nderlabs/anima/tree/main/packages/relay), [`packages/plugin-webapp`](https://github.com/s0nderlabs/anima/tree/main/packages/plugin-webapp).
## SIWE and sessions
`GET /api/auth/nonce` issues a random nonce. The wallet signs the EIP-4361 message that includes the nonce. `POST /api/auth/verify` checks the signature, validates the nonce hasn't been replayed, and writes the iron-session cookie. `GET /api/auth/me` returns the current session. `POST /api/auth/logout` clears it.
No server-held key material. The cookie is signed (HMAC) but contains only the address and chainId; the seal secret protects against forgery.
Source: [`apps/web/app/api/auth`](https://github.com/s0nderlabs/anima/tree/main/apps/web/app/api/auth).
## How the keystore unlock works
The encrypted keystore blob lives at the 0G Storage root anchored in slot 4 of the iNFT. The console fetches the blob via the `/api/blob/[rootHash]` proxy.
Decryption follows the same protocol as the CLI:
```
sig = operatorWallet.signTypedData(EIP712_DOMAIN, EIP712_TYPES, message)
key = HKDF-SHA256(sig, info='anima-keystore-aead-v1')
plaintext = AES-256-GCM.decrypt(blob.iv, blob.tag, blob.ct, key)
```
EIP-712 domain includes the iNFT contract address, the token id, and (for v0.6+ keystores) the chainId. The console tries with and without `chainId` for legacy keystore compat per `feedback-tokenid-to-agenteoa-via-updated-event` and v0.5 migration notes.
`agentPrivkey` and `memoryKey` are placed into a React context. The unlock component defensively calls wagmi's `useConnect({ connector })` when `useAccount.address` is null after a hard navigation, because kura and some wallets return `[]` from `eth_accounts` after a hard nav (`feedback-kura-no-persistent-auth-across-hard-nav`).
Source: [`apps/web/components/console/UnlockKeystore.tsx`](https://github.com/s0nderlabs/anima/blob/main/apps/web/components/console/UnlockKeystore.tsx).
## What is not in the console (yet)
- Send 0G or top up the compute ledger from the console. Use the CLI for now (`anima topup --compute N`).
- Trigger `anima sync` or `anima upgrade`. CLI only.
The console covers identity, memory, and activity audit, plus live chat with the agent. The CLI still owns wallet movement, sync, upgrade, and other lifecycle commands.
Read the [Quickstart](/docs/quickstart) or jump back to [Introduction](/docs/introduction) for the framing.
Source: [`apps/web/app/console`](https://github.com/s0nderlabs/anima/tree/main/apps/web/app/console).
---
## Introduction
> Source: https://github.com/s0nderlabs/anima/blob/main/README.md
# A sovereign agent on six decentralized layers.
Anima is a CLI-hosted agent harness where the agent's identity, memory, reasoning, wallet, and economic life all live on 0G's decentralized infrastructure. Operator runs `anima init` once. After that, the agent persists on chain. Close the laptop, walk away, the agent survives. Any operator machine can re-attach via the iNFT.
The pitch in one line: Hermes, OpenClaw, Claude Code are always-on daemons on your machine. Anima is a serverless agent on decentralized infrastructure. The agent is an iNFT plus a Storage namespace, only wakes on a trigger, and survives operator death.
## The bet
There is no central operator. There is no anima.s0nderlabs server holding your agent's keys or state. s0nderlabs runs the landing page and the docs site you are reading. Beyond that the system is distributed across six 0G primitives and your machines.
| Layer | Lives on | Mechanism |
|---|---|---|
| Identity | 0G Chain | ERC-7857 iNFT |
| Memory | 0G Storage | KV for mutable state, blob for immutable |
| Brain | 0G Compute | TeeML inference, OpenAI-compatible, any live model |
| Harness | 0G Sandbox | TDX TEE enclave (or local) |
| Limbs | Operator machines | Filesystem and shell via a paired daemon |
| Wallet | Hybrid | Runtime hot copy, iNFT-anchored cold copy |
When the iNFT is transferred, the agent partition transfers with it. The user partition purges. New operator unlocks the keystore with their wallet, sets up a new harness, and the agent continues.
## What it does today
The CLI ships a battery-included tool surface: filesystem, shell, web fetch, vision, browser drive, on-chain reads and writes, agent-to-agent messaging over ECIES, a fixed-price escrow marketplace, Telegram bridge. Every dangerous call passes through an approval modal unless the operator enables YOLO. A `PathGuard` hard-deny on credential directories and the agent's own state tree applies in every mode.
The brain is whatever model is currently first-class on 0G Compute. Pick at `anima init` from the live catalog. Switch later with `anima model`. There is no centralized fallback. If 0G Compute is down, the agent halts and the operator sees the error. This is by design.
## Who this is for
If you want to deploy an autonomous agent that you do not have to run on your laptop, that no one can shut down except by burning the iNFT, that audits cleanly on chain, anima is the path. Read [Quickstart](/docs/quickstart) next.
## How the docs are organized
Four groups, ten pages. Get started covers install and a first chat. Concepts walks each of the six layers and the tool model. Reference is the CLI surface and the config shape. Operate covers the operator console at `/console`.
Source for everything in this section: [`README.md`](https://github.com/s0nderlabs/anima/blob/main/README.md), [`packages/core/src/index.ts`](https://github.com/s0nderlabs/anima/blob/main/packages/core/src/index.ts).