QSDM Wallet
Generate a quantum-secure (ML-DSA-87, NIST FIPS 204) wallet entirely
inside this browser tab. The private key is created locally, encrypted with
AES-256-GCM under a passphrase you choose, and offered as a
downloadable JSON keystore. Nothing about the keypair touches the QSDM
validators, this site's web server, or any third party. Compatible
byte-for-byte with qsdmcli wallet.
ML-DSA-87 CSPRNG keygen, AES-256-GCM
encryption, HMAC fallback entropy, JWT verification and mTLS
are pinned by 5 rows in the cryptography
category of the public audit checklist, all passing. The
REST/JSON contract for the endpoints the wallet calls is
pinned by 6 rows under api. See
audit.html?category=cryptography
for the live view.
Read this before clicking anything
1) The QSDM wallet is the only thing standing between you and your coins. If you lose the JSON keystore file or forget the passphrase, the address is permanently unrecoverable — no support team, no reset email, no validator override. Back up the file. Pick a passphrase you will not forget.
2) Verify you are on qsdm.tech (look at the address bar) before
generating. A phishing clone of this page could replace the WASM binary and
exfiltrate the private key the moment it's generated. Even with HTTPS, treat
the wallet page like a hardware-wallet recovery screen.
3) The same keystore format is produced by the offline CLI: qsdmcli
wallet new. If you'd rather not trust a webpage, use the CLI on a
machine you control — the keystore opens here either way.
Game account linking (Sky Fang)
Sky Fang is the play-to-earn MMORPG experience. The earn-only CELL path is the wallet-link flow: players link their Sky Fang account to QSDM Hive so CELL eligibility can be proven without selling combat power.
The normal player flow uses QSDM Hive, not manual
public-key or signature copy-paste. Sky Fang opens
qsdm-hive://skyfang-link; Hive shows the exact challenge,
signs it locally with your QSDM ML-DSA-87 wallet, and returns the proof to
Sky Fang. Your private key never leaves Hive.
The Advanced → Sign message tab below is kept for developer and emergency diagnostics. It is not the recommended Sky Fang player flow.
Your QSDM Wallet
No wallet is installed in this browser yet. Pick one:
The vault is stored encrypted in this browser's
localStorage (AES-256-GCM with a PBKDF2-SHA-256
600 000-iteration key derived from your passphrase). It
never leaves this tab. Clearing site data or switching
browsers wipes it — export a backup
wallet.json from the Advanced → Open tab
any time.
Unlock Your Wallet
Address:
Your QSDM Wallet
Address ·
Sign & submit a CELL transfer from this unlocked wallet. The signing happens in-page (ML-DSA-87 via wallet.wasm); only the already-signed envelope is POSTed.
Share only the address below with senders. The passphrase and the keystore must never leave this tab.
Local-only log of sends submitted from this browser. Lives
in localStorage; clearing site data wipes it.
The validator's permanent record is in
/api/v1/transactions.
Auto-lock
The vault re-locks after this many minutes of inactivity,
or when you close the tab. Set to 0 to disable
idle auto-lock (the tab-close lock still applies).
Export & backup
Download a copy of the encrypted keystore. Lose access to
this browser? Use Advanced → Open with the
wallet.json + passphrase to restore.
Forget this wallet
Wipe the encrypted vault from this browser. Make sure you have a backup first — this cannot be undone.
Create a new wallet
Pick a strong passphrase (12+ chars, mix of types). The
ML-DSA-87 keypair is generated by wallet.wasm
in this tab; the encrypted keystore is then stored in
localStorage — no server upload.
Import existing keystore
Pick a wallet.json file (produced here or by
qsdmcli wallet new). The passphrase is verified
in-page; if it checks out, the keystore is copied into
localStorage for future unlocks.
Advanced (legacy tab UI)
The original five tabs. Useful for: generating a keystore without storing it in this browser, signing an arbitrary message, inspecting a third-party keystore, or doing an address-only balance lookup. The persistent wallet panel above reuses the same cryptographic primitives.
Generate a fresh wallet
Choose a strong passphrase (12+ characters, mix of types). The wallet
page never sees the passphrase in network traffic: it is fed to
PBKDF2-HMAC-SHA-256 (600 000 iterations) inside this browser
tab, the derived key encrypts the private key under AES-256-GCM,
and the resulting JSON keystore is yours to download.
Open an existing keystore
Drop in a wallet.json file (produced here, or by
qsdmcli wallet new). The browser will decrypt it locally to
confirm the passphrase is correct and the file isn't tampered with.
Nothing is uploaded.
Sign a message
Decrypt the keystore and produce a ML-DSA-87 signature over
an arbitrary message. The decrypted private key is held only inside the
WASM call; the browser zeros the JS reference as soon as the signature
is returned.
Check an address balance
Unlike the other three tabs, this one talks to the network.
It sends a single GET https://api.qsdm.tech/api/v1/wallet/balance?address=<addr>
and renders the response. An address is public information — it's already on
chain — so the request leaks no private material, but it does correlate
this browser with this address at the validator's HTTP log
layer. If you want to avoid that correlation, ask a friend's node, run your own
validator, or skip this tab entirely.
Send a transaction (self-custody)
This is the only tab that spends from your wallet.
Decrypt your keystore with the passphrase, fill in the recipient
and amount, click Send. The transaction is built and signed
inside this browser tab with your ML-DSA-87
private key — the private key never leaves the tab — and only
the already-signed envelope is POSTed to
api.qsdm.tech/api/v1/wallet/submit-signed. The
validator verifies the signature against the embedded public
key and enforces sender == hex(sha256(public_key)),
so the validator can never spend on your behalf.
v0.4.1 replay protection: every send carries a
per-account nonce that the validator atomically
bumps inside the same transaction as the balance debit. Replays
(same envelope, same nonce, twice) return HTTP 409
nonce_replay rather than double-spending. The
Nonce field below auto-resolves from the validator before
sign-time; you can override it manually for off-line ceremonies.
How this works
- Keypair generation: a
~3 MBGo WebAssembly module (wallet.wasm) runs the same cloudflare/circlML-DSA-87implementation that QSDM validators use to verify your transactions. It calls the browser'scrypto.getRandomValuesfor the keygen entropy. - Encryption: the browser's
WebCryptoAPI (crypto.subtle) derives the AES-256 key withPBKDF2-SHA256(600 000 iterations, 16-byte salt), then encrypts the private key underAES-256-GCMwith a fresh 12-byte nonce. The on-disk format is identical topkg/keystore(Go) so the CLI and browser can swap keystores freely. - Address derivation: the QSDM address is
hex(sha256(public_key)). Same shape as the validator-sidepkg/walletderivation. - Bounded network surface: the Generate / Open / Sign
tabs only fetch the three static files
(
wallet.wasm,wasm_exec.js,wallet.js). They never POST the passphrase, the private key, the public key, or even the address. The Check balance and Send transaction tabs are the two exceptions — Balance does a single read-onlyGETagainstapi.qsdm.techwith the address in the query string, and Send does a singlePOSTto/api/v1/wallet/submit-signedwith the already-signed transaction envelope (the body contains your public key + signature + amount + recipient; never the private key or passphrase). Both happen only after you click their button. Confirm in DevTools → Network. - WASM build version: loading…
Or do it from the CLI
Identical keystore format, but offline. Useful for cold-storage flows where
you don't want even a static webpage in the trust chain. Consumers should
import the keystore into QSDM Hive and run eligible tasks there. Advanced
operators can use qsdmminer-console --protocol=v2 against the
live NVIDIA-locked mainnet; no separate consumer GUI miner is shipped, and
the legacy CPU qsdmminer binary is no longer a public release
artefact (mainnet rejects its v1 proofs at consensus — see
MINER_QUICKSTART).
# Build once
git clone https://github.com/blackbeardONE/QSDM
cd QSDM/QSDM/source
go build -o qsdmcli ./cmd/qsdmcli
go build -o qsdmminer-console ./cmd/qsdmminer-console
# Wallet: generate (passphrase prompted, address printed to stdout)
./qsdmcli wallet new --out ~/.qsdm/wallet.json
ADDR=$(./qsdmcli wallet show | awk '/^address/{print $2}')
# Mining (v2): generate the HMAC key, enroll on-chain (10 CELL bond),
# then start the live miner against the mainnet validator.
./qsdmminer-console --gen-hmac-key=$HOME/.qsdm/hmac.key
./qsdmcli enroll --sender=$ADDR \
--node-id=rig-77 \
--gpu-uuid=$(nvidia-smi --query-gpu=uuid --format=csv,noheader | head -1) \
--hmac-key=$(cat $HOME/.qsdm/hmac.key) \
--nonce=<your-account-nonce>
./qsdmminer-console --protocol=v2 \
--validator=https://api.qsdm.tech \
--address=$ADDR \
--hmac-key-path=$HOME/.qsdm/hmac.key \
--node-id=rig-77 \
--gpu-uuid=$(nvidia-smi --query-gpu=uuid --format=csv,noheader | head -1) \
--gpu-arch=ada