For Glocker, I wanted to provide the convenience of a hosted dashboard but without compromising the private data of the people who use it. For this, I decided to implement end-to-end encryption. The ideas are interesting and new to me, so I thought I'd write about them here.
The setup is an agent that runs on your machine and records things about it, and a cloud service that stores those records and shows them to you in a browser. The goal is that the cloud can store and serve the data, but can't read it.
There are two keys. The blue one is the data key. It's random, and it locks every record before the agent sends it. The amber one is your passphrase, which only you know.
The cloud never holds a key. It holds locked boxes: the records, locked with the blue key, and one more box, locked with the amber key, with the blue key inside it. When you open the dashboard, your passphrase opens that outer box in your browser, and the blue key inside opens the records. All of this happens on your side.
Why two keys and not just the passphrase? Passphrases are guessable and you might want to change one. With a random data key, changing the passphrase only means relocking one small box, not every record. And before a passphrase is used as a key, it's run through a deliberately slow function (PBKDF2, hundreds of thousands of rounds), so trying guesses is expensive.
The first exchange is the only tricky part. (1) In your browser, a blue key is made and locked in an amber box. (2) The cloud stores that box. (3) On your machine, you type the passphrase once, the box opens, and the agent keeps the blue key from then on.
Two implementation details
- The cloud needs some way to tell if a record was already uploaded, but a plain hash of the content leaks it (anyone can hash "youtube.com" and compare), so the ID has to be a hash keyed with the blue key.
- Some things have to stay readable, like when a record was made, so the cloud can notice if the agent goes quiet. No way around this so it's the content that's locked. Not the timestamps.
I'm not a cryptographer and this hasn't been audited, so corrections are welcome.


Top comments (2)
Clear explanation, and keying the record-ID hash is a good catch. Two suggestions: pass each record's ID and timestamp as associated data when you encrypt (AES-GCM supports this), so the server can't quietly swap or replay records between slots. And if you can, Argon2id instead of PBKDF2 makes GPU guessing of the passphrase a lot more expensive for the same wait.
Thanks for the feedback! Right now, I seal each field with (timestamp,kind,field) as AAD. I don't use the device or record ID. I'll look at this. As for Argon2id, I went with PBKDF2 because it’s native in WebCrypto; the vault records its KDF, so moving to Argon2id via wasm is possible. I avoided it because I didn't want to ship a a third-party wasm build of Argon2id in the page. But you're right, from a security perspective, it's valid.