Every command deserves a wallet that thinks.
SONA is a walelt with superpowers built on Solana. In simple terms, you describe what you want to do in plain language, SONA reasons about it, confirms your intent, and executes signed transactions autonomously and also allows you to see its actions in real-time.
It pairs a sandboxed Rust signing core with an Artificially Intelligent brain written in Typescript, giving any LLM the ability to observe on-chain state, reason about actions, and execute signed transactions all within constitutional guardrails that can never be overridden.
Key capabilities:
| Feature | Description |
|---|---|
| Natural language commands | "Swap 0.01 SOL to USDC" or "Transfer 0.005 SOL to my treasury" |
| Recurring strategies | "Swap 0.01 SOL every 10 minutes" — schedules all steps automatically |
| Live balance from RPC | Always fetches real-time balance from Solana devnet, never stale |
| Constitutional safety | Four immutable laws enforced across TypeScript and Rust layers |
| Three operating modes | Standard (observe), Assisted (approve), God (autonomous) |
| Multi-provider AI | Anthropic · Groq · Gemini · OpenAI · Ollama — auto-detected from env |
| Pyth Hermes oracle | Real-time SOL/USD price feed in both agent observe and chat context |
| On-chain transparency | Every tx carries a SONA Memo receipt (Law III: Radical Transparency) |
| MCP-compatible | Any MCP host (Claude Desktop, Cursor) can use SONA as a signing tool |
| OpenClaw plugin | Install as an agent skill: clawhub install sona |
| Child wallet factory | Spawn isolated wallets per strategy — fleet-ready |
View ASCII diagram
╔══════════════════════════════════════════════════════════════════════════════════╗
║ USER INTERFACE LAYER ║
╠═══════════════════════════╦════════════════════════════════════════════════════ ║
║ CLI (packages/cli) ║ Next.js Dashboard :3000 ║
║ ───────────────────── ║ ───────────────────────────────────────────────── ║
║ sona init ║ CHAT TAB MONITOR TAB ║
║ sona start ║ ├─ AI conversation ├─ WalletState ║
║ sona logs ║ │ (SSE streaming) ├─ ActivityFeed (SSE) ║
║ sona approve ║ ├─ Model picker ├─ SpendLimitsPanel ║
║ sona status ║ │ Free · Pro · Local ├─ PendingActionsPanel ║
║ --mode standard ║ └─ Quick actions ├─ PolicyViewer ║
║ assisted ║ send · swap · stake └─ WalletRegistry ║
║ god ║ ║
║ --brain rules|ai|hybrid ║ ║
╚═══════════════════════════╩═══════════════════╦════════════════════════════════╝
│ SSE + REST
╔═══════════════════╩═══════════════════╗
║ Next.js API Routes ║
║ GET /api/stream SSE feed ║
║ GET /api/status agent status ║
║ GET /api/policy YAML policy ║
║ POST /api/policy live limits ║
║ POST /api/mode mode switch ║
║ POST /api/chat AI commands ║
║ POST /api/actions/approve ║
╚═══════════════════╦═══════════════════╝
│ reads / writes SQLite (WAL mode)
╔═══════════════════════════════════════════════╩════════════════════════════════════╗
║ AGENT CORE (TypeScript / Bun) ║
║ ║
║ ┌────────────────────────────── AGENT LOOP ───────────────────────────────┐ ║
║ │ │ ║
║ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐ │ ║
║ │ │ OBSERVE │───►│ REASON │───►│ DECIDE │───►│ EXECUTE │ │ ║
║ │ │ │ │ │ │ │ │ │ │ ║
║ │ │ · balance │ │ · rule eval│ │ · execute │ │ · build tx │ │ ║
║ │ │ · SOL price│ │ · signal │ │ · queue │ │ · simulate │ │ ║
║ │ │ (Pyth) │ │ scoring │ │ · skip │ │ · sign+send │ │ ║
║ │ └────────────┘ └────────────┘ └────────────┘ └──────┬──────┘ │ ║
║ │ │ │ ║
║ └─────────────────────────────────────────────────────────────────┼──────────┘ ║
║ │ ║
║ ┌────────────────────────────── SKILL REGISTRY ─────────────────┼──────────┐ ║
║ │ TransferSkill · SwapSkill · IsolatedSwapSkill │ │ ║
║ │ StakeSkill (Marinade) · ArbitrageSkill (Orca) ◄───────────────┘ │ ║
║ └──────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ AI Brain: rules | ai | hybrid Chat Commands: scheduled_at queue ║
║ Webhook alerts (Discord/HTTP) x402 Pay-for-Service client ║
║ Kora paymaster relay Named contacts (policy.yaml → address) ║
╚════════════════════════════════════════╦═══════════════════════════════════════════╝
│ IPC (JSON over stdin/stdout)
╔════════════════════════════════════════╩═══════════════════════════════════════════╗
║ RUST SIGNING CORE (crates/wallet-core) ║
║ ║
║ Ed25519 keypair · AES-256-GCM keystore · Law II spend limits ║
║ Enforced before every signature — TypeScript layer cannot bypass ║
╚════════════════════════════════════════════════════════════════════════════════════╝
- Bun 1.3+
- Rust 1.78+ (only needed to rebuild the signing core)
- At least one AI API key (Groq is free — console.groq.com). For the live demo, I made use of Gemini's SDK and the Grokq API where the rate limits were exceeded.
git clone https://github.com/Ubuntu-Technologies/sona.git
cd sona
bun installcp .env.example .env.localEdit .env.local — at minimum, set one AI provider key:
# AI provider (pick one — Groq is free)
GROQ_API_KEY=your_groq_key_here
# Or use Anthropic, Gemini, or OpenAI
# ANTHROPIC_API_KEY=...
# GEMINI_API_KEY=...
# OPENAI_API_KEY=...bun run sona initThis generates a keypair, encrypts it with your passphrase, and writes SONA_PASSPHRASE to .env.local.
solana airdrop 1 <your-wallet-address> --url devnet --allow-unfundedOr use the Solana CLI airdrop from the setup wizard in the dashboard.
bun run sona start --mode god --brain hybridOpen http://localhost:3000 — the dashboard launches automatically.
| Mode | Behaviour |
|---|---|
| Standard | Observe only — reads balance and price, no execution |
| Assisted | Queues actions for human approval before signing |
| God | Full autonomy — executes immediately within spend limits |
Switch modes live from the dashboard header or via:
bun run sona start --mode assisted| Brain mode | Behaviour |
|---|---|
rules |
Pure YAML policy evaluation — no AI, fully deterministic |
ai |
AI decides every cycle based on observed state |
hybrid |
AI is consulted; constitutional laws and policy caps still apply |
Auto-detected from env. Priority order: Anthropic → Gemini → Groq → OpenAI → Ollama.
| Provider | Env var | Notes |
|---|---|---|
| Anthropic | ANTHROPIC_API_KEY |
claude-opus-4-6 default |
| Gemini | GEMINI_API_KEY |
gemini-2.5-flash default |
| Groq | GROQ_API_KEY |
llama-4-maverick default (free tier available) |
| OpenAI | OPENAI_API_KEY |
gpt-4o-mini default |
| Ollama | OLLAMA_BASE_URL |
llama3 default — runs locally |
Override at runtime from the dashboard model picker (Chat tab → Model selector).
These four laws are enforced across both the TypeScript agent and the Rust signing core. No AI response, chat command, or external call can override them.
| Law | Name | Description |
|---|---|---|
| I | Owner Supremacy | Your YAML policy overrides all AI reasoning with no exceptions |
| II | Bounded Expenditure | 0.05 SOL/action, 0.5 SOL/session is enforced at the Rust signing layer |
| III | Radical Transparency | Every executed transaction includes an on-chain SONA Memo receipt |
| IV | Fail-Safe Halting | All transactions are simulated before signing |
SONA reads config/policy.yaml on startup. Rules fire when their trigger condition is met each cycle.
version: 1
agent_name: sona-alpha
mode: god # standard | assisted | god
brain: hybrid # rules | ai | hybrid
cycle_interval_seconds: 30
spend_limits:
max_per_action_sol: 0.05
max_per_session_sol: 0.5
contacts:
treasury: "7xKX..." # named address — use in action_params.destination
rules:
- id: dip_buy
priority: 10
trigger:
type: price_below
threshold: 140 # SOL/USD
action_type: transfer
action_params:
destination: treasury
amount_sol: 0.01
- id: rebalance
priority: 5
trigger:
type: balance_above
threshold: 500000000 # 0.5 SOL in lamports
action_type: swap
action_params:
input_mint: So11111111111111111111111111111111111111112
output_mint: BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k
amount_sol: 0.02
slippage_bps: 50sona init Generate keypair, encrypt keystore, write passphrase to .env.local
sona start Start the agent loop (--mode, --brain flags)
sona status Show live wallet balance and last cycle summary
sona logs Tail the audit DB — recent cycles, observations, decisions
sona approve <id> Approve a queued action (assisted mode)
sona policy Print the active policy rules
| Variable | Default | Description |
|---|---|---|
SONA_PASSPHRASE |
— | Keystore decryption passphrase (written by sona init) |
SONA_WALLET_CORE_BIN |
bin/wallet-core |
Path to Rust signing binary |
SONA_KEYSTORE_PATH |
~/.sona/keystore.json |
Encrypted keypair location |
SONA_DB_PATH |
~/.sona/audit.db |
SQLite audit database |
SONA_POLICY_PATH |
config/policy.yaml |
YAML policy rules |
SONA_DATA_DIR |
~/.sona |
Data directory root |
SONA_RPC_URL |
https://api.devnet.solana.com |
Solana RPC endpoint |
SONA_AGENT_MODE |
god |
Default operating mode |
SONA_BRAIN |
hybrid |
Default brain mode |
ANTHROPIC_API_KEY |
— | Anthropic API key |
GEMINI_API_KEY |
— | Gemini API key |
GROQ_API_KEY |
— | Groq API key (free tier at console.groq.com) |
GROQ_MODEL |
llama-3.3-70b-versatile |
Groq model override |
OPENAI_API_KEY |
— | OpenAI API key |
OPENAI_MODEL |
gpt-4o-mini |
OpenAI model override |
OLLAMA_BASE_URL |
http://localhost:11434/v1 |
Ollama base URL |
KORA_URL |
— | Kora paymaster relay URL (optional) |
SONA_WEBHOOK_URL |
— | Webhook URL for cycle alerts (Discord or HTTP POST) |
The Next.js dashboard runs on :3000 and provides:
- Chat tab — natural language commands with streaming AI responses. Ask SONA to transfer SOL, swap tokens, or describe a recurring strategy. It confirms intent and executes.
- Monitor tab — live agent telemetry via SSE: cycle status, balance, price, decisions, executions, spend limits, policy rules, child wallets, pending actions.
- Mode switcher — switch Standard / Assisted / God live without restarting.
- Setup wizard — first-run flow: register → initialize wallet → configure mode → start agent.
"What's my balance?"
"Transfer 0.005 SOL to 7xKX..."
"Swap 0.01 SOL to USDC"
"Buy the dip — swap 0.02 SOL to USDC every time price drops below $140, 5 times"
"Show my recent transactions"
SONA ships six built-in execution skills, registered in packages/agent/src/skills/index.ts:
| Skill | Trigger | Description |
|---|---|---|
TransferSkill |
action_type: transfer |
SOL and SPL token transfers with SONA Memo |
SwapSkill |
action_type: swap |
Orca Whirlpools DEX swap |
IsolatedSwapSkill |
action_type: swap_isolated |
Swap in a child wallet, sweep back to parent |
StakeSkill |
action_type: stake |
Marinade Finance liquid staking |
ArbitrageSkill |
action_type: arbitrage |
Triangular SOL→USDC→SOL via Orca (2 legs) |
See SKILLS.md for full parameter reference and how to write custom skills.
SONA exposes an MCP server at packages/mcp-server/ which means you can connect any MCP-compatible host (Claude Desktop, Cursor, VS Code) to use SONA as a signing tool.
# Start the MCP server
bun run packages/mcp-server/src/index.tsAvailable MCP tools: get_address, check_limits, sign_transaction, get_policy.
SONA ships as an installable OpenClaw skill — plug it into any OpenClaw-compatible agent to control your wallet via natural language.
clawhub install sonaOr load directly:
import register from "@sona/openclaw/plugin.ts"
register({
registerTool(tool) {
console.log("registered:", tool.name)
},
})Available tools: get_wallet_status, get_sol_price, get_agent_status, set_mode, get_policy, transfer_sol, get_pending_actions, approve_action, chat, get_activity.
Set SONA_API_URL and SONA_TOKEN to point at your running SONA instance.
SONA's WalletFactory lets the agent spawn isolated child wallets for per-strategy risk isolation:
# In policy.yaml
rules:
- id: isolated_arb
action_type: swap_isolated # creates child wallet, executes, sweeps back
action_params:
amount_sol: 0.03Child wallet lifecycle: create → fund → execute → sweep → close. The parent wallet retains all funds between strategies.
Route all transactions through a Kora relay for gasless UX:
KORA_URL=https://your-kora-instance.comSend a Discord embed or HTTP POST on every agent cycle:
SONA_WEBHOOK_URL=https://discord.com/api/webhooks/...SONA includes an X402Client that auto-pays HTTP 402 responses using Solana. The PPC is capped at 0.001 SOL/call. Demo endpoint available at /api/x402-demo.
# Install Railway CLI
npm i -g @railway/cli
# Login and link
railway login
railway link
# Deploy
railway upSet the following Railway environment variables:
GROQ_API_KEY your Groq key (free at console.groq.com)
SONA_AGENT_MODE god
SONA_BRAIN hybrid
SONA auto-detects $PORT from Railway. The dashboard, agent API, and agent loop all run in a single container. Persistent data (keystore, DB) lives on a Railway Volume mounted at /data.
After deploying, visit your Railway URL and complete the setup wizard (register → init wallet → start agent).
# Build
docker build -t sona .
# Run (mount a volume for persistent data)
docker run -p 3000:3000 \
-v sona-data:/data \
-e GROQ_API_KEY=your_key \
sonasona/
├── packages/
│ ├── agent/ TypeScript agent loop, skills, AI brain, Pyth oracle
│ ├── cli/ sona init / start / status / logs / approve
│ ├── dashboard/ Next.js 15 dashboard (Chat + Monitor tabs)
│ ├── mcp-server/ MCP server for Claude Desktop / Cursor integration
│ └── openclaw/ OpenClaw plugin adapter for ClaWHub marketplace
├── crates/
│ └── wallet-core/ Rust signing core — Ed25519, AES-256-GCM, spend limits
├── config/
│ └── policy.yaml Default policy rules (edit to customize)
├── bin/
│ └── wallet-core Pre-compiled Rust binary (Linux x86_64)
├── entrypoint.sh Container startup — dashboard + agent watcher
└── Dockerfile Single-stage bun:1.3 image
SONA's security model is built on a hard separation between the intelligence layer and the cryptographic layer. The TypeScript agent (the "shell") handles observation, reasoning, and decision-making but never holds a private key — not in memory, not on disk. The Rust binary (the "core") holds the Ed25519 keypair encrypted at rest with AES-256-GCM and Argon2id KDF, receives unsigned transaction bytes over a subprocess pipe, enforces spend limits before signing, and returns only the 64-byte signature. There is no shared memory between the two layers; the Rust process never makes network calls and cannot be exfiltrated via SSRF.
The IPC protocol uses newline-delimited JSON over stdin/stdout. The TypeScript layer sends a sign_transaction request containing the compiled transaction bytes, the amount in lamports, and the current spend policy. The Rust layer validates the amount against max_per_action_lamports and max_per_session_lamports before the keypair ever touches the transaction. If the amount exceeds policy, the Rust process returns an error — the TypeScript shell cannot bypass this check, even if the AI brain is compromised or hallucinating.
The Rust Signer trait is pluggable: the default LocalSigner uses AES-256-GCM for development, but the same interface supports Phala Network TEE (Intel SGX / AMD SEV enclaves where the key only exists in hardware) and Turnkey MPC (non-custodial multi-party computation with quorum approvals for large treasury moves). No TypeScript code changes are required to swap custody backends.
Threat mitigations:
| Threat | Mitigation |
|---|---|
| Prompt injection | AI output is a suggestion only — TypeScript constitution checks + Rust spend limits before any signature |
| Model hallucination (over-spend) | Law II: Rust rejects any amount exceeding policy limits before signing |
| Keystore theft | AES-256-GCM + Argon2id: the file is useless without the passphrase |
| Dependency compromise | Private key never in TypeScript heap — compromised npm package cannot exfiltrate it |
| Replay attack | Solana transactions include a recent blockhash (~90s TTL) — replayed transactions are rejected |
| Simulation bypass | Law IV: SONA's own simulation must pass before broadcast; skipPreflight only at the RPC layer after |
Operational security:
- The passphrase never leaves
.env.local(mode 0600). It is not logged or transmitted. - All API endpoints require JWT session tokens. Login rate-limited to 10 req/min per IP.
- Passwords require: 12+ chars, uppercase, lowercase, digit, symbol.
- CORS is locked to
SONA_DASHBOARD_ORIGIN. No wildcard on state-changing routes. - Every transaction is simulated on devnet before broadcast. Simulation failure = halt.
- Every executed transaction includes an on-chain SONA Memo for permanent audit.
Agent shows OFFLINE in dashboard
The agent watcher needs the keystore to exist and SONA_PASSPHRASE to be set in the data volume's .env.local. Complete the setup wizard to initialize.
Cannot find module '@sona/agent/src/...'
Rebuild the lockfile: bun install. Ensure you're on Bun 1.3+.
SQLITE_BUSY errors
The database uses WAL mode — this should not occur in normal operation. If it does, restart the container.
Gemini quota exceeded
Set GROQ_API_KEY in your env and remove GEMINI_API_KEY. Groq has a free tier that works out of the box.
Balance shows 0 but wallet is funded
The dashboard fetches live balance from the Solana devnet JSON-RPC on every chat command. If balance is 0, the wallet may not be funded yet — run solana airdrop 1 <address> --url devnet.
Apache-2.0 — see LICENSE for details.
Built on Solana · Powered by Pyth · Secured by Rust
