Ciphertext. That's it.
Opaque AES-256-GCM output. No coin names, no amounts, no cost basis. A stolen database is a stolen blob — attacker still has no key.
Starting…
Client-side encrypted — zero-knowledge by design
StealthLedger is a private portfolio tracker for crypto and cash — one screen for what you actually own. Your holdings are sealed with AES-256-GCM in your browser; the server stores an opaque blob it has no key to open.
Invite-only Signups need a one-time code from someone who already has one.
The whole idea, in one picture
Same data. Two views. The one on the left is the only one that ever leaves your device.
Opaque AES-256-GCM output. No coin names, no amounts, no cost basis. A stolen database is a stolen blob — attacker still has no key.
Rendered client-side from a locally decrypted vault. The server watched the ciphertext go by and learned nothing.
Sample numbers. Your vault, your keys, your privacy.
Most trackers only see crypto. Yours knows about your bank accounts too, so the number at the top is what you actually have.
AES-256-GCM with a key derived from your vault passphrase via scrypt. The plaintext never leaves your device — not to the server, not to a CDN, not to logs.
Face ID, Touch ID, Windows Hello, or a hardware key. WebAuthn PRF becomes your login secret — nothing to type, nothing to phish, nothing for an attacker to grind against.
Every write is bound to a monotonic sequence and authenticated with the same key that decrypts it. Rollback and cross-vault splice attacks both fail the same authentication check.
A stolen database gives an attacker no shortcut. The server holds a scrypt verifier and ciphertext; the key that unlocks either only ever lives in your device's memory.
Bring your Koinly holdings in as a live section — read-only, on your device, cached against your API key. Or don't; the app works entirely with hand-entered holdings if you'd rather nothing third-party ever sees your ledger.
Track crypto and cash in one view, priced in your base currency (AUD, USD, whatever). Bank balances live next to XMR bags; the grand total is one number.
See it in action
This is the actual app after unlock. Every number below was rendered client-side from a locally decrypted vault. The server watched ciphertext go by.
How it works
No email, no exchange logins, no wallet addresses. Sign up, add what you hold, and Face ID keeps it locked.
Under the hood
Standard primitives, boringly configured. Nothing custom, nothing clever — the encryption layer and threat model are open and published for review.
FAQ
Yes, at the level we can prove in the browser. Your holdings are encrypted on your device with AES-256-GCM before they ever leave. The server sees only a blob of ciphertext plus a monotonic sequence number. The key never touches the network.
Default to passkey. Your device (Face ID, Touch ID, Windows Hello, a hardware key) holds a private key that never leaves it; on login the browser asks the device for a per-site secret and we stretch that with the same scrypt KDF the passphrase path uses. Nothing to remember, nothing to phish.
Pick passphrase if you'd rather memorise a phrase, or if you plan to log in from browsers where passkey roaming is awkward.
There are actually two secrets, doing two different jobs. Login proves who you are to the server so it hands over your encrypted file. Unlock then decrypts the file inside your browser — the server never sees the unlock key. Both can be passkeys, or one passkey and one passphrase, or both passphrases. The most common setup is: login passkey + unlock passkey, so a normal visit is a single Face ID prompt.
Your vault passphrase stays as a fallback either way — it's what protects your holdings if you ever lose every device that holds a passkey.
Because they do different jobs and only one of them can involve the server.
So there can be up to four credentials on a full account: a login passkey, a login passphrase, an unlock passkey, and a vault passphrase. Most people set one on each side and forget about it. Settings groups them under “Login — how you prove it’s you” and “Unlock — how the vault decrypts” for exactly this reason.
Rule of thumb: if a screen is titled Log in, it wants a login credential. If it’s titled Unlock your vault, it wants an unlock credential. Adding a passkey on one side doesn’t add it on the other — they’re independent.
You added it on one side; the other side doesn’t have one yet. The most common case: you added a login passkey, signed in with a tap, and then the unlock screen asked for your passphrase because the vault itself doesn’t know about a passkey yet.
Fix it in Settings → Unlock — how the vault decrypts → Add passkey for unlocking. That wraps the vault key with a passkey-derived secret and appends it to the vault’s unlockers, so next time the unlock screen offers a passkey button instead of the passphrase field.
Restoring a backup carries over whatever unlockers the backup had, which is usually just the original passphrase. Add an unlock passkey once, after restore, and you’re done.
If your passkey syncs (iCloud Keychain, Google Password Manager, 1Password, etc.) it comes back with your account. If it doesn't — say it lived only on one hardware key — the account is stuck. StealthLedger can't reset it because it has no shared secret to reset to.
Practical mitigations, in order of effort:
Right now, the account is stuck. If we later add an email-based reset it will only reset the login, not decrypt anything — the vault contents are still gated by the vault passphrase.
A backup file is your encrypted vault as it was on the day you exported it: same vaultId, same unlockers, same holdings. Restoring it into a new account overwrites whatever vault that account currently has and hands you back the old one. You’ll unlock it with the backup’s vault passphrase or recovery code — not the passphrase you just used to sign up.
The usual flow when moving between accounts or coming from the old single-tenant version:
If the restore screen refuses your file, the fastest reset is Settings → Forget access token, log in again with the account passphrase, then retry.
No — the Koinly import is a bookmarklet you drag onto your desktop browser's bookmarks bar, and mobile browsers don't have one. Run the import once on a laptop or desktop and the resulting Koinly section syncs to your vault; from then on you can view it on mobile like any other section.
The server never stores your login secret. It stores a scrypt verifier with N=65536, r=8, p=1 — about 64 MB of memory per guess. A stolen database gives an attacker no help decrypting any vault; vault contents need the vault passphrase, which the server has never seen.
Your holdings are gone. Not "wait a while" gone — gone. There is no reset flow because there is no reset key. Set up a recovery code when you create the vault; that's the escape hatch.
Because the site serves you the JavaScript that touches your vault passphrase, a determined operator could push a poisoned build. We mitigate that with a strict Content-Security-Policy (no inline scripts, no third-party origins), a small hand-written codebase, and a published crypto spec you can diff against what your browser loads. The application code itself is proprietary; the encryption layer, threat model, and audit-log format are open for review.
Free while we’re in preview. There’s no ad tier, no data resale, and no plan to add one — the whole product only works if the server can’t read your vault. If it stays around long-term it’ll be a small optional subscription so hosting pays for itself; the free tier and the encryption model don’t change.
No. You type in what you hold (or paste it from Koinly), and we fetch unit prices only — the same anonymous quote your browser can already get from CoinGecko. We never see, ask for, or store your exchange logins, wallet addresses, or API keys.
Yes. StealthLedger runs in any modern mobile browser — Safari, Chrome, Firefox — and installs to your home screen like a native app (“Add to Home Screen”). Face ID / Touch ID passkeys give you a one-tap unlock. The only thing you can’t do on mobile is the Koinly bookmarklet import; run that once on a laptop and the result syncs everywhere.
Same idea, narrower scope. StealthLedger is a purpose-built encrypted spreadsheet for crypto holdings — fewer features, smaller codebase. Bitwarden and 1Password are fully open source; StealthLedger publishes its crypto layer and threat model rather than the whole app.
Yes, any time. Settings → Account → Danger zone → Delete my account. Type your username to confirm. It’s a hard delete — encrypted vault, sessions, audit log, all of it. No grace period, no undo. Export your data first from Settings if you want a copy.
Yes. Settings → Export encrypted vault downloads your holdings as an encrypted file you can restore later, and Delete account is a real delete — there’s nothing to hold on to anyway, since we can’t read what we’re storing.
None. No trackers, no pings, no third-party scripts.
Support the project
StealthLedger has no VC, no ads, no tracking, no upsell tier. If it saves you an hour or a headache, an XMR tip keeps the servers up and the roadmap ours to write.
Signups are invite-only right now. If a friend already has an account, ask them to mint you a one-time invite from their admin panel — the link pre-fills the code so you're two minutes and one passkey prompt away.
Paste the access token for your vault. This only lets the device reach the encrypted file — it can't decrypt anything on its own.
It's the VAULT_ACCESS_TOKEN you set in your Netlify
environment variables. Opening the install link from your desktop
(#t=…) fills this in automatically and then wipes it from
the address bar.
Your encrypted holdings, on a server that can’t read them.
Welcome back, . Your session expired — log in to reach your vault. Not you? Sign in to a different account
New here? Create an account · How does this work?
Welcome to the StealthLedger private beta. You’re one of a small group testing the privacy-first crypto tracker — your feedback shapes what ships publicly.
Pick a username, tap once with Face ID / Touch ID / Windows Hello, and write down the recovery code we show you. That's it.
Already have an account? Log in · How does this work?
Paste your operator token. Stored in this tab only.
Loading…
Pick who can create new accounts. Changes take effect immediately for new visitors — existing users are unaffected.
SIGNUP_MODE env var takes over again.
Open a capped signup window — for example, ‘50 signups, no code needed’. Set the mode above to Preview window to make this the gate for new signups. Two shapes:
PREVIEW-2026) that users must type. Same cap + one-per-IP guardrails.Rotating or clearing the code doesn’t wipe the redeemers list — use Reset redemptions to open a fresh pool of slots.
Signup link — auto-fills the code:
Loading…
Monero-only for now. Empty this field to hide the donate card on
the FAQ page. Standard mainnet addresses start with 4
or 8 and are 95 characters long; testnet or unusual
address types are rejected.
CoinGecko is called at most once per fresh window per coin+currency combo, shared across every open tab and every function instance via the blob store. If CoinGecko rate-limits us (HTTP 429), we keep serving the last good price for up to the stale window before showing an error — nobody’s screen goes blank. All knobs here take effect within ~30 seconds; no redeploy needed.
Loading cache stats…
Loading…
Signed in as . Pick how you'll unlock this vault on this device — your face, your fingerprint, or a passphrase you remember.
Everything is encrypted on this device before it's uploaded. Nobody operating the server can read it — including us. That also means if you lose your unlock method AND your recovery code, it's unrecoverable.
Your recovery code. It's the only way back in if you forget the passphrase and lose your passkey. It is shown once and never stored anywhere in readable form.
Your data is decrypted locally. Nothing on our servers can read it without your passphrase or passkey.
Signed in as
or
Deriving key…
—
The app code on the server has changed since this device pinned it. Nothing new runs until you accept it.
A short hash of the app files this device has pinned. If it changes unexpectedly, nothing new runs until you accept the update — so a silent replacement of the code between logins is visible here.
The value is computed and shown by the app itself, which means it detects unexpected changes rather than proving any single load was safe.
Add some holdings first, and this chart will populate.
No priced crypto yet.
No private coins yet.
Total —
Read-only here. To refresh, open Settings → Koinly import and click
your bookmarklet from an app.koinly.io tab. Your Koinly
cookies never leave that tab.
Total —
No cash entries yet.
Total —