kunji

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 →

What kunji can never see
  • 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.

HOW IT WORKS

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.

WHAT THE SERVER HOLDS

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.

HOW IT WORKS

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.

WHAT AN APP SEES

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.

HOW IT WORKS

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.

THE TRADE-OFF — BE HONEST WITH YOURSELF

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).