On September 5th, 2025, I decided to build a side project.
Three days later, I was surrounded by dozens of AI startup ideas, none of them good enough to make me want to open my editor. At the same time, I kept thinking about becoming a father soon — and the thought hit me:
What if one day my daughter asks me something I can’t explain, then asks ChatGPT — and it answers better than me?
Instead of sulking about it, I did the only reasonable thing: I made ChatGPT teach me things I didn’t know — starting with cryptography.
That’s how Assetspan was born: a zero-knowledge secret manager for teams — built by someone who knew very little about cryptography two months ago.
Teaching ChatGPT to Teach Me
At first, I did what everyone does:
“Explain how a zero-knowledge password manager works.”
It threw every crypto term it could find: KDFs, salting, nonce generation, symmetric key wrapping, HMAC authentication…
I was about to give up — until I told it this:
“Explain it like I’m a full-stack PHP + JS developer who knows nothing about cryptography.”
That changed everything. It started using analogies with Laravel’s Hash::make(), everything was suddenly a PHP variable, etc.
Lesson learned:
If you want ChatGPT to be useful, first teach it who you are.
Zero-Knowledge in Developer Terms
A zero-knowledge secrets manager means:
• The server stores encrypted data only.
• The encryption key is never sent to the server.
• Even if someone steals the database — they get nothing useful.
• Even I, as the creator, can’t decrypt your data.
To do this, I needed 3 core ideas:
1. Master Password → Master Key
2. Every asset (password entry, SSH key, license, etc.) gets its own encryption key
3. Shared secrets don’t leak keys to the server or other users
Key Hierarchy (The Heart of The System)
User Password (not stored anywhere)
↓
Argon2id → Master Key (UserKey)
↓
Encrypts → KEK (Key Encryption Key)
↓
KEK encrypts → AEAKs (per-asset keys)
↓
AEAK encrypts → Secrets (passwords, SSH keys, API tokens)
Deriving the Master Key (JS example)
deriveMasterKey(password: string, params: UserKeys) {
const salt = b64dec(params.salt)
const nonce = b64dec(params.private_blob.nonce)
const ciphertext = b64dec(params.private_blob.cipher)
// Derive KEK with Argon2id
const KEK = sodium.crypto_pwhash(
params.key_size,
sodium.from_string(password),
salt,
params.ops,
params.mem * 1024,
sodium.crypto_pwhash_ALG_ARGON2ID13,
)
// Decrypt private key
this.privKey = sodium.crypto_secretbox_open_easy(
ciphertext, nonce, KEK
)
sodium.memzero(KEK)
},
• The salt is generated at user registration and stored in the DB.
• The password is never stored in plain text.
• This master key never leaves the browser.
Encrypting a Secret (Client-side, before sending to server)
async function encryptSecret(secret, assetKey) {
await sodium.ready
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES)
const ciphertext = sodium.crypto_secretbox_easy(
secret, nonce, assetKey
)
return {
ciphertext: sodium.to_base64(ciphertext),
nonce: sodium.to_base64(nonce),
}
}
The server only sees { ciphertext, nonce }. No key. No plaintext.
Sharing a Secret Securely
1. User A encrypts the asset key (AEAK) with User B’s public key.
2. User A sends the encrypted AEAK to the server.
3. User B decrypts it locally using their master key.
No plaintext secret or key is ever shared with the server.
So… What Is Assetspan?
It’s now live at Assetspan.com — a zero-knowledge secret & credential manager designed for teams.
You don’t even need to register to try it — the “Create Asset” form is right on the homepage. No email wall.
Final Thought — For My Daughter
If someday she asks me something I don’t know, and ChatGPT replies perfectly instead of me, I think I’ll tell her this:
“I don’t know everything. I’m just a fool — not a tool. But I built one so we can both learn.”
Top comments (0)