Encrypt anything.
You keep the key.
Crypt locks your text, hands you the key, and forgets it immediately. No account, no reset link. Send a one time link and it destroys itself the moment someone reads it.
Your text never leaves this tab. WebCrypto does the work locally.
Someone sent you a one time message
Opening it destroys it. There is no second look, so read it somewhere you can keep it.
The link is already spent, so this ciphertext only exists on this screen. Keep it if you want another try at the key, then open it in the Decrypt tab.
A fresh 256 bit key, random every time. Strongest option, and you have to keep the key somewhere safe.
Crypt stretches this with PBKDF2-SHA256, 310,000 rounds. A short passphrase is still a short passphrase.
Off: you send the link and the key separately, so neither one alone opens it. The key stays out of the URL either way until you turn this on.
This is the only copy. Crypt never wrote it down, so nobody can send it to you again.
Paste a token and Crypt will ask for whichever one it needs, a key or a passphrase.
The API
A Netlify Function used the wrong way round: as a one-shot cryptography oracle. It mints a key, encrypts with it, returns the key in the response body, and keeps nothing. No storage, no accounts, no server side secret. Tokens made here open in the app above, and the other way round.
Send text. Get a token plus the key that opens it.
# a key Crypt generates for you curl -s https://achenkunju.com/api/crypt/encrypt \ -H 'content-type: application/json' \ -d '{"text":"meet me at the usual place"}' # { # "token": "AC1.k.4f1c….9a2b…", # "key": "u7Hk…", <- yours, and only yours # "stored": false # }
Send the token and the key, or the token and the passphrase.
curl -s https://achenkunju.com/api/crypt/decrypt \ -H 'content-type: application/json' \ -d '{"token":"AC1.k.…","key":"u7Hk…"}' # { "text": "meet me at the usual place", "mode": "key" }
Just a fresh 256 bit key, nothing encrypted. Bring your own key to /encrypt with "key", or use "passphrase" instead and Crypt derives one with PBKDF2.
curl -s -X POST https://achenkunju.com/api/crypt/key
# { "key": "u7Hk…", "bits": 256, "alg": "AES-256-GCM" }
One time messages, the one place Crypt stores anything. Send a token you already made and the plaintext and key never reach the server at all. Send text instead and the key comes back the usual way.
curl -s https://achenkunju.com/api/crypt/once \ -H 'content-type: application/json' \ -d '{"token":"AC1.k.…","expiresIn":3600}' # { # "id": "y49ue9vtg3hv", # "url": "https://achenkunju.com/crypt/?o=y49ue9vtg3hv", # "stored": "ciphertext only", # "reads": 1 # }
GET peeks and changes nothing, so a link preview or a prefetch cannot spend someone else's message. POST burns it: the first caller gets the token, everyone after gets a 410. Two callers at the same instant resolve to exactly one winner.
# still there? curl -s https://achenkunju.com/api/crypt/once/y49ue9vtg3hv # { "waiting": true, "mode": "key", "expiresAt": "…" } # open it, which destroys it curl -s -X POST https://achenkunju.com/api/crypt/once/y49ue9vtg3hv # { "token": "AC1.k.…", "burned": true } # and again # { "error": "That message is gone. It was a one time link." }
The whole contract as JSON: endpoints, token format, limits.
- Cipher
- AES-256-GCM
- Key derivation
- PBKDF2-SHA256 ×310k
- Token
- AC1.k.iv.ct
- Max text
- 64 KB per call
- One time link
- 1 read, then gone
- Stored
- nothing, bar one time ciphertext
- Recovery
- none
How it works
Both engines speak the same token format, so nothing is locked to this page.
AC1.k.<iv>.<ciphertext> a random 256 bit key AC1.p.<salt>.<iv>.<ciphertext> a passphrase, stretched with PBKDF2 every segment is base64url, no padding 12 byte IV, random per message, GCM tag appended by WebCrypto the tag authenticates the token: edit one character and it refuses to open instead of handing back garbage