OS Vault icon # OS Vault **Self-custody, multi-chain crypto wallet for macOS.** One recovery phrase. Six chain families. Zero API keys. Nothing ever leaves your Mac. by **Simon-Pierre Boucher** ยท [contact@spboucher.ai](mailto:contact@spboucher.ai) [![Platform](https://img.shields.io/badge/platform-macOS%2015.5%2B-111?logo=apple&logoColor=white)](#requirements) [![Swift](https://img.shields.io/badge/Swift-6-F05138?logo=swift&logoColor=white)](#build) [![SwiftUI](https://img.shields.io/badge/UI-SwiftUI-0A84FF)](#architecture) [![Tests](https://img.shields.io/badge/tests-32%20passing-2EA043?logo=github)](#tests) [![Release](https://img.shields.io/badge/release-v1.0.0-blueviolet)](../../releases) [![Notarized](https://img.shields.io/badge/Apple-notarized%20%2B%20stapled-000?logo=apple&logoColor=white)](#release-pipeline) [![Keyless](https://img.shields.io/badge/API%20keys-zero-2EA043)](#infrastructure--no-api-keys) [![Self--custody](https://img.shields.io/badge/custody-yours%20only-orange)](#security-model) [![Bitcoin](https://img.shields.io/badge/Bitcoin-BIP--84-F7931A?logo=bitcoin&logoColor=white)](#chains) [![Ethereum](https://img.shields.io/badge/EVM-11%20chains-3C3C3D?logo=ethereum&logoColor=white)](#chains) [![Solana](https://img.shields.io/badge/Solana-SPL-9945FF?logo=solana&logoColor=white)](#chains) [![Tron](https://img.shields.io/badge/Tron-TRC--20-EB0029)](#chains) [![XRPL](https://img.shields.io/badge/XRPL-RLUSD-25A768?logo=xrp&logoColor=white)](#chains) [![TON](https://img.shields.io/badge/TON-jettons-0098EA?logo=ton&logoColor=white)](#chains)
--- ## Why OS Vault Most wallets make you choose: convenience (custodial, tracked, keyed APIs) or sovereignty (CLI tools, manual everything). OS Vault refuses the trade: - ๐Ÿ” **Your keys, your Mac, your encryption.** The BIP-39 mnemonic is sealed with OS Vault's **own vault format** โ€” PBKDF2-HMAC-SHA512 (600k rounds) โ†’ AES-256-GCM โ€” in a local file. No macOS Keychain, no iCloud, no telemetry. - ๐ŸŒ **One phrase, every chain.** The same 12 words derive Bitcoin (BIP-84), 11 EVM chains, Solana, Tron, XRPL and TON โ€” cross-validated against independent crypto stacks in the test suite. - ๐Ÿ—๏ธ **Zero mandatory API keys.** Every endpoint is public and keyless, with automatic failover. The only egress is blockchain RPC (+ optional CoinGecko prices, one toggle to kill). - โœ๏ธ **Sign-and-forget.** The private key exists only for the milliseconds a transaction is being signed โ€” every send re-derives it from your password and discards it. ## Chains | Family | Assets | Fees handled | Testnet default | |---|---|---|---| | **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 | | **Bitcoin** | BTC (native SegWit `bc1qโ€ฆ`) | sat/vB presets live from mempool.space, RBF on | Signet | | **Solana** | SOL + USDC (SPL) | ATA rent surfaced when the recipient has no token account | Devnet | | **Tron** | TRX + USDT (TRC-20) | **Energy burn estimated pre-send** (~13โ€“27 TRX), fee_limit capped | Nile | | **XRP Ledger** | XRP + RLUSD | Reserves shown as locked; **one-tap RLUSD trustline**; recipient trustline checked | Testnet | | **TON** | TON + USDT (jetton) | Jetton-wallet indirection handled; ~0.07 TON attached, excess refunded | Testnet | Every stablecoin contract address and decimal count was **verified live on-chain** before registration โ€” including the traps: BNB-peg USDT/USDC are 18 decimals, bridged USDC.e is indistinguishable from native USDC by `symbol()` alone, DAI is 18 while USDC is 6. See [`docs/STABLECOINS.md`](docs/STABLECOINS.md). ## Security model ``` password โ”€โ”€โ–ถ PBKDF2-HMAC-SHA512 (600k) โ”€โ”€โ–ถ AES-256-GCM โ”€โ”€โ–ถ vault.json (0600) โ–ฒ wrong password / tampering โ”€โ”˜ indistinguishable (GCM auth) unlock โ”€โ”€โ–ถ public addresses only stay in memory send โ”€โ”€โ–ถ password โ†’ derive key โ†’ sign โ†’ discard (every single time) ``` - Forced written-backup verification (3 random words) before the wallet exists - Confirm screen with recipient, amount, network, worst-case fees โ€” always - EIP-55 checksums; per-chain address validation; TESTNET badges everywhere - App Sandbox + Hardened Runtime; signed, **notarized and stapled** by Apple - The watch-only Bitcoin wallet holds public descriptors only; a throwaway in-memory signer signs PSBTs ## Infrastructure โ€” no API keys | Concern | Source (keyless) | Fallback | |---|---|---| | EVM RPC | PublicNode (11/11 verified) | official chain RPCs, health-scored failover | | Bitcoin | mempool.space Esplora | blockstream.info | | Solana | PublicNode | api.mainnet-beta.solana.com | | Tron | TronGrid (anonymous, backoff) | โ€” | | XRPL | xrplcluster.com | s1.ripple.com | | TON | toncenter v2/v3 (1 req/s, throttled client-side) | โ€” | | Prices | CoinGecko batched (USD/CAD/EUR), stale-while-revalidate, **off switch** | DefiLlama | ## Build Pure SwiftPM โ€” no `.xcodeproj`, no CocoaPods, no manual steps: ```sh scripts/vendor-walletcore.sh # one-time: vendors Trust wallet-core for macOS (~270 MB, not in git) swift build # compile swift test # 32 tests: BIP-39/BIP-84 vectors, vault crypto, decimals, validators make dev # debug bundle, ad-hoc signed, launched make release # Developer ID + notarize + staple + DMG with volume icon ``` The wallet-core vendoring is this repo's party trick: upstream ships no macOS SwiftPM support, so the script repackages the official CocoaPods tarball as a local `binaryTarget` โ€” including a surgical `ld -r` pass that demotes the duplicate Rust runtime symbol it shares with the Bitcoin Dev Kit. Details in [`docs/RESEARCH-MULTICHAIN.md`](docs/RESEARCH-MULTICHAIN.md). ## Architecture ``` Sources/OSVaultKit โ”œโ”€โ”€ Services โ”‚ โ”œโ”€โ”€ VaultCrypto โ† the encryption mechanism (no Keychain) โ”‚ โ”œโ”€โ”€ KeyManager โ† BIP-39, HD derivation, vault lifecycle โ”‚ โ”œโ”€โ”€ RPCService โ† EVM JSON-RPC with endpoint failover โ”‚ โ”œโ”€โ”€ TransactionService โ† EIP-1559 + 5 other real fee models โ”‚ โ”œโ”€โ”€ BitcoinService โ† bdk-swift, watch-only + transient signer โ”‚ โ”œโ”€โ”€ SolanaService โ† solana-swift (vendored), SPL + ATA โ”‚ โ”œโ”€โ”€ TronService โ† wallet-core signing + TronGrid REST โ”‚ โ”œโ”€โ”€ XRPLService โ† wallet-core signing + xrplcluster JSON-RPC โ”‚ โ”œโ”€โ”€ TONService โ† wallet-core signing + toncenter v2/v3 โ”‚ โ””โ”€โ”€ PriceService โ† CoinGecko keyless, cached, optional โ”œโ”€โ”€ Models โ† Network (chain registry), Token (verified matrix)โ€ฆ โ””โ”€โ”€ Views โ† SwiftUI: onboarding, home, per-chain panels ``` ## Requirements - macOS 15.5+ (Apple silicon or Intel) - That's it. No accounts, no keys, no configuration. ## Testnet quickstart 1. Create a wallet (write the 12 words down โ€” the app makes you prove it). 2. Fund: [Circle faucet](https://faucet.circle.com) (USDC on Base Sepolia + Solana devnet), a Base Sepolia ETH faucet, [mempool.space signet faucet](https://signetfaucet.com), [nileex.io](https://nileex.io) (TRX + test USDT), [XRPL faucet](https://xrpl.org/resources/dev-tools/xrp-faucets), [@testgiver_ton_bot](https://t.me/testgiver_ton_bot). 3. Send. Watch it confirm on the linked explorer. ---
**Built with Swift, paranoia, and a refusal to type API keys.** ยฉ 2026 Simon-Pierre Boucher ยท [contact@spboucher.ai](mailto:contact@spboucher.ai)