Experimental · regtest/testnet only
Mufasa Bitcoin Wallet
A personal, self-hosted Bitcoin wallet that connects address management, node synchronization, local state, and signed transactions in a focused Rust CLI.
Software engineer · sole developer
System flow
- 01Load the wallet
- 02Sync with Bitcoin Core
- 03Build & sign a PSBT
- 04Persist change & broadcast
- 05Record pending state
Context & ownership
I built Mufasa from scratch as a descriptor-based wallet application. Its initial scope was a personal, self-hosted wallet, with a command-line interface and locally persisted state.
It supports regtest and testnet. The implementation brings existing Bitcoin wallet primitives together into a complete application, with clear boundaries between command handling, wallet behavior, node interaction, and persistence.
Architecture & dependencies
A thin executable calls a reusable Rust wallet library. I separated these responsibilities to keep the architecture extensible and wallet behavior independently testable.
BDK Wallet supplies descriptor handling, address derivation, coin selection, PSBT construction, and signing. BDK Bitcoind RPC supplies block and mempool synchronization; Bitcoin Core RPC handles node interaction and broadcasting. Bitcoin types represent networks, addresses, amounts, and transactions, while Clap handles the command interface.
Persistence & transaction ordering
The wallet stores public descriptors, derivation state, and chain checkpoints in SQLite. I chose BDK’s synchronous Rusqlite persistence because a single-process personal CLI did not need an async runtime. A future server would be a reason to revisit that choice.
Sending first synchronizes the wallet and checks the destination network. BDK builds and signs the PSBT; the application then persists newly revealed change state before broadcasting. This preserves the change index if the process exits after the node accepts the transaction. After broadcast, the transaction is recorded locally as unconfirmed; confirmation requires a later chain update.
Offline reads & test boundaries
I added an offline flag for balance and UTXO inspection. These commands load the saved wallet state and skip the Bitcoin Core connection and synchronization. Users can inspect the last known state without a running node, with the tradeoff that chain and mempool information may be stale.
Node interactions sit behind a narrow interface so transaction tests can run without Bitcoin Core. Tests use a recording backend and temporary SQLite databases to exercise signing, broadcasting, and persisted state. These isolated checks complement real-node validation rather than replacing it.
Regtest validation
I manually exercised wallet creation and receiving-address generation, receipt of regtest funds, synchronization and balance inspection, sending, block confirmation, and reopening the persisted wallet.
The environment used Bitcoin Core managed locally through Polar. Mufasa connected using Polar’s RPC credentials. I also used Bitcoin Core’s built-in wallet as a transaction counterparty, then synchronized the resulting transaction into Mufasa’s SQLite state. This validates a local regtest workflow; it does not establish production readiness.
Current limits & next steps
Mufasa currently supports BIP84 single-signature wallets and assumes one process accesses a wallet database at a time. The database does not store the recovery phrase, but signing secrets supplied through process configuration are not encrypted key storage.
The logged roadmap starts with transaction preview and explicit confirmation before signing or broadcast: show the destination, amount, fee, change, and selected inputs, default to cancellation, and provide a deliberate automation override. The review boundary must also preserve safe change-address handling.
Other open issues cover a local SQLite recipient address book, Bitcoin Core fee estimation with a manual rate override, and manual UTXO selection or exclusion with an explicit selected-only policy. These are planned improvements, with acceptance criteria for address validation, estimation failures, conflicting inputs, and persistence.
A self-hosted service accessible from multiple clients remains a longer-term possibility, after the wallet’s transaction and recovery behavior is strengthened.