Wrapper crate for the EmVault multi-signature custody architecture.
See the CHANGELOG for release notes.
It provides a unified, backend-agnostic foundation for building secure m-of-n federations that can mix consumer hardware wallets (Trezor, Ledger, etc. — any signer that can export a BIP-48 XPUB and sign a P2WSH sortedmulti or Taproot multi_a PSBT) with HSMs (via PKCS#11) in the same federation. EmVault handles descriptor construction, heterogeneous signing coordination, federation mutation, efficient multi-account migrations, and recovery templates — while staying a pure library with no runtime or network ownership.
It also carries the handful of framework-agnostic utilities that every app needs but that don't belong in emvault-core (env-var + hex helpers).
Want to see emvault wired into a real application? Two reference web apps
exercise the library end-to-end and are the fastest way to learn the API:
-
test-app-xpub — self-custody, multi-party signing. Users bring their own hardware wallet, onboard an XPUB, and co-sign m-of-n P2WSH or Taproot transactions through a proposal lifecycle.
-
test-app-pkcs11 — custodial HSM-backed wallets. A federation of HSMs signs every spend server-side, across both Bitcoin and Elements/Liquid.
Each repo ships a FEATURES.md that maps every capability to the source symbol
that implements it. Point your AI coding agent at a demo repo and its
FEATURES.md for a complete implementation guide to the architecture — it has
everything an agent needs to understand the full feature surface, data flow, and
where each piece lives in the code.
| Crate | Role | Repository |
|---|---|---|
emvault-core |
Backend-agnostic core: Signer trait, Federation, descriptor builder, PSBT pipeline, migration, recovery, snapshots, roster, the shared wallet view types. |
https://github.com/gmikeska/emvault-core |
emvault-xpub |
XPUB signer backend for consumer hardware wallets (Trezor, Jade, Ledger, Coldcard, Passport). | https://github.com/gmikeska/emvault-xpub |
emvault-pkcs11 |
PKCS#11 / HSM signer backend. | https://github.com/gmikeska/emvault-pkcs11 |
emvault-elements |
Elements/Liquid support: confidential descriptors, PSET pipeline, client-side wollet, daemon RPC. | https://github.com/gmikeska/emvault-elements |
emvault-dev-signer |
Dev/CI HsmBackend that pairs with libemvault_dev_hsm. |
https://github.com/gmikeska/emvault-dev-signer |
emvault-esplora |
Nodeless Esplora + Waterfalls chain backend (sync/broadcast, no local node). Behind the esplora feature; re-exported as emvault::core::esplora. |
https://github.com/gmikeska/emvault-esplora |
emvault-electrum |
Descriptor-private electrs/Electrum chain backend (sync/broadcast + a scripthash watch layer). Behind the electrum feature; re-exported as emvault::core::electrum. |
https://github.com/gmikeska/emvault-electrum |
libemvault_dev_hsm |
Software-only PKCS#11 shared library for dev/CI — see HSM Integration for how HSMs connect to EmVault. | https://github.com/gmikeska/libemvault_dev_hsm |
emvault-core and emvault-elements both expose descriptor, error,
network, and federated_wallet modules and overlapping item names
(to_multipath_string, NetworkType vs ElementsNetwork, KeyMode vs
CtKeyMode). A flat pub use ::* of every backend would collide, so each crate
keeps its own namespace here, and the prelude gathers the
high-frequency, unambiguous types for the common case.
| Namespace | Always on? | Backed by |
|---|---|---|
emvault::core |
yes | emvault-core |
emvault::config |
yes | this crate (env-var + hex helpers) |
emvault::xpub |
xpub feature |
emvault-xpub |
emvault::pkcs11 |
pkcs11 feature |
emvault-pkcs11 |
emvault::elements |
elements feature |
emvault-elements |
emvault::dev_signer |
dev-signer feature |
emvault-dev-signer |
emvault::prelude |
yes | curated re-exports (feature-gated entries) |
Backends are optional dependencies behind features so a build pulls in exactly what it uses and nothing more (a Bitcoin-only app must not compile the HSM/Elements dependency stack):
| Feature | Effect |
|---|---|
xpub |
Consumer hardware-wallet signer (emvault::xpub). |
pkcs11 |
HSM-backed signer (emvault::pkcs11). |
elements |
Elements/Liquid support (emvault::elements); turns on emvault-core/elements and forwards the Elements feature into pkcs11 and xpub only if that backend is also enabled (weak ?/ dep), so both HSM and Liquid-capable xpub signers advertise Liquid. |
dev-signer |
Dev/CI HSM helper (emvault::dev_signer); implies pkcs11. |
esplora |
Nodeless Esplora + Waterfalls chain backend, re-exported as emvault::core::esplora (forwards emvault-core/esplora → the emvault-esplora crate). |
electrum |
Descriptor-private electrs/Electrum chain backend (sync/broadcast + scripthash watch layer), re-exported as emvault::core::electrum (forwards emvault-core/electrum → the emvault-electrum crate). |
elements-electrum |
Nodeless Elements/Liquid sync over Electrum (emvault::elements::nodeless); implies elements and turns on emvault-elements/electrum. |
elements-esplora |
Nodeless Elements/Liquid sync over Esplora + Waterfalls (emvault::elements::nodeless); implies elements and turns on emvault-elements/esplora. |
test-utils |
Exposes test scaffolding (MockSigner, fixtures, the Elements testkit) to downstream test suites. |
emvault::core and emvault::config are always available.
# Bitcoin-only consumer hardware wallets:
emvault = { version = "0.10", features = ["xpub"] }
# HSM federation with Elements + dev helpers:
emvault = { version = "0.10", features = ["pkcs11", "elements", "dev-signer"] }
# Custodial HSM wallets synced over a private Electrum server:
emvault = { version = "0.10", features = ["pkcs11", "electrum"] }One import for the types most consumers touch (backend-specific entries are feature-gated to match the enabled namespaces):
use emvault::prelude::*;
// Federation, DescriptorBuilder, build_federation, BuiltFederation,
// SigningCoordinator, UnsignedPsbt, FinalizedPsbt, NetworkType, …
// + ExternalSigner (xpub), Pkcs11Signer (pkcs11), ElementsWollet (elements)Framework-agnostic helpers shared by every consuming app — always available, no backend feature needed:
use emvault::config::{require, optional, hex_decode, hex_encode, ConfigError};
let secret = hex_decode(&require("APP_SESSION_SECRET")?)?;
let manifest = optional("TREZOR_MANIFEST_EMAIL");
# Ok::<(), emvault::config::ConfigError>(())cargo build # core + config only
cargo build --all-features # every backend
cargo test --all-features
cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic
cargo doc --no-deps --all-featuresMIT OR Apache-2.0.
