spb/os-vault Public
Self-custody, multi-chain crypto wallet for macOS. One recovery phrase, six chain families, zero API keys — nothing leaves your Mac.
Swift 96%
Shell 3.4%
Makefile 0.6%
1<div align="center">23<img src="assets/icon/os-vault.svg" width="128" alt="OS Vault icon"/>45# OS Vault67**Self-custody, multi-chain crypto wallet for macOS.**8One recovery phrase. Six chain families. Zero API keys. Nothing ever leaves your Mac.910[](#requirements)11[](#build)12[](#architecture)13[](#tests)14[](../../releases)15[](#release-pipeline)16[](#infrastructure--no-api-keys)17[](#security-model)1819[](#chains)20[](#chains)21[](#chains)22[](#chains)23[](#chains)24[](#chains)2526</div>2728---2930## Why OS Vault3132Most wallets make you choose: convenience (custodial, tracked, keyed APIs) or33sovereignty (CLI tools, manual everything). OS Vault refuses the trade:3435- 🔐 **Your keys, your Mac, your encryption.** The BIP-39 mnemonic is sealed36 with OS Vault's **own vault format** — PBKDF2-HMAC-SHA512 (600k rounds) →37 AES-256-GCM — in a local file. No macOS Keychain, no iCloud, no telemetry.38- 🌐 **One phrase, every chain.** The same 12 words derive Bitcoin (BIP-84),39 11 EVM chains, Solana, Tron, XRPL and TON — cross-validated against40 independent crypto stacks in the test suite.41- 🗝️ **Zero mandatory API keys.** Every endpoint is public and keyless, with42 automatic failover. The only egress is blockchain RPC (+ optional CoinGecko43 prices, one toggle to kill).44- ✍️ **Sign-and-forget.** The private key exists only for the milliseconds a45 transaction is being signed — every send re-derives it from your password46 and discards it.4748## Chains4950| Family | Assets | Fees handled | Testnet default |51|---|---|---|---|52| **EVM × 11** — Ethereum, Base, Arbitrum, OP, Polygon, BNB, Avalanche, Gnosis, Linea, Scroll (+ Base Sepolia) | USDC, USDC.e, USDT, DAI, USDS, EURC + native coin | EIP-1559, BSC zero-base-fee, **OP-stack/Scroll L1 data fee via oracle**, Arbitrum inclusive estimates, Linea pinned base | Base Sepolia |53| **Bitcoin** | BTC (native SegWit `bc1q…`) | sat/vB presets live from mempool.space, RBF on | Signet |54| **Solana** | SOL + USDC (SPL) | ATA rent surfaced when the recipient has no token account | Devnet |55| **Tron** | TRX + USDT (TRC-20) | **Energy burn estimated pre-send** (~13–27 TRX), fee_limit capped | Nile |56| **XRP Ledger** | XRP + RLUSD | Reserves shown as locked; **one-tap RLUSD trustline**; recipient trustline checked | Testnet |57| **TON** | TON + USDT (jetton) | Jetton-wallet indirection handled; ~0.07 TON attached, excess refunded | Testnet |5859Every stablecoin contract address and decimal count was **verified live60on-chain** before registration — including the traps: BNB-peg USDT/USDC are6118 decimals, bridged USDC.e is indistinguishable from native USDC by62`symbol()` alone, DAI is 18 while USDC is 6. See63[`docs/STABLECOINS.md`](docs/STABLECOINS.md).6465## Security model6667```68password ──▶ PBKDF2-HMAC-SHA512 (600k) ──▶ AES-256-GCM ──▶ vault.json (0600)69 ▲70 wrong password / tampering ─┘ indistinguishable (GCM auth)7172unlock ──▶ public addresses only stay in memory73send ──▶ password → derive key → sign → discard (every single time)74```7576- Forced written-backup verification (3 random words) before the wallet exists77- Confirm screen with recipient, amount, network, worst-case fees — always78- EIP-55 checksums; per-chain address validation; TESTNET badges everywhere79- App Sandbox + Hardened Runtime; signed, **notarized and stapled** by Apple80- The watch-only Bitcoin wallet holds public descriptors only; a throwaway81 in-memory signer signs PSBTs8283## Infrastructure — no API keys8485| Concern | Source (keyless) | Fallback |86|---|---|---|87| EVM RPC | PublicNode (11/11 verified) | official chain RPCs, health-scored failover |88| Bitcoin | mempool.space Esplora | blockstream.info |89| Solana | PublicNode | api.mainnet-beta.solana.com |90| Tron | TronGrid (anonymous, backoff) | — |91| XRPL | xrplcluster.com | s1.ripple.com |92| TON | toncenter v2/v3 (1 req/s, throttled client-side) | — |93| Prices | CoinGecko batched (USD/CAD/EUR), stale-while-revalidate, **off switch** | DefiLlama |9495## Build9697Pure SwiftPM — no `.xcodeproj`, no CocoaPods, no manual steps:9899```sh100scripts/vendor-walletcore.sh # one-time: vendors Trust wallet-core for macOS (~270 MB, not in git)101swift build # compile102swift test # 32 tests: BIP-39/BIP-84 vectors, vault crypto, decimals, validators103make dev # debug bundle, ad-hoc signed, launched104make release # Developer ID + notarize + staple + DMG with volume icon105```106107The wallet-core vendoring is this repo's party trick: upstream ships no macOS108SwiftPM support, so the script repackages the official CocoaPods tarball as a109local `binaryTarget` — including a surgical `ld -r` pass that demotes the110duplicate Rust runtime symbol it shares with the Bitcoin Dev Kit. Details in111[`docs/RESEARCH-MULTICHAIN.md`](docs/RESEARCH-MULTICHAIN.md).112113## Architecture114115```116Sources/OSVaultKit117├── Services118│ ├── VaultCrypto ← the encryption mechanism (no Keychain)119│ ├── KeyManager ← BIP-39, HD derivation, vault lifecycle120│ ├── RPCService ← EVM JSON-RPC with endpoint failover121│ ├── TransactionService ← EIP-1559 + 5 other real fee models122│ ├── BitcoinService ← bdk-swift, watch-only + transient signer123│ ├── SolanaService ← solana-swift (vendored), SPL + ATA124│ ├── TronService ← wallet-core signing + TronGrid REST125│ ├── XRPLService ← wallet-core signing + xrplcluster JSON-RPC126│ ├── TONService ← wallet-core signing + toncenter v2/v3127│ └── PriceService ← CoinGecko keyless, cached, optional128├── Models ← Network (chain registry), Token (verified matrix)…129└── Views ← SwiftUI: onboarding, home, per-chain panels130```131132## Requirements133134- macOS 15.5+ (Apple silicon or Intel)135- That's it. No accounts, no keys, no configuration.136137## Testnet quickstart1381391. Create a wallet (write the 12 words down — the app makes you prove it).1402. Fund: [Circle faucet](https://faucet.circle.com) (USDC on Base Sepolia +141 Solana devnet), a Base Sepolia ETH faucet, [mempool.space signet142 faucet](https://signetfaucet.com), [nileex.io](https://nileex.io) (TRX +143 test USDT), [XRPL faucet](https://xrpl.org/resources/dev-tools/xrp-faucets),144 [@testgiver_ton_bot](https://t.me/testgiver_ton_bot).1453. Send. Watch it confirm on the linked explorer.146147---148149<div align="center">150151**Built with Swift, paranoia, and a refusal to type API keys.**152© 2026 Simon-Pierre Boucher · [contact@spboucher.ai](mailto:contact@spboucher.ai)153154</div>155