SPB Git

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%
7.7 KB · 155 lines markdown
Rendered Raw Blame History
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[![Platform](https://img.shields.io/badge/platform-macOS%2015.5%2B-111?logo=apple&logoColor=white)](#requirements)11[![Swift](https://img.shields.io/badge/Swift-6-F05138?logo=swift&logoColor=white)](#build)12[![SwiftUI](https://img.shields.io/badge/UI-SwiftUI-0A84FF)](#architecture)13[![Tests](https://img.shields.io/badge/tests-32%20passing-2EA043?logo=github)](#tests)14[![Release](https://img.shields.io/badge/release-v1.0.0-blueviolet)](../../releases)15[![Notarized](https://img.shields.io/badge/Apple-notarized%20%2B%20stapled-000?logo=apple&logoColor=white)](#release-pipeline)16[![Keyless](https://img.shields.io/badge/API%20keys-zero-2EA043)](#infrastructure--no-api-keys)17[![Self--custody](https://img.shields.io/badge/custody-yours%20only-orange)](#security-model)1819[![Bitcoin](https://img.shields.io/badge/Bitcoin-BIP--84-F7931A?logo=bitcoin&logoColor=white)](#chains)20[![Ethereum](https://img.shields.io/badge/EVM-11%20chains-3C3C3D?logo=ethereum&logoColor=white)](#chains)21[![Solana](https://img.shields.io/badge/Solana-SPL-9945FF?logo=solana&logoColor=white)](#chains)22[![Tron](https://img.shields.io/badge/Tron-TRC--20-EB0029)](#chains)23[![XRPL](https://img.shields.io/badge/XRPL-RLUSD-25A768?logo=xrp&logoColor=white)](#chains)24[![TON](https://img.shields.io/badge/TON-jettons-0098EA?logo=ton&logoColor=white)](#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)6970                          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