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.910by **Simon-Pierre Boucher** · [contact@spboucher.ai](mailto:contact@spboucher.ai)1112[](#requirements)13[](#build)14[](#architecture)15[](#tests)16[](../../releases)17[](#release-pipeline)18[](#infrastructure--no-api-keys)19[](#security-model)2021[](#chains)22[](#chains)23[](#chains)24[](#chains)25[](#chains)26[](#chains)2728</div>2930---3132## Why OS Vault3334Most wallets make you choose: convenience (custodial, tracked, keyed APIs) or35sovereignty (CLI tools, manual everything). OS Vault refuses the trade:3637- 🔐 **Your keys, your Mac, your encryption.** The BIP-39 mnemonic is sealed38 with OS Vault's **own vault format** — PBKDF2-HMAC-SHA512 (600k rounds) →39 AES-256-GCM — in a local file. No macOS Keychain, no iCloud, no telemetry.40- 🌐 **One phrase, every chain.** The same 12 words derive Bitcoin (BIP-84),41 11 EVM chains, Solana, Tron, XRPL and TON — cross-validated against42 independent crypto stacks in the test suite.43- 🗝️ **Zero mandatory API keys.** Every endpoint is public and keyless, with44 automatic failover. The only egress is blockchain RPC (+ optional CoinGecko45 prices, one toggle to kill).46- ✍️ **Sign-and-forget.** The private key exists only for the milliseconds a47 transaction is being signed — every send re-derives it from your password48 and discards it.4950## Chains5152| Family | Assets | Fees handled | Testnet default |53|---|---|---|---|54| **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 |55| **Bitcoin** | BTC (native SegWit `bc1q…`) | sat/vB presets live from mempool.space, RBF on | Signet |56| **Solana** | SOL + USDC (SPL) | ATA rent surfaced when the recipient has no token account | Devnet |57| **Tron** | TRX + USDT (TRC-20) | **Energy burn estimated pre-send** (~13–27 TRX), fee_limit capped | Nile |58| **XRP Ledger** | XRP + RLUSD | Reserves shown as locked; **one-tap RLUSD trustline**; recipient trustline checked | Testnet |59| **TON** | TON + USDT (jetton) | Jetton-wallet indirection handled; ~0.07 TON attached, excess refunded | Testnet |6061Every stablecoin contract address and decimal count was **verified live62on-chain** before registration — including the traps: BNB-peg USDT/USDC are6318 decimals, bridged USDC.e is indistinguishable from native USDC by64`symbol()` alone, DAI is 18 while USDC is 6. See65[`docs/STABLECOINS.md`](docs/STABLECOINS.md).6667## Security model6869```70password ──▶ PBKDF2-HMAC-SHA512 (600k) ──▶ AES-256-GCM ──▶ vault.json (0600)71 ▲72 wrong password / tampering ─┘ indistinguishable (GCM auth)7374unlock ──▶ public addresses only stay in memory75send ──▶ password → derive key → sign → discard (every single time)76```7778- Forced written-backup verification (3 random words) before the wallet exists79- Confirm screen with recipient, amount, network, worst-case fees — always80- EIP-55 checksums; per-chain address validation; TESTNET badges everywhere81- App Sandbox + Hardened Runtime; signed, **notarized and stapled** by Apple82- The watch-only Bitcoin wallet holds public descriptors only; a throwaway83 in-memory signer signs PSBTs8485## Infrastructure — no API keys8687| Concern | Source (keyless) | Fallback |88|---|---|---|89| EVM RPC | PublicNode (11/11 verified) | official chain RPCs, health-scored failover |90| Bitcoin | mempool.space Esplora | blockstream.info |91| Solana | PublicNode | api.mainnet-beta.solana.com |92| Tron | TronGrid (anonymous, backoff) | — |93| XRPL | xrplcluster.com | s1.ripple.com |94| TON | toncenter v2/v3 (1 req/s, throttled client-side) | — |95| Prices | CoinGecko batched (USD/CAD/EUR), stale-while-revalidate, **off switch** | DefiLlama |9697## Build9899Pure SwiftPM — no `.xcodeproj`, no CocoaPods, no manual steps:100101```sh102scripts/vendor-walletcore.sh # one-time: vendors Trust wallet-core for macOS (~270 MB, not in git)103swift build # compile104swift test # 32 tests: BIP-39/BIP-84 vectors, vault crypto, decimals, validators105make dev # debug bundle, ad-hoc signed, launched106make release # Developer ID + notarize + staple + DMG with volume icon107```108109The wallet-core vendoring is this repo's party trick: upstream ships no macOS110SwiftPM support, so the script repackages the official CocoaPods tarball as a111local `binaryTarget` — including a surgical `ld -r` pass that demotes the112duplicate Rust runtime symbol it shares with the Bitcoin Dev Kit. Details in113[`docs/RESEARCH-MULTICHAIN.md`](docs/RESEARCH-MULTICHAIN.md).114115## Architecture116117```118Sources/OSVaultKit119├── Services120│ ├── VaultCrypto ← the encryption mechanism (no Keychain)121│ ├── KeyManager ← BIP-39, HD derivation, vault lifecycle122│ ├── RPCService ← EVM JSON-RPC with endpoint failover123│ ├── TransactionService ← EIP-1559 + 5 other real fee models124│ ├── BitcoinService ← bdk-swift, watch-only + transient signer125│ ├── SolanaService ← solana-swift (vendored), SPL + ATA126│ ├── TronService ← wallet-core signing + TronGrid REST127│ ├── XRPLService ← wallet-core signing + xrplcluster JSON-RPC128│ ├── TONService ← wallet-core signing + toncenter v2/v3129│ └── PriceService ← CoinGecko keyless, cached, optional130├── Models ← Network (chain registry), Token (verified matrix)…131└── Views ← SwiftUI: onboarding, home, per-chain panels132```133134## Requirements135136- macOS 15.5+ (Apple silicon or Intel)137- That's it. No accounts, no keys, no configuration.138139## Testnet quickstart1401411. Create a wallet (write the 12 words down — the app makes you prove it).1422. Fund: [Circle faucet](https://faucet.circle.com) (USDC on Base Sepolia +143 Solana devnet), a Base Sepolia ETH faucet, [mempool.space signet144 faucet](https://signetfaucet.com), [nileex.io](https://nileex.io) (TRX +145 test USDT), [XRPL faucet](https://xrpl.org/resources/dev-tools/xrp-faucets),146 [@testgiver_ton_bot](https://t.me/testgiver_ton_bot).1473. Send. Watch it confirm on the linked explorer.148149---150151<div align="center">152153**Built with Swift, paranoia, and a refusal to type API keys.**154© 2026 Simon-Pierre Boucher · [contact@spboucher.ai](mailto:contact@spboucher.ai)155156</div>157