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.8 KB · 157 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.910by **Simon-Pierre Boucher** · [contact@spboucher.ai](mailto:contact@spboucher.ai)1112[![Platform](https://img.shields.io/badge/platform-macOS%2015.5%2B-111?logo=apple&logoColor=white)](#requirements)13[![Swift](https://img.shields.io/badge/Swift-6-F05138?logo=swift&logoColor=white)](#build)14[![SwiftUI](https://img.shields.io/badge/UI-SwiftUI-0A84FF)](#architecture)15[![Tests](https://img.shields.io/badge/tests-32%20passing-2EA043?logo=github)](#tests)16[![Release](https://img.shields.io/badge/release-v1.0.0-blueviolet)](../../releases)17[![Notarized](https://img.shields.io/badge/Apple-notarized%20%2B%20stapled-000?logo=apple&logoColor=white)](#release-pipeline)18[![Keyless](https://img.shields.io/badge/API%20keys-zero-2EA043)](#infrastructure--no-api-keys)19[![Self--custody](https://img.shields.io/badge/custody-yours%20only-orange)](#security-model)2021[![Bitcoin](https://img.shields.io/badge/Bitcoin-BIP--84-F7931A?logo=bitcoin&logoColor=white)](#chains)22[![Ethereum](https://img.shields.io/badge/EVM-11%20chains-3C3C3D?logo=ethereum&logoColor=white)](#chains)23[![Solana](https://img.shields.io/badge/Solana-SPL-9945FF?logo=solana&logoColor=white)](#chains)24[![Tron](https://img.shields.io/badge/Tron-TRC--20-EB0029)](#chains)25[![XRPL](https://img.shields.io/badge/XRPL-RLUSD-25A768?logo=xrp&logoColor=white)](#chains)26[![TON](https://img.shields.io/badge/TON-jettons-0098EA?logo=ton&logoColor=white)](#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)7172                          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