A headless, self-custody crypto wallet for your terminal — and a JSON-RPC server to power an exchange.
Nine chains. Local signing. No GUI, no telemetry, no custodian.
Download · Quick start · RPC server · Build an exchange · Security
odrium is the command-line build of the Odrium wallet. It
derives keys for nine chains, signs every transaction locally, and ships
a built-in JSON-RPC 2.0 server — the foundation for a self-hosted exchange,
payment gateway, or any backend that needs to move crypto programmatically.
Your seed never leaves the machine. The vault is decrypted in memory for a single command (or for the lifetime of the RPC daemon) and wiped on exit. The encrypted vault is byte-compatible with the Odrium desktop app — create a wallet here and open it there, or vice versa.
$ odrium new-address btc
BTC #0 bc1qrpspwczgfvv4p4terukk5hms8er9sathqmdhwa
$ odrium balance --chain btc --address 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
BTC 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa 57.200485 BTC
$ odrium send eth --to 0xRecipient... --amount 0.25
broadcast ok
txid: 0xabc...
explorer: https://etherscan.io/tx/0xabc...- One binary, nine chains — Bitcoin, Litecoin, Ethereum, BNB Smart Chain, Arbitrum, Tron, Solana, TON, Monero, Zcash, XRP — plus ERC-20 / BEP-20 / TRC-20 / SPL tokens.
- Local signing — keys are derived from your seed and used in-process; nothing is ever sent to a third party.
- Server-ready RPC —
odrium rpcserves the wallet over HTTP JSON-RPC with bearer-token auth and a read-only-by-default safety gate. - Exchange toolkit — per-user deposit addresses, deposit detection, withdrawals, fee estimation, and cross-chain swaps — all over the wire.
- Scriptable —
--jsonon every command; password via env var for unattended / service use. - Exact amounts — string-based unit conversion, no floating-point rounding.
- No telemetry — talks only to the chain RPC endpoints you configure.
Grab a binary from the latest release.
| Platform | File |
|---|---|
| 🍎 macOS (Apple Silicon + Intel) | odrium-macos-universal |
| 🐧 Linux x86-64 (glibc 2.31+) | odrium-linux-x86_64 |
| 🪟 Windows x86-64 (10 / 11) | odrium-windows-x86_64.exe |
macOS
curl -L -o odrium https://github.com/odrium-dev/odrium-wallet-cli/releases/download/v1/odrium-macos-universal
chmod +x odrium
xattr -d com.apple.quarantine odrium 2>/dev/null # clear Gatekeeper quarantine
sudo mv odrium /usr/local/bin/
odrium --helpLinux
curl -L -o odrium https://github.com/odrium-dev/odrium-wallet-cli/releases/download/v1/odrium-linux-x86_64
chmod +x odrium
sudo mv odrium /usr/local/bin/
odrium --helpWindows (PowerShell)
Invoke-WebRequest -Uri https://github.com/odrium-dev/odrium-wallet-cli/releases/download/v1/odrium-windows-x86_64.exe -OutFile odrium.exe
.\odrium.exe --helpcurl -LO https://github.com/odrium-dev/odrium-wallet-cli/releases/download/v1/SHA256SUMS.txt
shasum -a 256 -c SHA256SUMS.txtSHA-256 checksums
f7db5dad48deca562af3d78683bcb9eee8da54b6ee13471c229ba486e447b1f6 odrium-linux-x86_64
bb84b8bcb38455e94a79d4c6b90da5fb33e554d30a4fea81afb0b8edf9d3e259 odrium-macos-universal
fd2091f1e3609de1f3d2b054b211d2bb2f19c9f93b743a9fd0ebe49234c488bc odrium-windows-x86_64.exe
# 1. Create a wallet — the recovery phrase is shown ONCE. Write it down.
odrium create --name main
# 2. Derive receive addresses
odrium new-address btc
odrium new-address eth
# 3. Check balances
odrium balance # all tracked addresses
odrium balance --chain btc
# 4. Estimate a fee, then send
odrium fee btc --to bc1q... --amount 0.01
odrium send btc --to bc1q... --amount 0.01The unlock password is resolved in this order: --password flag →
ODRIUM_PASSWORD env var → interactive hidden prompt. Add --json to any
command for machine-readable output.
💾 Where's my data? The vault lives in the same place as the Odrium desktop app (
~/Library/Application Support/com.wallet.desktopon macOS,~/.local/share/wallet-desktopon Linux,%APPDATA%\WalletDesktopon Windows). Override withODRIUM_DATA_DIR.
| Command | Description |
|---|---|
wallets · switch <id> |
list wallets · set the active one |
create · import |
new wallet (prints seed) · restore from a mnemonic |
addresses · receive <chain> |
list addresses · show receive addresses |
new-address <chain> |
derive & save an address (--label, --index, --private for ZEC) |
balance |
balances for all addresses, a chain, or any address |
fee |
estimate a network fee without broadcasting |
send · send-token |
sign & broadcast a native · token transfer |
mnemonic |
reveal the recovery phrase (asks for the password) |
rpc |
run the JSON-RPC server |
Run odrium help <command> for per-command flags. Amounts default to human units
(0.5 ETH); pass --base-units for raw sat/wei/lamport.
odrium rpc unlocks one wallet, keeps it in memory, and serves JSON-RPC 2.0 over
HTTP — run a wallet on a server and drive it from your own backend.
# read-only, bound to localhost, auto-generated token
ODRIUM_PASSWORD=… odrium rpc --port 17800
# enable fund movement + address creation, with your own token
ODRIUM_PASSWORD=… odrium rpc --allow-write --token "$(openssl rand -hex 32)"Every request is POST / with a bearer token:
curl -s -X POST http://127.0.0.1:17800 \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"address.new","params":{"chain":"btc","label":"user-42"}}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"chain": "btc",
"index": 0,
"derivation_path": "m/84'/0'/0'/0/0",
"address": "bc1qsukx7ehzujgnal8tep8clmdvlpygxvweequfw8",
"label": "user-42",
"source": "master"
}
}All methods
Read — status, rpc.discover, wallets.list, addresses.list,
tokens.list, balance.native, balance.tokens, balances.bulk,
balances.cached, tx.list, prices.get, swap.assets, swap.quotes,
swap.list, swap.refresh, rpc_config.get.
Write (require --allow-write) — address.new, tx.refresh,
fee.estimate, tx.send, tx.sendToken, swap.create.
The RPC server gives you everything a deposit/withdrawal backend needs:
URL=http://127.0.0.1:17800; H="Authorization: Bearer $TOKEN"; J="Content-Type: application/json"
# 1) give each user a unique deposit address
curl -s -X POST $URL -H "$H" -H "$J" \
-d '{"jsonrpc":"2.0","id":1,"method":"address.new","params":{"chain":"btc","label":"user-42"}}'
# 2) poll for deposits — returns the fetched on-chain records and caches them
curl -s -X POST $URL -H "$H" -H "$J" \
-d '{"jsonrpc":"2.0","id":2,"method":"tx.refresh","params":{"chain":"btc"}}'
# 3) pay out a withdrawal (amount in base units, e.g. satoshi)
curl -s -X POST $URL -H "$H" -H "$J" \
-d '{"jsonrpc":"2.0","id":3,"method":"tx.send","params":{"chain":"btc","from_index":0,"to":"bc1q...","amount":"50000"}}'Operational tips
- Dedupe deposits by
txid;tx.refreshmay return a transaction more than once. - Wait for enough confirmations (check
confirmedand depth viablock) before crediting. - Keep only a hot working balance on the server; hold reserves in a separate cold wallet — open it with the same CLI on an offline machine.
- Generate a pool of deposit addresses ahead of time under heavy load
(
address.newre-seals the vault on each call).
- Self-custody. You hold the seed. There is no server, no account, no recovery service. Lose the recovery phrase and the funds are gone.
- Local signing. Private keys never leave the process; secrets are zeroized on exit.
- RPC is locked down by default. Binds
127.0.0.1; non-loopback needs--allow-remote(put TLS in front and firewall it). Bearer token on every request. Fund-moving methods require--allow-write. - No telemetry. Outbound traffic goes only to the chain RPC endpoints you configure.
⚠️ This software handles real funds. Back up your recovery phrase offline, test with small amounts first, and keep large balances in cold storage.
| id | Chain | Ticker | id | Chain | Ticker | |
|---|---|---|---|---|---|---|
btc |
Bitcoin | BTC | ton |
TON | TON | |
ltc |
Litecoin | LTC | xmr |
Monero | XMR | |
eth |
Ethereum | ETH | zec |
Zcash | ZEC | |
bsc |
BNB Smart Chain | BNB | xrp |
XRP Ledger | XRP | |
arb |
Arbitrum | ETH | trx |
Tron | TRX | |
sol |
Solana | SOL |
Tokens — ERC-20 (ETH / Arbitrum), BEP-20 (BSC), TRC-20 (Tron), SPL (Solana):
USDT, USDC, DAI, WBTC, WETH, LINK, UNI and more. Full list via tokens.list.
Limitations — Monero balances/sends require the Odrium desktop scanner (the chain is private by design). Shielded Zcash sends (t→z) go through the desktop app; deriving shielded receive addresses and transparent ZEC sends work here.
Is it really non-custodial?
Yes. Keys are derived from a BIP-39 seed stored locally in an Argon2id-encrypted vault. Nothing is uploaded anywhere.
macOS says the binary is damaged / can't be opened.
The binary is ad-hoc signed (not notarized). Clear the quarantine attribute:
xattr -d com.apple.quarantine ./odrium.
Does it work on Alpine / musl?
The Linux build is glibc-linked (glibc 2.31+). For a fully static musl binary, open an issue.
Can the CLI and the desktop app share a wallet?
Yes — they use the same encrypted vault format and data directory.