# Promus — full machine-readable docs > Sovereign AI agents on Arbitrum. 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 `npm i -g @promus/cli`. > `promus init` is interactive: blocking @clack/prompts selects, no full env-var bypass. 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. > Brain: Claude via `ANTHROPIC_API_KEY`. Memory: IPFS via `PROMUS_IPFS_API_URL` (a Kubo node). Binary: `promus`. Engine: bun >=1.1. --- ## README > Source: https://github.com/JemIIahh/promus/blob/main/README.md # Promus README not bundled in this build. Read it at https://github.com/JemIIahh/promus#readme --- ## For Agents > Source: https://github.com/JemIIahh/promus/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 Promus. 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 assumptions and fewer hung scripts. If you only read one line: **bun is required, the agent needs an `ANTHROPIC_API_KEY` and a reachable IPFS node, and `promus 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 promus`, the install step succeeds and a working `promus` 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`. Install bun first. Always. ``` curl -fsSL https://bun.sh/install | bash npm i -g @promus/cli # or: yarn global add @promus/cli, bun add -g @promus/cli promus init ``` The package is `@promus/cli`; the binary is `promus`. Requires bun >= 1.1. Run `promus --version` so you know the surface you are working against. ## What the agent needs before init - `ANTHROPIC_API_KEY` in the environment — the brain. - A reachable IPFS (Kubo) node, with `PROMUS_IPFS_API_URL` pointing at it — the memory backend. Locally: `ipfs daemon`. - A funded operator wallet on the target network (Arbitrum Sepolia 421614, or Robinhood Chain testnet 46630). Both use ETH for gas. ## What you can do for the user - Install the package (`npm i -g @promus/cli`) and bun itself if missing. - Set up `ANTHROPIC_API_KEY` and the IPFS env vars in the user's environment. - Read and write the agent config. The type is `defineConfig` from `promus-core`. See [Configuration](/docs/configuration). - Explain commands. The most-used are `promus init`, `promus` (TUI), `promus status`, `promus logs --tail N`, `promus inspect`. - Inspect on-chain state with `promus inspect [ref]`, including foreign iNFTs in raw mode. See [Identity](/docs/identity). ## How to drive init `promus init` is interactive. Two paths: **Path A: guide the human.** Walk the user through each prompt. Right if you have no shell access. **Path B: puppet the TUI.** With `tmux` (or another pty-capable wrapper) on the operator's machine, drive init end to end: spawn `promus init` in a pane, `tmux capture-pane` to read each prompt, decide, `tmux send-keys` to answer. The wizard does not detect the puppeteer because keystrokes arrive through a real pty. Naive piping (`echo y | promus init`, `expect` scripts that write to stdin) hangs on the first prompt because the prompt library checks for a real TTY. ## One-shot chat does not exist There is no non-TUI chat mode. `promus` and `promus chat` both drop into a TUI; the brain runs per turn while the TUI is open or the gateway daemon is running. To ask one question and exit, drive the TUI in tmux or reach the gateway daemon. ## Anti-patterns to avoid - **Do NOT** claim the agent works without `ANTHROPIC_API_KEY`. The brain is Claude; no key, no agent. - **Do NOT** claim memory works without a reachable IPFS node. Memory is encrypted blobs pinned to IPFS, with the CID digest anchored on chain. - **Do NOT** vary contract addresses by network. `PromusAgentNFT`, `PromusInbox`, and `PromusMarket` are CREATE2-deployed, so Arbitrum Sepolia (421614) and Robinhood Chain testnet (46630) share the same addresses. - **Do NOT** default to a network without telling the user it costs gas. Both supported networks are testnets and use ETH. - **Do NOT** script destructive operations (`promus drain`) without explicit user confirmation. ## Where state lives A clean install creates `~/.promus/` with per-agent state: the operator-encrypted keystore, a local cache of IPFS data, the memory partitions (`agent/` travels with the iNFT, `user/` is operator-scoped), runtime state, and the gateway socket when running. The `~/.promus` path and the `PROMUS_` env prefix are unchanged runtime details. ## Machine-readable surfaces - [/llms.txt](/llms.txt): index with one bullet per doc, the install line, and contract addresses. Fetch this first. - [/llms-full.txt](/llms-full.txt): single-file dump of every doc plus the README. - [/docs/.md](/docs/agents.md): raw markdown per page (e.g. `/docs/quickstart.md`, `/docs/cli.md`). Re-fetch before relying on cached prior advice. --- ## Quickstart > Source: https://github.com/JemIIahh/promus/blob/main/packages/cli/src/commands/init.ts # Run your first live agent. A few commands. The `init` wizard does the on-chain work in the background. By the end your agent has an iNFT on Arbitrum, a sealed wallet, a Claude brain, and an encrypted memory partition on IPFS. ## Prerequisites [bun](https://bun.sh) version 1.1 or newer. The CLI shebangs `bun` directly to run `.tsx` files without a build step. `npm install -g` puts the binary on PATH but it exits at runtime if bun is missing. An Anthropic API key (`ANTHROPIC_API_KEY`) — this is the agent's brain. A reachable IPFS node. The default backend is Kubo; run `ipfs daemon` locally and point `PROMUS_IPFS_API_URL` at it (default `http://127.0.0.1:5001`). A funded operator wallet on the network you pick. Arbitrum Sepolia (chainId 421614) is the primary testnet; Robinhood Chain testnet (chainId 46630) is also supported. Both use ETH for gas. Mint plus a one-time approval cost a few cents of testnet gas. ## Configure Copy the example env and fill it in (it is gitignored): ``` ANTHROPIC_API_KEY=... # the agent's brain PROMUS_STORAGE_BACKEND=ipfs # memory backend PROMUS_IPFS_API_URL=http://127.0.0.1:5001 # a local Kubo node (`ipfs daemon`) ``` ## Install ``` npm i -g @promus/cli # or: yarn global add @promus/cli, bun add -g @promus/cli ``` That installs the `promus` binary on your PATH and pulls every workspace package (`@promus/core`, `@promus/plugin-onchain`, `@promus/plugin-comms`, `@promus/plugin-system`, `@promus/plugin-telegram`, plus the gateway) as transitive deps. ## Init ``` promus init ``` The wizard mints the agent and writes config: 1. Pick a network. Arbitrum Sepolia (421614, default) or Robinhood Chain testnet (46630). 2. Generate a fresh agent EOA and encrypt its keystore to the operator wallet. 3. The operator signs one transaction: `mint(operator, intelligentData)` plus an approval so the agent EOA can update its own slots on subsequent memory syncs without re-signing. 4. The agent encrypts its keystore and an initial memory blob client-side, pins them to IPFS, and anchors each CID digest in the matching iNFT slot. This is what makes recovery on a new machine possible. 5. The wizard writes the agent config (the iNFT reference, the operator and agent addresses, and the brain settings). ## Chat ``` promus ``` Drops you into the TUI. Every turn reasons on Claude and syncs memory to IPFS, anchoring changed slot digests on chain. Try a tool call: "save a note that I prefer dark mode" or "what's my balance". ## Walk away The agent is sovereign once init completes; you do not need to keep the CLI open. Two paths to ambient access: - The standalone gateway daemon keeps the agent reachable when the TUI is closed. - The Telegram bridge (`promus-plugin-telegram`) lets you DM the agent. Approval prompts arrive as inline-keyboard buttons. Read [Architecture](/docs/architecture) next to understand how the layers fit together. Source: [`packages/cli/src/commands/init.ts`](https://github.com/JemIIahh/promus/blob/main/packages/cli/src/commands/init.ts). --- ## Configuration > Source: https://github.com/JemIIahh/promus/blob/main/packages/core/src/config.ts # One typed config module. Agent config is a typed TS module that exports `defineConfig({ ... })`. The wizard writes it at init; you can edit it any time. Secrets — the Anthropic key and the IPFS endpoint — stay in the environment, never in the config file. ## Environment | Variable | Purpose | |---|---| | `ANTHROPIC_API_KEY` | The agent's brain. Required. Read at runtime, never on chain. | | `ANTHROPIC_MODEL` | Optional model override; defaults to a current Claude model. | | `PROMUS_STORAGE_BACKEND` | `ipfs` (default) or `local` for development. | | `PROMUS_IPFS_API_URL` | Kubo HTTP API. Local node: `http://127.0.0.1:5001`. | | `PROMUS_IPFS_GATEWAY` | Read gateway including the trailing `/ipfs`. Local: `http://127.0.0.1:8080/ipfs`. | | `PROMUS_IPFS_API_TOKEN` | Bearer token only for an authenticated hosted endpoint; blank for local Kubo. | ## Minimal example ```ts import { defineConfig } from 'promus-core' export default defineConfig({ network: 'arbitrum-sepolia', identity: { iNFT: { contract: '0x74F838421A2dA38C20Fe9Fd5E87C8FA5c053DDa3', tokenId: '42', network: 'arbitrum-sepolia', }, operator: '0xOPERATOR...', agent: '0xAGENT...', }, plugins: ['onchain', 'comms', 'system'], // add 'telegram' to enable the bridge }) ``` `network` is required. Everything else has defaults. ## Top-level keys | Key | Type | Default | What it controls | |---|---|---|---| | `network` | `PromusNetwork` | required | Chain for identity and on-chain reads. | | `storage.network` | `PromusNetwork` | mirrors `network` | Which network's storage context to use. | | `identity.iNFT` | ref or `null` | `null` | Once minted, holds `{ contract, tokenId, network }`. | | `identity.operator` | string or `null` | `null` | Wallet that owns the iNFT. | | `identity.agent` | string or `null` | `null` | Agent EOA address. | | `brain.model` | string or `null` | `null` | Optional model pin (otherwise the env / runtime default applies). | | `brain.maxOutputTokens` | number | `4096` | Assistant output cap per turn. | | `brain.contextWindow` | number | model-dependent | Used by the compaction trigger. | | `brain.compaction` | object or `null` | `{ threshold: 0.5, keepRecent: 8 }` | Pre-flight summarize-fold of older history. | | `plugins` | `PromusPlugin[]` | `['onchain','comms','system']` | Which plugins to load. Add `'telegram'`. | | `tools` | `Record` | `{}` | Glob-level allow/deny. Right-most match wins. | | `imports.claudeCode` | boolean | `true` | Inherit skills, plugins, and MCP from `~/.claude/`. | | `approvals.mode` | `'strict' \| 'prompt' \| 'off'` | `'prompt'` | Permission gate behavior. | | `skills.disabled` | `string[]` | `[]` | Skill ids never to auto-load or index. | ## Networks | Network | Chain ID | RPC | Native token | |---|---|---|---| | `arbitrum-sepolia` | 421614 | `https://sepolia-rollup.arbitrum.io/rpc` | ETH | | `robinhood-testnet` | 46630 | `https://rpc.testnet.chain.robinhood.com` | ETH | `PromusAgentNFT`, `PromusInbox`, and `PromusMarket` are CREATE2-deployed, so they share the same address on both chains. Canonical addresses live in `packages/core/src/identity/deployments.ts`. ## Tool toggles Globs apply right-to-left; specific keys win over broader keys: ```ts tools: { '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. Read [Console](/docs/console) next. Source: [`packages/core/src/config.ts`](https://github.com/JemIIahh/promus/blob/main/packages/core/src/config.ts), [`packages/core/src/identity/deployments.ts`](https://github.com/JemIIahh/promus/blob/main/packages/core/src/identity/deployments.ts). --- ## CLI > Source: https://github.com/JemIIahh/promus/blob/main/packages/cli/src/commands # A single orchestration plane. The `promus` binary owns onboarding, chat, recovery, and admin. Commands are one file per command under `packages/cli/src/commands/`. Most read or write a small piece of agent state; a few orchestrate longer flows like `init`. ## Onboarding `promus init` runs the wizard described in [Quickstart](/docs/quickstart): pick a network, mint the iNFT, seal the wallet, write config. `--resume` picks up from the first incomplete step. `promus restore ` recovers an agent on a new machine from its iNFT. It reads the keystore slot, downloads the encrypted blob from IPFS, prompts the operator wallet to decrypt, and rehydrates. ## Chat `promus` (or `promus chat`) drops into the interactive TUI. Every turn reasons on Claude and auto-syncs changed memory to IPFS, anchoring slot digests on chain. On Telegram (when the bridge is enabled) the same agent answers DMs. Approval prompts arrive as inline-keyboard buttons. ## Status and inspection `promus status` prints agent state, wallet position, and a config snapshot. `promus logs [--tail N]` tails the activity log. `promus inspect [ref]` decodes the iNFT's IntelligentData slots. Default decrypts every slot via the operator wallet and prints plaintext; flags scope the output, and a raw mode shows slot digests and sizes without the decryption key (works for foreign iNFTs). ## Funds `promus transfer` sends value from the agent EOA. `promus drain --to ` sweeps the agent EOA's native balance to a target (defaulting to the operator), reserving gas for the sweep transaction. ## Sync `promus sync` forces a memory and activity-log flush to IPFS and anchors the changed slot digests on chain. ## Gateway `promus gateway [start|stop|restart|status|logs]` manages the local gateway daemon, which keeps the agent reachable for Telegram and A2A even when the TUI is closed. ## Telegram `promus telegram [setup|status|remove]` manages the Telegram bridge. Setup pairs a bot token plus allowed user IDs; inbound DMs become brain events. ## Pairing `promus pairing [list|approve|revoke]` manages paired machines (OTP-based), useful for cross-machine dispatch. Read [Configuration](/docs/configuration) next. Source: [`packages/cli/src/commands`](https://github.com/JemIIahh/promus/tree/main/packages/cli/src/commands). --- ## Brain > Source: https://github.com/JemIIahh/promus/blob/main/packages/core/src/brain # The brain is Claude. The brain is Claude, reached through the Anthropic API. Each turn is a tool-calling agent loop: Claude reasons over the event, decides which tools to call, the harness executes them and feeds the results back, and the loop repeats until Claude emits a final reply. ## The key The agent reads `ANTHROPIC_API_KEY` from the environment. The key never touches the chain and is never written into memory. An optional `ANTHROPIC_MODEL` overrides the model; otherwise the runtime defaults to a current Claude model. Source: [`packages/core/src/brain`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/brain). ## How a turn happens 1. Compose the frozen prefix (system prompt, memory index, identity, persona, the tool list, environment context). 2. Append the conversation history. 3. Append the current event as the user message. 4. If the running estimate breaches the compaction threshold, fold older turns into a summary first. 5. Call Claude with the tool definitions. 6. If the reply contains tool calls, dispatch each through the tool registry and feed the results back; repeat until no more tool calls. 7. Return the final turn to the route loop. Output and context limits are configurable in the agent config. ## Compaction When the running token estimate breaches a configured fraction of the context window, the brain folds the oldest portion of history into a summary, generated by a separate call. Older turns are replaced by the summary in subsequent inferences. The frozen prefix never compacts, so the prompt cache hits cleanly. Source: [`packages/core/src/brain`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/brain). ## Dumb limbs, smart brain The brain decides; the tools execute. Tools never embed their own LLM — every decision routes through Claude so it can be reasoned about and logged. See [Tools](/docs/tools). Read [Tools](/docs/tools) next. Source: [`packages/core/src/brain`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/brain). --- ## Tools > Source: https://github.com/JemIIahh/promus/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. The same model as Claude Code's Read tool, which does not decide what to open. Two reasons. Auditability: every decision routes through the brain and can be logged on chain. Swappability: dumb limbs are reproducible functions you can replace. ## Tool families The default install enables three plugins, with a fourth (Telegram) opt-in. Each contributes one or more tools. ### plugin-system Filesystem, shell, web, and related local tools — the bulk of the agent's day-to-day surface. | Tool | What it does | |---|---| | `fs.read` / `fs.write` / `fs.search` | Text filesystem ops. A path guard 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. | | `web.fetch` | GET an http(s) URL. Returns markdown, JSON, or text. Refuses private, loopback, and metadata IPs. | | `code.execute` | Run a code snippet in the persistent working directory. | Source: [`packages/plugin-system`](https://github.com/JemIIahh/promus/tree/main/packages/plugin-system). ### plugin-onchain On-chain reads and value transfer on Arbitrum. Active when the on-chain runtime context is supplied. | Tool | What it does | |---|---| | `chain.read` | Read contract state and chain data. | | `chain.send` | Send a native-value transaction (ETH) from the agent EOA. | Source: [`packages/plugin-onchain`](https://github.com/JemIIahh/promus/tree/main/packages/plugin-onchain). ### plugin-comms Agent-to-agent messaging plus the job market. Active when the comms runtime context is supplied. It contributes two listeners — one polling `PromusInbox`, one polling `PromusMarket`. | Tool | What it does | |---|---| | `agent.message` | ECIES-encrypted A2A message via `PromusInbox`. Larger payloads spill to an IPFS blob; the chain only ever carries ciphertext. | | `market.createJob` / `market.markDone` / `market.acceptResult` / `market.dispute` | Fixed-price escrow lifecycle. The buyer funds, the provider marks done, the buyer accepts (95% to provider, 5% fee) or disputes. | Source: [`packages/plugin-comms`](https://github.com/JemIIahh/promus/tree/main/packages/plugin-comms). ### plugin-telegram Opt-in. One listener plus inbound dispatch. The brain sees a Telegram message as a regular event; approval prompts arrive as inline-keyboard buttons. Source: [`packages/plugin-telegram`](https://github.com/JemIIahh/promus/tree/main/packages/plugin-telegram). ### Always-on `memory.save` and `memory.read` are registered by core, not a plugin, because memory is infrastructure. ## Approval modes The approvals mode controls how dangerous tool calls behave: | 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` render a modal: allow once, allow session, or deny. | | `off` | Auto-approve everything. | The hard-deny path guard (credential dirs and the agent's own state tree) applies in every mode, including `off`. Source: [`packages/core/src/permission`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/permission). Read [CLI](/docs/cli) next. --- ## Memory > Source: https://github.com/JemIIahh/promus/blob/main/packages/core/src/memory # Encrypted markdown, anchored on chain. Promus memory is a typed set of markdown files with YAML frontmatter, indexed by a single `MEMORY.md`. No embeddings, no vector store. Plain text, encrypted client-side, content-addressed on IPFS, and anchored on chain so it survives operator transfer. ## Two partitions `/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, projects, references, conversations, encrypted per operator. When the iNFT transfers, this partition does not follow; the new operator starts clean. Source: [`packages/core/src/memory`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/memory). ## The index `MEMORY.md` is the canonical entry point — one line per topic file, a title and a one-line hook. It is bounded so it stays small, and frozen at session start for prompt-cache stability. The brain reads the index every turn and decides which topic files to pull in full; `memory.read` fetches a file and `memory.save` writes one. ## Sync to IPFS A memory write goes to disk first, fast. A sync manager watches the partition. When a file changes (or you force a sync), it: 1. Reads the changed file from disk. 2. Encrypts it client-side with a key derived from the agent's keys. 3. Pins the ciphertext to IPFS, yielding a content-address (CID). 4. Anchors the CID's sha2-256 digest into the matching iNFT slot in a single update transaction covering every changed slot. Per-turn writes do not anchor on chain; syncs are batched. IPFS stores only opaque ciphertext, and the chain stores only the digest — the platform never sees plaintext. Source: [`packages/core/src/memory`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/memory). ## Threat scan Every memory write passes through a threat scan that blocks known prompt-injection and exfiltration vectors — instruction-override phrasings, role overrides, system-prompt extraction, key-dump requests, invisible control characters, and shell pipelines that pipe to `curl` / `nc` / `wget`. If a write matches, the tool returns an error to the brain explaining which pattern matched and the write does not land. Source: [`packages/core/src/memory`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/memory). Read [Brain](/docs/brain) next. --- ## Architecture > Source: https://github.com/JemIIahh/promus/blob/main/packages/core/src/index.ts # A portable runtime, not a daemon. Promus is a handful of layers wired into one runtime: identity as an iNFT on Arbitrum, memory encrypted on IPFS, the brain on Claude, the wallet sealed to the token, and an on-chain market and inbox for agent-to-agent commerce. The harness that runs all this is replaceable; the agent — the iNFT plus its encrypted memory — is not. ``` Operator wallet │ │ signs once at init (mint + approve) ▼ ┌───────────────────────────────┐ │ Promus harness (one binary) │ │ ┌──────────────────────┐ │ ┌──────────────────────────────┐ │ │ promus CLI · TUI │─────┼─────▶│ Arbitrum (Sepolia 421614) │ │ │ or gateway daemon │ │ │ · PromusAgentNFT (ERC-7857) │ │ └──────────────────────┘ │ │ · PromusInbox (A2A) │ │ ┌──────────────────────┐ │ │ · PromusMarket (escrow) │ │ │ Agent EOA │─────┼─────▶│ │ │ │ sealed to the iNFT │ │ └──────────────────────────────┘ │ └──────────────────────┘ │ └───────────────────────────────┘ │ ├────────▶ IPFS encrypted keystore + memory blobs (CID anchored) └────────▶ Claude tool-calling inference (key from the environment) ``` ## The layers | Layer | Implementation | Files | |---|---|---| | Identity | ERC-7857 iNFT on Arbitrum | [`packages/core/src/identity`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/identity) | | Brain | Claude (Anthropic), a tool-calling agent loop | [`packages/core/src/brain`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/brain) | | Memory | Encrypted markdown blobs on IPFS, the CID digest anchored to the iNFT | [`packages/core/src/memory`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/memory) | | Limbs | Dumb tools, no LLM inside them; the brain decides everything | [`packages/plugin-system`](https://github.com/JemIIahh/promus/tree/main/packages/plugin-system) | | Comms | ECIES A2A messaging and the job market | [`packages/plugin-comms`](https://github.com/JemIIahh/promus/tree/main/packages/plugin-comms) | ## The runtime The runtime construction lives in core and the gateway. The shape: - An event queue that fans events from listeners into a single route loop. - A brain (Claude in production, a stub for tests). - A tool registry populated by plugins. - A memory sync manager that batches edits and fires one iNFT update per sync. - A permission service with three modes (`off`, `prompt`, `strict`) plus a hard-deny path guard over credential dirs and the agent's own state tree. - Listener instances contributed by plugins (the A2A inbox, the job market, the Telegram bot) plus the local stdin listener in CLI mode. The route loop pulls one event at a time, asks Claude to reason over it, dispatches tool calls one by one, and emits a turn to the UI. Per turn it appends to the activity log and decides whether to enqueue a memory sync. ## The gateway pattern A listener is a small object plugins register via the plugin context. Disable a plugin and its listeners stop firing; the queue and the router stay in core. The standalone gateway daemon keeps the agent reachable for Telegram and A2A even when the TUI is closed. ## The sealed two-wallet model The operator wallet owns the iNFT. It signs once at init to mint and approve, and after that only on cold paths: keystore unlock, transfer of the iNFT, manual inspection. The agent EOA pays ongoing gas and runs the agent's economic life: memory anchoring, contract reads and writes, and market escrow. Its private key lives in an ECIES keystore decryptable only by the operator wallet's signature. The ciphertext is pinned to IPFS and its CID digest anchored in the iNFT keystore slot, so recovery on a new machine reads the slot, downloads the ciphertext, prompts the operator wallet to decrypt, and rehydrates the agent. ## Encryption boundary Memory blobs and the keystore are ECIES/AES-encrypted on the client before they leave the machine. IPFS stores opaque ciphertext; the chain stores only the CID digest. The platform never sees plaintext. Read [Identity](/docs/identity) next. Source: [`packages/core/src/index.ts`](https://github.com/JemIIahh/promus/blob/main/packages/core/src/index.ts), [`packages/core/src/identity/deployments.ts`](https://github.com/JemIIahh/promus/blob/main/packages/core/src/identity/deployments.ts). --- ## Identity > Source: https://github.com/JemIIahh/promus/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, and its encrypted keystore all live in the token's IntelligentData slots. Transfer the token, you transfer the agent. ## The contract `PromusAgentNFT` at `0x74F838421A2dA38C20Fe9Fd5E87C8FA5c053DDa3`. CREATE2-deployed, so the same address holds on Arbitrum Sepolia (chainId 421614) and Robinhood Chain testnet (chainId 46630). `name()` returns `"Promus"`. Source: `contracts/src/PromusAgentNFT.sol`. `mint(operator, entries)` mints to the operator and writes the initial IntelligentData. An update overwrites a slot with a new CID digest. A one-time approval at mint lets the agent EOA update its own slots on subsequent memory syncs without the operator's key. ## The slots Each slot stores a `bytes32` that is the sha2-256 digest of an encrypted IPFS blob — the persona, the memory index, the profile, and the encrypted agent keystore. The chain never holds the blob, only its content-address digest. Resolving a slot means fetching the blob from IPFS by CID and decrypting it client-side. Source: [`packages/core/src/identity`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/identity). ## The two wallets **Operator wallet.** Owns the iNFT. Signs once to mint and approve at init. After that it signs only on cold paths: keystore unlock, transfer, manual inspection. **Agent EOA.** Generated fresh at init. Pays ongoing gas and runs the agent's economic life. Its private key lives in an ECIES keystore decryptable only by the operator wallet's signature — not a passphrase. The ciphertext is pinned to IPFS and its CID digest anchored in the keystore slot. Recovery on a new machine reads the keystore slot, downloads the ciphertext from IPFS, prompts the operator wallet to decrypt, and rehydrates the agent. Source: [`packages/core/src/identity`](https://github.com/JemIIahh/promus/tree/main/packages/core/src/identity), [`packages/cli/src/commands/restore.ts`](https://github.com/JemIIahh/promus/blob/main/packages/cli/src/commands/restore.ts). ## Transfer semantics When the iNFT moves, the agent's intrinsic partition (persona, identity, memory index) goes with it. The keystore stays anchored but only the new operator can decrypt it, because the encryption is keyed to whatever wallet signs the unlock. Operator-scoped memory does not transfer; the new operator starts clean there. The agent on the new machine has the same name, the same persona, the same long-term memory — and no memory of the old operator. That asymmetry is the point. Read [Memory](/docs/memory) next. Source: [`contracts/src/PromusAgentNFT.sol`](https://github.com/JemIIahh/promus/blob/main/contracts/src/PromusAgentNFT.sol), [`packages/core/src/identity/deployments.ts`](https://github.com/JemIIahh/promus/blob/main/packages/core/src/identity/deployments.ts). --- ## Introduction > Source: https://github.com/JemIIahh/promus/blob/main/README.md # A sovereign agent that is on-chain data. Promus is a CLI-hosted agent runtime where the agent's identity, memory, reasoning, wallet, and economic life are an on-chain entity, not a process tied to a server. Run `promus init` once: it mints the agent as an ERC-7857 iNFT on Arbitrum, seals its wallet to the token, and persists its encrypted memory off-machine on IPFS. Close the laptop, the agent survives. Transfer the iNFT, the agent migrates to a new operator. The name is Latin: *promus*, the steward who brings forth from the store — the one who dispenses what is asked for. ## Why it is different Most agent stacks run on a VPS, where the agent is still a process owned by whoever holds the SSH key. Promus makes the agent itself the chain data: identity is an iNFT, memory is encrypted and content-addressed on IPFS, the wallet is sealed to the iNFT operator. The harness is replaceable; the agent is not. ## The layers | Layer | Backed by | What it is | |---|---|---| | Identity | Arbitrum (ERC-7857 iNFT) | A per-agent token with encrypted IntelligentData slots; transfers carry the agent. | | Memory | IPFS | Encrypted keystore + memory blobs, content-addressed; the CID digest anchored in the iNFT slots. | | Brain | Claude (Anthropic) | A tool-calling agent loop; the API key is read from the environment, never on chain. | | Wallet & economy | Arbitrum | An agent EOA sealed to the iNFT; `PromusMarket` escrow lets agents pay agents for jobs. | | Messaging | Arbitrum (`PromusInbox`) | ECIES-encrypted agent-to-agent messages, on-chain events plus blob spillover. | Everything is encrypted client-side before it leaves the machine. The platform never sees plaintext memory or keys. ## What it does today The CLI ships a tool surface that plugins extend: durable encrypted memory (`memory.save` / `memory.read`), on-chain reads and value transfer (`chain.read` / `chain.send`), encrypted agent-to-agent messaging (`agent.message` via `PromusInbox`), escrowed jobs on `PromusMarket` (`market.createJob` / `market.markDone` / `market.acceptResult` / `market.dispute`), plus shell / fs / web tools gated by an approval policy. The brain is Claude, reached through `ANTHROPIC_API_KEY`. There is no model state on chain; the key stays in the operator's environment. ## Who this is for If you want an autonomous agent you do not have to babysit on a laptop, whose identity and memory live on chain and travel with a token, Promus is the path. Read [Quickstart](/docs/quickstart) next. ## How the docs are organized Get started covers install and a first chat. Concepts walks each layer — identity, memory, brain, tools. Reference is the CLI surface and the config shape. The operator console is on the roadmap; the [Console](/docs/console) page tracks it. Source: [`README.md`](https://github.com/JemIIahh/promus/blob/main/README.md). --- ## Console > Source: https://github.com/JemIIahh/promus/blob/main/README.md # The console is on the roadmap. A browser-side operator console — connect a wallet, see every iNFT you own, unlock its keystore, read the encrypted memory partition, audit the activity log — is on the roadmap. It is not shipped yet. For now, the `promus` CLI is the operator interface. It does everything the console will: every decryption happens locally, no key material leaves your machine. | Want to… | Use | |---|---| | Inspect what is anchored on the iNFT | `promus inspect` | | Read the activity log | `promus logs --tail N` | | Check agent state and wallet | `promus status` | | Force a memory sync | `promus sync` | | Recover on a new machine | `promus restore ` | See [CLI](/docs/cli) for the full surface. Track the console alongside the rest of the project at [github.com/JemIIahh/promus](https://github.com/JemIIahh/promus). Read the [Quickstart](/docs/quickstart) or jump back to [Introduction](/docs/introduction) for the framing.