pai
README.md
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset=".github/assets/logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset=".github/assets/logo-light.svg">
<img alt="Phantom Logo" src=".github/assets/logo-dark.svg" width="56" height="62">
</picture>
</p>
<h1 align="center">pai</h1>
<p align="center">
<strong>Keys, money, and subagent controls for AI agents</strong><br>
Built by <a href="https://phantom.codes">Phantom AI</a>
</p>
<p align="center">
<a href="https://github.com/connortessaro/pai/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/connortessaro/pai/ci.yml?branch=main&label=CI&color=161b22&labelColor=0d1117" alt="CI"></a>
<a href="package.json"><img src="https://img.shields.io/badge/version-0.3.0-161b22?labelColor=0d1117" alt="Version 0.3.0"></a>
<a href="package.json"><img src="https://img.shields.io/badge/node-%3E%3D24-161b22?labelColor=0d1117" alt="Node >= 24"></a>
<a href="docs/reference.md#mcp-tools"><img src="https://img.shields.io/badge/MCP-16_tools-161b22?labelColor=0d1117" alt="MCP Compatible"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-161b22?labelColor=0d1117" alt="MIT License"></a>
</p>
<p align="center">
<a href="#install">Install</a> •
<a href="#quickstart">Quickstart</a> •
<a href="#how-it-works">How It Works</a> •
<a href="#features">Features</a> •
<a href="#commands">Commands</a> •
<a href="#use-with-an-mcp-client">MCP Server</a> •
<a href="docs/reference.md">API Reference</a>
</p>
---
`pai` runs on your machine and manages a [Phantom AI](https://phantom.codes) key.
Your agent can hand each subagent a child key with its own spending limit, lifetime, and rate cap.
It also sets plans and model routing, pays for credit from an agent wallet, and checks which model answered a call.
`pi`, `Claude Code`, `Codex`, and `Cursor` drive it through a skill, and any MCP client can use `pai mcp`.
The CLI is MIT licensed. The Phantom AI API it calls is a hosted service.
## How It Works
```text
┌────────────────────────┐
│ Main Phantom Key │
│ (prepaid, no id) │
└───────────┬────────────┘
│
┌───────────────────────┼───────────────────────┐
│ pai child │ pai child │ pai wallet
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Researcher │ │ Code Agent │ │ Agent Wallet │
│ Claude Code │ │ Cursor │ │ (Solana) │
│ $0.50 limit │ │ $1.00 limit │ │ auto-topup │
│ 6 hour TTL │ │ $0.10 / min │ │ below $1.00 │
└──────┬───────┘ └──────┬───────┘ └──────────────┘
│ │
└───────────┬───────────┘
▼
┌───────────────────────┐
│ Phantom AI Gateway │ ──> https://phantom.codes/v1
│ (zero prompt logs) │ ──> Ed25519 signed receipts
└───────────────────────┘
```
## Features
- **Scoped Subagent Keys** — Create child keys that spend the parent's balance up to a limit, with lifetimes and rate caps (`pai child`). Revoke one with `pai burn`.
- **Autonomous Agent Wallet** — Local Solana keypairs allow running agents to self-fund inference on chain. Capped by environment-only limits (`PHANTOM_WALLET_MAX_USD`).
- **Multi-Agent Skill & MCP** — Native skill installation for `pi`, `Claude Code`, `Codex`, and `Cursor`, plus a 16-tool Model Context Protocol (MCP) server over stdio.
- **Cryptographic Receipts** — Upstream responses return an Ed25519 signature verifying the model served, actual token counts, and cost (`pai verify`).
- **Local Tooling Suite** — Private local markdown memory notebooks, throwaway Docker sandboxes, and safe IMAP/SMTP mail drafting.
## Install
```bash
npm i -g @phantom-ai/pai
```
The package also installs the same CLI as `phantom-key`, its earlier name.
## Quickstart
```bash
# 1. Login once and install the skill for your installed agents
pai login # paste your key once; pai saves it with mode 600
pai setup # configures pi, Claude Code, Codex, and Cursor
# 2. Check balance and active caps
pai balance
# 3. Mint a child key for an autonomous subagent
pai child --limit 0.50 --ttl 6 --rate 0.10 --save researcher
# 4. Run the subagent as the scoped child
PHANTOM_KEY_NAME=researcher claude
```
## Use It From an Agent
`pai setup` installs the `phantom-ai` skill for each agent it finds in your
home directory (`pi`, `Claude Code`, `Codex`, `Cursor`). You can then prompt your agent in
plain words, for example:
> *"Give a subagent $0.50 for 6 hours with a $0.10/min rate cap."*
- `--agent pi` (or `claude`, `codex`, `cursor`) sets up one agent.
- `--mcp` also registers the MCP server with Claude Code, Codex, and Cursor.
- `pi` has no MCP and uses the CLI through the skill; install there with:
```bash
pi install npm:@phantom-ai/pai
```
## Commands
`pai --help` prints every command and flag. [docs/reference.md](docs/reference.md)
has the complete text, the MCP tools, and the environment variables, all generated
from the code.
| Group | Commands | What they do |
| --- | --- | --- |
| **Setup** | `login`, `logout`, `setup` | Save your key, and install the skill for your agents |
| **Keys** | `balance`, `child`, `children`, `key`, `rotate`, `burn` | Show credit, create child keys paid from this key, save keys by name, and replace or revoke a key |
| **Caps and plans** | `budget`, `plan` | Set a monthly cap and a per-minute cap, or money for a period |
| **Routing** | `route` | Choose which models `model: "auto"` can run, and the rules that pick one |
| **Credit** | `buy`, `payment` | Buy credit with crypto |
| **Agent wallet** | `wallet`, `autotopup`, `buy --pay` | Keep Solana wallets and pay for credit from them. See [Let the agent pay](#let-the-agent-pay) |
| **Receipts** | `verify` | Check which model answered a call, from its signed receipt |
| **Local tools** | `memory`, `mail`, `browser`, `sandbox` | See the sections below |
| **MCP** | `mcp` | Run an MCP server over stdio. See [Use with an MCP client](#use-with-an-mcp-client) |
A command runs as the saved key named by `PHANTOM_KEY_NAME` if it is set, then
as `PHANTOM_API_KEY`, then as the `login` key. A subagent started with
`PHANTOM_KEY_NAME=<child>` runs as the child even when your parent key is exported.
## Memory
Each agent gets a private notebook on this machine. Notes are markdown files in
`~/.config/phantom-key/memory/<space>/`, so you can read, edit, or commit them.
`pai` sends none of them anywhere.
| Command | What it does |
| --- | --- |
| `memory add "text" [--tag a,b] [--title t]` | Keeps a note. Pipe long notes on stdin |
| `memory search <words> [--tag t] [--any] [--limit n]` | Notes with every word (or any word with `--any`), best match first. Returns 10 by default |
| `memory list [--tag t] [--limit n]` / `memory show <id>` / `memory rm <id>` | The newest notes (50 by default), one note, or delete one |
| `memory spaces` | Each notebook and how many notes it holds |
`pai` picks the notebook from `--space`, then `PAI_MEMORY_SPACE`, then the saved
key name (`PHANTOM_KEY_NAME`), then `main`, so each subagent keeps its own.
## Mail
`pai` reads your own mailbox over IMAP and SMTP. It saves the login, with an app
password, in `~/.config/phantom-key/mail.json` (mode 600). Gmail, Outlook,
iCloud, and Fastmail addresses have presets; for any other provider, pass
`--imap host:port --smtp host:port`.
| Command | What it does |
| --- | --- |
| `mail setup --user you@gmail.com` | Checks the login by listing one message, then saves it. A wrong password is not saved. The password comes from a prompt or `PAI_MAIL_PASSWORD` |
| `mail status` | Shows the mailbox and whether sending is on |
| `mail list [--unread] [--from x] [--limit n] [--folder f]` / `mail search <words>` / `mail read <uid>` | Reads mail from `INBOX` by default. `list` returns 20 messages by default. `read` cuts a message at 20,000 characters |
| `mail draft --to a --subject s [--reply <uid>] [--body "..."]` | Saves a draft in your Drafts folder and sends nothing. Without `--body`, pai reads the body from stdin |
| `mail send` (same flags) | Sends only when `PAI_MAIL_SEND=1`, to at most `PAI_MAIL_MAX_PER_DAY` recipients (default 10) in 24 hours, and only to `PAI_MAIL_SEND_TO` (addresses or `@domain`s) when set. pai reads all three from the environment, so an agent can't turn sending on with a flag. With sending on, the `create_child_key` tool requires `save_as`, so a new key never lands in the agent's context. SMTP on 587 and IMAP on 143 require STARTTLS |
## Browser and Sandbox
`pai` wraps two existing tools:
| Command | What it does |
| --- | --- |
| `browser setup [--install]` | Checks for [agent-browser](https://github.com/vercel-labs/agent-browser), a headless Chrome built for agents. `--install` installs it with npm |
| `browser <command>` | Runs agent-browser (`open <url>`, `snapshot -i`, `click @e1`, `fill @e2 "text"`, `screenshot`). Each space gets its own session and Chrome profile in `~/.config/phantom-key/browser/<space>/`, so a subagent keeps its own logins. `AGENT_BROWSER_PROFILE=Default` uses your own Chrome profile instead |
| `sandbox check` | Reports whether Docker or Podman is running |
| `sandbox run [--image i] [--net] [--write] [--timeout s] -- <command>` | Runs a command in a throwaway container (default image `node:24-slim`). The container has no network unless `--net`, and mounts this folder read-only unless `--write`. It runs as an unprivileged user with all capabilities dropped, 2 CPUs and 2 GB of memory. It stops after 300 seconds by default and exits 124 on timeout |
## Output Format
Commands print JSON on stdout, so you can pipe into `jq`. `--table` prints
a formatted table for human inspection. `browser` and `sandbox run` pass through the exit code of the
command they ran.
| Exit code | Meaning |
| --- | --- |
| `0` | Success |
| `1` | Any other error |
| `2` | Key rejected (401/403). Get a new key; a retry fails the same way |
## Use With an MCP Client
`pai mcp` starts an MCP server over stdio. Add it to your client's config:
```json
{
"mcpServers": {
"phantom": {
"command": "npx",
"args": ["-y", "@phantom-ai/pai", "mcp"]
}
}
}
```
[docs/reference.md](docs/reference.md#mcp-tools) lists every tool with its
inputs and whether it is read-only or destructive.
`delete_key` authenticates as the key you pass in `api_key` or `key_name` and
deletes that key. It refuses the key the server runs as; use `pai burn` for
that.
## Let the Agent Pay
The agent can maintain its own Solana wallets and buy credit with them. Each wallet
is a Solana keypair in `~/.config/phantom-key/wallets/` (mode 600). The wallet
pays Phantom AI's Solana address, and Phantom AI finds the payment on chain.
1. **Create a wallet**, and back up its file:
```bash
pai wallet create --table # saved as "main"
pai wallet create --name work --table # a second one
```
2. **Fund the address** with USDC on Solana, plus some SOL for fees (at least 0.003 SOL).
3. **Set the safety cap**:
```bash
export PHANTOM_WALLET_MAX_USD=10
```
Without this variable, `pai` refuses to pay. The total over 24 hours is
capped by `PHANTOM_WALLET_MAX_USD_PER_DAY`, which defaults to the same
amount: one payment a day. Raise it for `autotopup`.
4. **Pay**:
```bash
pai buy --amount 5 --pay
```
With more than one wallet saved, `pai` asks in a terminal which one pays. It
then reports each step:
1. Getting a payment request from Phantom AI (amount, recipient, and unique reference)
2. Sending USDC or SOL from your wallet
3. Confirming on Solana
4. Phantom AI finding the payment on chain by its reference
5. Credit balance before and after
A payment request expires after 30 minutes.
You can also prompt your agent: *"Top up my Phantom AI key with $5 from my Phantom agent wallet."*
Over MCP it calls `list_wallets`, asks which wallet if not specified, then calls `pay_for_credit`.
To refill automatically when the balance runs low:
```bash
pai autotopup --below 1 --amount 5 --wallet main --every 10
```
### Safeguards
- `pai` pays the recipient and exact amount the payment request names, with that request's reference attached.
- `pai` reads `PHANTOM_WALLET_MAX_USD` and `PHANTOM_WALLET_MAX_USD_PER_DAY` strictly from the environment; no flag or tool argument can raise them.
- Before signing, `pai` checks the payment request against what it asked for: the coin, the USDC mint, and an amount no more than requested (for SOL, within 10% of Coinbase's price). A wrong or hostile `PHANTOM_BASE_URL` cannot make it sign more.
- One payment runs at a time, across processes.
- `pai` verifies the wallet balance before requesting a payment.
- `pai` records payments in `~/.config/phantom-key` before sending, and keeps the record until Phantom AI reports the payment credited or dead. If a run crashes, times out or loses the connection, the next run waits for that payment instead of paying again.
- These limits bind an agent that reaches pai through MCP. An agent with its own shell can set environment variables and edit the state folder, so give such an agent a wallet holding only what you are willing for it to spend.
- Store wallet secrets in a local secrets manager (e.g. [KRU](https://github.com/omaekumiko2-create/kru)) and inject `PHANTOM_WALLET_KEY` at runtime.
## Configuration
`pai` reads its settings from environment variables. Run `pai --help` or see
[docs/reference.md#environment-variables](docs/reference.md#environment-variables) for the full list,
including secrets, environment-only flags, and caps.
## Requirements
- **Node 24** or newer
- Runtime dependencies: `@modelcontextprotocol/sdk` and `zod` for `mcp`, `@solana/kit` for wallet payments, and `imapflow`, `mailparser`, `nodemailer` for `mail`.
Read [phantom.codes/docs/concepts](https://phantom.codes/docs/concepts) for key lifecycle and subagent hierarchy details.
## Development
```bash
npm ci
npm test # vitest
npm run typecheck
npm run docs # regenerate docs/reference.md
node src/pai.mts --help # run from source
```
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, style, and how to add a
command or MCP tool. [CHANGELOG.md](CHANGELOG.md) lists changes by release.
## Security
Report vulnerabilities privately through a
[GitHub security advisory](https://github.com/connortessaro/pai/security/advisories/new).
See [SECURITY.md](SECURITY.md).
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues