Line coverage of the Rust test suite, measured by .github/workflows/coverage.yml (cargo-llvm-cov) on every push to main and published to Codecov.
Wallets for the Rand Protocol RAND chain (the fully shielded pool served by
rand-node), one per platform, sharing one Rust core:
| client | language | directory | ships as | can send |
|---|---|---|---|---|
| iOS | Swift (SwiftUI) | ios/ |
TestFlight / App Store | on a device with about 8 GB of memory; otherwise through the RandProtocol provers or your own |
| Android | Java | android/ |
Google Play (.aab), .apk |
on a device with about 8 GB of memory; otherwise through the RandProtocol provers or your own |
| Chrome | JavaScript, Manifest V3 | chrome/ + extension/ |
Chrome Web Store | through the RandProtocol provers or your own |
| Firefox | JavaScript, Manifest V3 | firefox/ + extension/ |
addons.mozilla.org | through the RandProtocol provers or your own |
| Windows, Linux, macOS | Tauri (shared UI + Rust core) | desktop/ |
.msi / .AppImage, .deb, .rpm, .tar.gz / .dmg | yes, and proves for your other wallets |
| Local web wallet | JavaScript (shared UI + WebAssembly core) | web/wallet/ + ui/ |
nothing — served from a checkout | through the RandProtocol provers or your own |
The two extensions, the desktop app and the local web wallet share more than the core: ui/ is one
copy of the whole interface — the screens, the app shell and router, the design tokens, and
ui/engine/ (the vault, the JSON-RPC client and the scan/send orchestration). Each of them is only
a shell around it, saying how to store a key and how to reach the core. iOS and Android have their
own native UIs over the same core/.
Every client creates a wallet (spend key → viewing key → rand1… address), scans the
commitment tree for its own notes, asks the testnet faucet, and hands the user the viewing key
and per-transaction keys that randscan.org opens confidential
transactions with. The shells that can fit a proof in memory — the desktop app and the mobile
apps — also prove and submit shielded transfers of RAND and of any listed RPL token; the browser
extension and the web wallet build, authorise and submit the same transfers but have the bundle
proof made by a prover: by default the RandProtocol provers (four machines run by the
validators under prover.randprotocol.org, each with its own key pinned in the wallet, no fee;
the wallet says what they see before the first send), or one you pair for privacy — the desktop
app on the same machine, or your own rand-prover (see the known limitation below and
docs/prover.md). A phone without the memory does the same. The extensions
also give sites a window.rand provider: randbridge.org connects to it, and durian.market swaps
through window.rand.invoke (a program call the wallet shows, proves and submits on Approve);
the wallet's own Swap screen trades through the same durian.market pools.
Downloads are listed at https://randprotocol.org/clients (web/).
Design: docs/superpowers/specs/2026-09-13-rand-wallet-clients-design.md.
Every release is on GitHub Releases,
and https://randprotocol.org/clients links the same files with their checksums. <v> below is
the version, 0.8.0 today.
| you have | download | then |
|---|---|---|
| macOS, Apple silicon | rand-wallet-<v>-macos-arm64.dmg |
open it, drag Rand Wallet to Applications |
| macOS, Intel | rand-wallet-<v>-macos-x64.dmg |
the same |
| Windows 10 or 11 | rand-wallet-<v>-windows-x64.msi |
run it; …-windows-x64-setup.exe is the same app in an .exe installer |
| Debian, Ubuntu | rand-wallet-<v>-linux-x86_64.deb |
sudo apt install ./rand-wallet-<v>-linux-x86_64.deb |
| Fedora, openSUSE | rand-wallet-<v>-linux-x86_64.rpm |
sudo dnf install ./rand-wallet-<v>-linux-x86_64.rpm |
| any other Linux | rand-wallet-<v>-linux-x86_64.AppImage |
chmod +x it and run it |
| a Linux none of those fit | rand-wallet-<v>-linux-x86_64.tar.gz |
the bare binary; its INSTALL says where the files go |
| Linux on arm64 | the same four, named …-linux-arm64.… |
|
| Chrome, Edge, Brave | rand-wallet-chrome-<v>.zip |
unzip; chrome://extensions → Developer mode → Load unpacked |
| Firefox | rand-wallet-firefox-<v>.zip |
about:debugging → This Firefox → Load Temporary Add-on… → the zip |
| Android 8 or later | rand-wallet-<v>-android.apk |
open it on the phone and allow the install, or adb install |
| to build it yourself | rand-wallet-<v>-source.tar.gz |
this repository and the submodule in one archive; see Build from source |
The Linux desktop app is the same interface the browser extension shows — one ui/ directory
drives both — in a native window, and it is the one that proves.
iOS has no download: Apple installs an app only from the App Store or TestFlight, and the listing is pending. Until then build it in Xcode (below). The store listings for Android, Chrome and Firefox are pending as well; the files above are the same builds that are submitted.
SHA256SUMS is attached to every release. Download it next to the file and compare:
shasum -a 256 -c SHA256SUMS --ignore-missing # macOS
sha256sum -c SHA256SUMS --ignore-missing # LinuxGet-FileHash .\rand-wallet-0.8.0-windows-x64.msi -Algorithm SHA256 # Windows: compare with the line in SHA256SUMSA matching sum says the file is the one that was published. It does not say the file was built from this source: the builds are not bit-for-bit reproducible yet, so if that is the question you are asking, build it yourself.
Nothing here is signed with a paid developer certificate yet, and each OS says so in its own words. None of these messages is about the file being damaged; check the sum, then:
- macOS — the app is ad-hoc signed and not notarised, so the first launch says Apple could
not verify it. Open System Settings → Privacy & Security, scroll to the message about Rand
Wallet and choose Open Anyway; or remove the quarantine flag yourself:
xattr -dr com.apple.quarantine "/Applications/Rand Wallet.app". - Windows — SmartScreen shows "Windows protected your PC". Choose More info → Run anyway. The installer adds the WebView2 runtime if the machine does not have it.
- Android — the
.apkis signed with this project's upload key (certificate SHA-25675:22:2E:BF:22:D3:95:80:A5:11:2A:AC:B7:EC:2E:03:93:91:07:77:96:E0:45:1D:7B:CF:E3:7E:FA:06:53:52); the phone asks you to allow installs from the app you opened it with. A later install from Google Play is signed by Play's own key, so Android will ask you to remove this one first — export your key file from Settings before you do. - Firefox — a temporary add-on is removed when Firefox restarts; release Firefox installs permanently only what addons.mozilla.org has signed.
The chain has no accounts and no signatures: a transfer is a 2-in-2-out bundle authorised by a
STARK proof, keys are Poseidon2 hashes, envelopes are ML-KEM-768 + ChaCha20-Poly1305. Those
primitives exist only in the fullnode's Rust crates, and a wallet that re-implemented them in
Swift, Java or JavaScript would have to be byte-identical to the node or every transfer is
refused. So core/ vendors the fullnode crates (core/vendor/fullnode,
a submodule at fullnode's v0.8.0 tag c0878c3 (constraint set 9, the randprotocol-msig signer checks with the payee check a signer runs, and the verified fold in the verify cache), chain 21 onward: split authorisation, where a transaction carries a bundle proof made from the viewing key and a small authorisation proof the wallet always makes itself from the spend key; BIND-1, a transaction bound to its chain's genesis; program state and the Invoke action (RPL-2); and the chain's bridge fee) and exposes one JSON entry point, call(method, params), that each
client wraps: an XCFramework on iOS, a .so on Android, WebAssembly in the browser. Everything
above that line — the RPC client, note store, scan and send flow, key storage and the UI — is
Swift, Java and JavaScript.
The public endpoint is https://rpc.randprotocol.org — a synced chain-21 node behind a
CORS-open reverse proxy, and the default in every client. Settings keeps one editable "RPC URL"
that replaces it while filled in (a saved URL is probed before it is kept: an unreachable or
wrong-chain node is refused rather than saved); clearing the field goes back to the default.
More public endpoints are planned (rpc1/rpc2/rpc3.randprotocol.org), and the clients
already fail over between whatever set they ship with — standing them up one at a time needs no
client change. To run one, put a reverse proxy with CORS in front of one synced node's RPC
(every node binds JSON-RPC to 127.0.0.1:8545).
docs/rpc-endpoints.md is the whole recipe — Caddy, CORS, POST-only,
request-size and rate limits, and what to front. The short version:
rpc1.randprotocol.org {
@rpc method POST
header Access-Control-Allow-Origin *
header Access-Control-Allow-Headers content-type
@preflight method OPTIONS
respond @preflight 204
reverse_proxy @rpc 127.0.0.1:8545
}
To use your own node instead, point a client at one you can reach (an SSH tunnel to a droplet
works: ssh -N -L 8545:127.0.0.1:8545 root@<node> and RPC URL http://127.0.0.1:8545; on
Android use 10.0.2.2 from the emulator).
A bundle proof (what authorises a transfer — of RAND, of an RPL token, or a bridge burn; they
are all the same one proof) peaks at about 6.2 GB of memory, measured with core/crates/wallet-core/examples/prove_fixture.rs:
cd core && cargo build --release --example prove_fixture
/usr/bin/time -l target/release/examples/prove_fixture production # macOS; Linux: /usr/bin/time -vConsequences today, per shell: the desktop app proves natively and sends everything; the browser
extensions and the local web wallet cannot prove for themselves (WebAssembly is capped at 4 GB), so
they prove through a prover — by default the RandProtocol provers, or one you pair: the desktop
app on the same machine (Settings → Prove for my other devices), or your own rand-prover on
a server. With none (Settings → Prover → none) the Send screen explains the wall. A prover receives the wallet's viewing key and a one-time salt with every job —
it can read that wallet's whole history and cannot spend; the spend key stays on the device, which
makes the small authorisation proof itself — so it need not be yours, though running your own is
what keeps your history to yourself; docs/prover.md is the whole guide. Phones with less than
about 8 GB of RAM will have the app terminated mid-proof (the review step warns with the device's
numbers), so they too send through the RandProtocol provers or your own rand-prover over
https (Settings → Prover, docs/prover.md §6). Every other feature — creating and importing wallets, receiving, scanning, the faucet,
activity, viewing keys and per-transaction keys for randscan.org — works on all five shells
(iOS, Android, the browser extension, the desktop app and the web wallet), and the whole send
path is implemented and tested against the chain's own verifier in the core. The fix is in the
prover (randprotocol-zkvm: it materialises every table's low-degree extension at once); when
its peak drops, update PROVER_PEAK_MEMORY_BYTES in core/crates/wallet-core/src/lib.rs and
the two mirrored constants in the mobile apps, rebuild, and the Send flows light up unchanged.
Until then, a phone without the memory sends through a prover, or from the desktop app or the
rand command-line wallet using the key file every client exports.
Everything that is published is built from this repository by the commands below. Pick the client you want; each one starts from the same checkout.
| for | you need |
|---|---|
| every client | git; rustup (it installs Rust 1.98.1, the version pinned in core/rust-toolchain.toml, the first time cargo runs here); Node 22 or later |
| desktop, any OS | cargo install tauri-cli --version "^2" --locked |
| desktop on Linux | sudo apt install build-essential libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf rpm file (Debian and Ubuntu names; other distributions carry equivalents) |
| desktop on Windows | Visual Studio Build Tools with "Desktop development with C++"; Git for Windows, whose Git Bash runs the scripts |
| desktop on macOS | Xcode Command Line Tools: xcode-select --install |
| Chrome, Firefox, the web wallet | nothing more: the build installs wasm-bindgen-cli itself |
| Android | JDK 17 or later; the Android SDK with platform 36, build-tools 36 and NDK 27 (sdkmanager "platforms;android-36" "build-tools;36.0.0" "ndk;27.2.12479018") |
| iOS | a Mac with Xcode 16 or later |
The first build of any client compiles the prover and takes several minutes.
git clone --recurse-submodules https://github.com/randprotocol/clients.git
cd clients
git checkout v0.8.0 && git submodule update --init # a release, rather than mainor unpack rand-wallet-<v>-source.tar.gz from a release, which has the submodule in it already.
The submodule is the full node (core/vendor/fullnode, at its v0.8.0 tag): the wallet's
cryptography is the node's own code.
cd core && cargo test --release && cd .. # includes a real proof, checked by the chain's verifierDesktop — Linux, Windows, macOS
scripts/release/build-desktop.sh # → dist/release/v<version>/It builds the packages of the OS it runs on, under the names the releases use: on Linux an
AppImage, a .deb, an .rpm and the bare binary in a .tar.gz; on Windows (from Git Bash) the
.msi and the -setup.exe; on macOS the .dmg. --target x86_64-apple-darwin builds the Intel
dmg on an Apple-silicon Mac. To run it without packaging anything:
cd desktop/src-tauri && cargo tauri devdesktop/README.md has the details.
Chrome and Firefox extensions
core/scripts/build-wasm.sh # the core as WebAssembly → extension/shared/core/
chrome/pack.sh # → dist/chrome/ and dist/rand-wallet-chrome-<version>.zip
firefox/pack.sh # → dist/firefox/ and dist/rand-wallet-firefox-<version>.zipLoad dist/chrome with Load unpacked at chrome://extensions (Developer mode on), or
dist/firefox/manifest.json with Load Temporary Add-on… at about:debugging.
chrome/README.md, firefox/README.md.
Android
core/scripts/build-android.sh # the core → android/app/src/main/jniLibs/
cd android
echo "sdk.dir=$HOME/Library/Android/sdk" > local.properties # wherever your SDK is
./gradlew assembleDebug # → app/build/outputs/apk/debug/app-debug.apk
adb install -r app/build/outputs/apk/debug/app-debug.apkA release build (./gradlew bundleRelease assembleRelease) is signed with your own key:
android/README.md.
iOS
core/scripts/build-ios.sh # the core → ios/Frameworks/RandWalletCore.xcframework
open ios/RandWallet.xcodeproj # choose a simulator or your own device, then RunRunning on your own iPhone needs your Apple ID in Xcode → Settings → Accounts and your team
selected under Signing & Capabilities. ios/README.md.
The local web wallet
web/wallet/serve.sh # builds, then serves http://127.0.0.1:8787/npm ci --prefix ui
node --test "ui/test/**/*.test.mjs" "web/wallet/test/**/*.test.mjs" "extension/test/*.test.mjs"
# the shared interface, the web wallet, the extension
cd desktop/src-tauri && cargo test # the desktop app's own crate
cd android && ./gradlew testDebugUnitTest # Androidscripts/release/ holds the whole release build, and the three places it runs use the same
scripts:
.github/workflows/release.yml— on av*tag, GitHub's runners build the desktop app for macOS (arm64, x64), Windows and Linux (x86_64, arm64), both extension zips and the source tarball, and attach them to a draft release withSHA256SUMS.Jenkinsfile— the same pipeline for a Jenkins with agents labelledlinux,macosandwindows, as a backup.scripts/release/build-local.sh— on a Mac: the extension zips, the dmg, the signed Android.aaband.apk, an unsigned iOS archive and the source tarball. Android and iOS are built only here, because their signing keys are in no CI.
scripts/release/checksums.sh <tag> [files…] attaches more files to a release and rewrites
SHA256SUMS over everything in it. What each store then needs — the listing text, the review
notes, the privacy answers — is docs/store/README.md.
core/ Rust: wallet-core (library), wallet-ffi (C + JNI), wallet-wasm; vendored chain crates
ui/ the shared interface, one copy for every JavaScript client: screens/, the app shell
and router (app.js), design tokens and CSS, lib/ helpers, and engine/ — the vault,
the JSON-RPC client and the scan/send orchestration. gallery.html and dev.html run
it against a fake backend; test/ is its suite (node --test ui/test)
ios/ xcodegen project.yml → RandWallet.xcodeproj; SwiftUI app
android/ Gradle project; Java app
extension/ the extension's code, one copy for both browsers
chrome/ Chrome manifest, packaging, store notes
firefox/ Firefox manifest, packaging, store notes
web/ the icon for the randprotocol.org /clients pages (the pages themselves live in
that repository: web/README.md), and wallet/ below
web/wallet/ the local web wallet: index.html and main.js (the shell), worker.js (the wasm core
off the UI thread), idb.js (IndexedDB, and a Map for what must never be written),
and serve.mjs — a loopback-only static server. Built and served by serve.sh from a
checkout; it is not deployed anywhere, and it sends through a prover
(docs/prover.md)
design/ tokens.json, make-icons.py, generated icons
docs/ prover.md (proving through a prover), rpc-endpoints.md, design specs
desktop/ Tauri app for Windows, Linux and macOS: ui/ in a webview, wallet-core linked
directly — the one client that can prove a transfer locally, and a prover for
the extension and the web wallet on the same machine
linux/ macosx/ windows/ per-OS packaging notes pointing at desktop/
scripts/release/ the release build: build-desktop.sh, build-local.sh, source-tarball.sh,
checksums.sh — run by .github/workflows/release.yml, by the Jenkinsfile, and by hand
docs/store/ what each store upload needs: listing text, review notes, privacy answers
PRIVACY.md the privacy policy the store listings link to (randprotocol.org/clients/privacy)
TERMS.md the terms of service they link to (randprotocol.org/clients/terms)
Every client speaks English, Russian, Mandarin (Simplified Chinese), Cantonese (Traditional Chinese, Hong Kong), Korean, Indonesian, Malay, Japanese, Arabic, Persian, Spanish, Portuguese, German, French, Italian and Polish, and lays out right to left for Arabic and Persian. Where the strings live, and how to add a language:
| client | strings | guide |
|---|---|---|
extension, web, desktop (the shared ui/) |
ui/locales/<code>.js, generated en.js |
ui/locales/README.md |
| extension name and description | extension/shared/_locales/<dir>/messages.json |
extension/shared/_locales/README.md |
| Android | android/app/src/main/res/values-<lang>/ |
android/LOCALIZATION.md |
| iOS | ios/RandWallet/Localizable.xcstrings, InfoPlist.xcstrings |
ios/LOCALIZATION.md |
Amounts are written with ASCII digits and a decimal point in every language, so what the wallet shows can be pasted back into a field or a command line.
Experimental testnet software, like the chain itself: not audited, not for real value.
This repository is GPL-3.0-only; the full text is in LICENSE. Every client links
the fullnode's crates, which are GPL-3.0-only, so the wallets that link them are too.
Third-party components keep their own licences: the Inter and JetBrains Mono fonts in ui/fonts/
are under the SIL Open Font License 1.1, and the vendored node and circuits crates under
core/vendor/ stay under their own terms.