Zero npm packages in the interface. No JavaScript dependency tree, so no npm supply chain to poison.
Zcash · Desktop · Self-custody
A Zcash wallet
that keeps quiet.
zbag defaults to the Orchard shielded pool, routes wallet traffic through embedded Tor, and keeps keys in a Rust core. A desktop wallet for macOS, Windows, and Linux, in design preview.
The interface
▢ holds no keysFlutter UI
Renders derived display data. No webview, no npm packages.
The core
▣ keys + seedRust · librustzcash
librustzcash holds the keys and signs locally, on your own device.
Keystone
▣ offlineOptional air-gapped signer, spending keys can stay off the internet entirely.
Refused by design.
Most "desktop" wallets are a web page in a costume. zbag refuses the parts that widen the attack surface.
No embedded browser. The Flutter UI renders its own pixels natively, it is not a website in a window.
Not Electron. Your wallet should not ship a whole browser just to draw a balance.
Keys and seed stay in the Rust core or on your Keystone, on your own device.
THE RECEIPTS
Real npm packages. Recent dates. zbag's interface pulls in none of them.
A maintainer got phished, and about 18 tiny utility packages with roughly 2 billion downloads a week were rigged to rewrite crypto wallet addresses right inside the browser.
Socket.devThe first self-replicating worm on npm. Using stolen publish tokens, it trojanized more than 500 packages on its own and harvested secrets from every machine it reached.
StepSecurityAttackers hijacked TanStack's own release pipeline and pushed credential-stealing code into 42 Router and Start packages, so it shipped with valid provenance. It ran on install to grab cloud, npm, and crypto wallet secrets. A fresh wave of the same Shai-Hulud worm.
GitHub AdvisoryBooby-trapped versions of a package with more than 70 million downloads a week dropped a remote-access trojan, tied to a state-backed group that targets crypto wallets.
MicrosoftA volunteer talked his way into maintaining a popular package, then slipped in code aimed at one thing, draining a Bitcoin wallet app. The pattern is not new.
The RegisterThis removes one large, repeatedly exploited surface, not every risk. zbag's Rust core and Flutter SDK have their own dependencies, which carry their own duty of care. What the interface does not have is an npm dependency tree, or a JavaScript engine for this kind of attack to live in.
WHAT WE TURNED DOWN
A wallet UI has to run on macOS, Windows, and Linux. Here is the honest tradeoff behind each option, including the one we kept.
| Option | Verdict | Notes |
|---|---|---|
| Electron | Too heavy | Ships a full Chromium browser plus Node inside every app. Big downloads, heavy memory, and a wide surface you have to keep hardening. |
| Tauri | Inconsistent | Lighter, but it borrows each system's own webview, so the same UI renders in a different engine on every platform. On Linux (WebKitGTK) it lags behind with documented rendering and performance bugs, and it can feel like a browser in a frame, not a native app. |
| Native per-OS | Too costly | The best feel and the best accessibility, because you use each platform's own toolkit. The catch is a separate UI to build and maintain for every platform, the most effort by far. |
| Qt | Licensing | Genuinely polished and cross-platform. But the commercial license is a paid per-seat subscription, and the free path (LGPL or GPL) carries copyleft obligations like shipping Qt's source and allowing relinking. Check qt.io for current terms. |
| Flutter | Our pick | One codebase that draws its own pixels with its own engine, so the UI looks and works the same everywhere. No bundled browser, no npm dependency tree, free and open (BSD). The same codebase can ship a mobile build later. Honest caveat: its accessibility is not yet fully at native parity, but the team ships accessibility and semantics improvements in nearly every release, and the gap is closing. |
No stack is free of tradeoffs. We picked the one whose weak spot is improving fast, and whose strengths, one consistent and dependency-light UI across desktop and, later, mobile, fit a wallet.
We know these tradeoffs firsthand. zbag's own pre-alpha was built on Tauri, with an embedded Chromium (CEF) renderer and a React UI, the very embedded-browser approach the Flutter rewrite drops. It works, and it is now archived as public source, build from source, no prebuilt binaries, superseded by the beta this page describes.
Tauri alpha · archived source ↗Privacy, stated plainly.
Each line below is something zbag does by design, in plain language. No absolutes, only what the architecture supports.
- Shielded by default in the Orchard pool: amount, sender, and recipient are not visible on-chain for shielded sends.
- Wallet traffic is routed through embedded Arti Tor.
- If Tor is unavailable, zbag does not fall back to a direct connection.
- Your ISP sees Tor traffic rather than direct wallet-server requests.
- The lightwallet server does not receive your direct IP, though it still receives wallet requests.
- Keys and seed stay in the Rust core.
- Discreet display controls.
- Fail-closed behavior.
Built like an instrument.
A Rust core does the sensitive work. The interface works with derived, non-sensitive data, and every automation has a manual lever.
Shielded by default
zbag keeps funds in the Orchard shielded pool, and an amount is shielded before it can be sent. There is no transparent-send path in the wallet.
Fail-closed Tor
Routed through an embedded Tor client, no system setup. If Tor is unavailable, zbag does not fall back to a direct lightwalletd connection.
Air-gapped signing
Keystone hardware support over animated QR. Your spending keys can live on a device that never touches the internet, signing with PCZT.
Keys stay in Rust
Spending keys never reach the interface. Seed phrases appear only in explicitly permitted flows, not logged or persisted by the UI layer.
Built-in swap
Built-in swaps route through NEAR Intents and settle as native ZEC on mainnet. zbag treats swap-related transparent exposure as explicit, not hidden.
Desktop-first
A real desktop application, not a browser tab. The Flutter interface embeds no webview and pulls in no npm packages, which keeps the dependency surface small. macOS, Windows, and Linux.
Where your traffic goes.
A schematic of a shielded send, what each party along the path can read, from your own device out to the Zcash chain.
-
Step 01
Your own device
zbag
Balances, history, and memos in the clear, locally.
-
Step 02
Embedded Tor
Arti
Carries every wallet request, no fallback to a direct connection.
-
Step 03
Lightwallet server
lightwalletd
Receives requests over Tor, not your direct IP.
-
Step 04
Zcash chain
Orchard pool
Amount and parties hidden for shielded sends.
What everyone else sees.
Privacy is only real if you can name who is watching and what they get. Here is the read for each observer when a shielded send leaves your own device.
zbag has no transparent-send path. An amount is shielded into the Orchard pool before it can be sent, so every send leaves from the shielded pool. That makes the read above the only case, with no transparent send to put a recipient or amount on-chain in the clear.
Get on the list.
zbag is not public yet, and what you see here is a design preview. Join the waitlist for early builds, release news, and a heads-up before launch.