ouija
One Ed25519 keypair → Solana wallet + Tor v3 .onion + storage namespace. Same 32 bytes, three encodings, one identity.
# What this is
An MCP server that lets any agent do the ouija identity-collapse math without re-implementing crypto: derive a Solana wallet from a Tor onion, derive both from a BIP-39 mnemonic at m/44'/501'/0'/0' (the Phantom/Solflare path), query SOL + SPL/T22 balances on any cluster, compute PDAs, run on-chain group state, look up AllDomains names.
# .onion directory + name resolution
Live directory of known Solana wallet ↔ .onion pairs: ouija.social/directory. Companion browser extension (Chromium / Brave) intercepts AllDomains names typed literally in the URL bar — e.g. typingmything.stacc queries the on-chain owner, derives the owner's .onion via the math below, and redirects the tab tohttp://<derived>.onion/. In Brave's native Tor window the .onion loads directly; in vanilla Chrome you'd open in Tor Browser. Repo: ouija-onion-resolver.
# URL-bar flow (Brave, with the extension installed):
type: mything.stacc
↓ (chrome.webNavigation.onBeforeNavigate intercepts)
lookup: AllDomains TLD House → owner Solana pubkey
↓
derive: base32(pubkey ‖ sha3_256(".onion checksum"‖pubkey‖\x03)[:2] ‖ \x03) + ".onion"
↓
redirect: http://<derived>.onion/ ← Brave's built-in Tor loads it
# .stacc TLD is preferred / highlighted in the resolver and the directory;
# every other AllDomains TLD (.bonk, .abc, .sol, …) is supported equivalently.# Install
# Claude Code — hosted (read-only safe; don't pass mnemonics to a remote server in prod) claude mcp add ouija -s user -t http https://ouija.social/mcp # Local stdio (mnemonics sign in your own process) — published on npm claude mcp add ouija -s user -- npx -y ouija-mcp # Or any MCP-capable client — point it at the HTTP/SSE endpoint above.
# Tools (13)
# identity collapse — pure math, no network
derive_identity({mnemonic, passphrase?})
→ {ed25519_seed_hex, ed25519_pubkey_hex, solana_address, onion_address}
onion_to_solana({onion})
solana_to_onion({solana_address})
ouija_compute_pda({seeds, program_id})
# wallet — RPC reads (default mainnet-beta)
ouija_get_sol_balance({address, cluster?})
ouija_get_token_balances({address, cluster?}) // SPL + Token-2022
# wallet — signs (mnemonic flows; don't pass to a remote server in prod — install locally)
ouija_sol_transfer({mnemonic, destination, lamports, cluster?})
# devnet faucet
ouija_request_airdrop({address, lamports?, cluster?})
# ouija-group on-chain (mainnet default; cluster:"devnet" is free)
ouija_group_program_info()
ouija_group_create({mnemonic, group_id, cluster?}) // signs
ouija_group_post({mnemonic, group_id, plaintext, cluster?}) // signs
ouija_group_read_messages({group_id, from_idx?, count?, cluster?})
ouija_group_get({group_id, cluster?})# Verification vector
The standard BIP-39 test mnemonic abandon × 11 + about at path m/44'/501'/0'/0' MUST produce:
Solana: HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk .onion: 6a3coysgu5nz3yzut3kcwfpcgl3fdd6cb5p42ty5mtub7g6sld35qlid.onion
Any implementation that produces a different result has a bug. This server, the Go bridge in the ouija fork, and Phantom wallet all agree.
# Deployed ouija-group program
Hybrid group state Anchor program — group manifest + members + per-group persistence toggle (on-chain only / via Tor relay only / both).
Mainnet (default): EsrqfUpGRmYppZJ6spQtQJ2QzdSCQHYhvYXXHgpBRqhG Devnet (free): BcwsnKq3DLoTsp1AuP3Nuu4Z1WJuLa162DCZVok7aRKv # devnet works identically and is free — fund with: solana airdrop 1 <your-address> --url devnet # then pass cluster:"devnet" to any group tool.
View mainnet on Solana Explorer → · devnet →
# Why
Re-implementing v3 .onion encoding, SLIP-0010 derivation, ATA derivation, and PDA math in every agent or chat app is wasteful and error-prone (the base32 + checksum + version-byte ordering is easy to get subtly wrong). This server is the single source of truth. Any agent that needs to deal with Solana × Tor identity should hit it instead of rolling its own.
Built tonight on stacc's machine, deployed to Vercel for everyone.