SECURITY & PRIVACY
Verifiable, not just promised
kunji is built so that the parts that matter can't betray you — and so you don't have to take our word for it. Every claim below maps to code you can read. Don't trust, verify: read the source →
- Your passkey — it never leaves your device.
- Your master key — generated and held only on your devices.
- Which apps you connect to, or your identities within them.
- Your per-app private keys — only signatures ever leave the vault.
- Any way to link your identities across different apps.
Zero-knowledge
Your vault is encrypted on your device with a key only you can derive. kunji's servers store ciphertext they cannot read.
When you choose a passkey, kunji runs it through Argon2id — a
memory-hard key-derivation function (≈256 MB, 4 passes) — with a random salt,
producing a wrapper key.
That wrapper key encrypts (AES-GCM) a randomly-generated
master key, and the master key encrypts everything else. The passkey and master
key are computed and used only on your device. kunji even authenticates
anonymously — no email, phone, or name.
Only a random anonymous id, the KDF salt, the encrypted master-key blob, and a small encrypted validator. Argon2id is one-way and deliberately expensive, so the stored blob can't be turned back into your passkey; and nothing encrypted under the master key is readable without it.
Verify it yourself: src/lib/crypto (Argon2id, AES-GCM, key handling) and LockScreen.jsx (the unlock flow).
Per-app identity
Every app you sign into sees a different identifier. Apps can't correlate you across services.
From your master key, kunji deterministically derives a separate
Ed25519 keypair for each app domain (via HKDF keyed by the
domain). The identifier an app receives is
sub = hex(SHA-256(publicKeyBase64)). Different domain → different key →
unrelated sub. Because it's derived from your master key, the
same vault on any of your devices reproduces the same identity for that app.
Only the public key and sub for that one app. Two apps
comparing notes see unrelated values and cannot tell they belong to the same person.
Verify it yourself:
src/services/identity.js
(per-app key derivation, deriveSubFromPublicKey).
Self-sovereign
No account to create, no registry, no company in the middle. You are your key.
There's no signup — an anonymous session bootstraps your vault. Your apps list syncs
across your devices via a
vault id derived from your master key, so only devices holding the key can find
and decrypt it. You add a device by scanning a QR or entering a short code; the
master key travels over an ECDH-encrypted channel, and
both devices show a matching confirmation code you compare before the key is
released — so a substituted key is caught. You can also
change a device's passkey any time (it re-wraps the vault under the new passkey;
your recovery key and other linked devices are unaffected), and a separately-encrypted
recovery key restores everything if you lose every device. In the login path,
kunji shares no database with apps and runs no backend — the wallet signs
locally and posts straight to the app.
Because only you hold the keys, if you lose your vault and your recovery key, no one — including us — can restore your access. That's the price of a system where nobody can impersonate you, lock you out, or hand your data over. Export your recovery key and store it somewhere safe.
Verify it yourself: src/services/vault.js and linking.js (recovery + device linking).