How a secret stays sealed from one device to the next.
A walkthrough of protocol v1 as proposed: the keys, how a new device joins, how conflicting edits are kept, and how devices notice a server that lies about history. Where this page simplifies, the normative spec wins.
Three ways to open a vault. The server holds none of them.
Each vault has one random vault key, and every item gets its own key derived from it. The server stores the vault key only in wrapped form, locked to things only you hold.
- Each enrolled device. The vault key is wrapped to the device's X25519 key, so only that device can unwrap its copy.
- Your master password. Stretched with Argon2id, so a stolen wrapped copy has to be guessed offline.
- Your recovery key. 32 random bytes shown in groups of characters, for when every device is gone.
- One key opens the whole vault. Any member device can decrypt every item. Separate vaults are the only access boundary.
A new device joins only after you compare six digits.
The server relays enrollment messages, so it could try to slip in a key of its own. A short authentication string (SAS) stops that.
The new device creates its keys
It generates its own signing and key-exchange keys. The private halves never leave it.
Both screens show a code
Each device hashes the same transcript and shows six digits.
You compare, then approve
If the codes match, the owner device signs a
device_addevent that commits to the enrollment transcript and to the vault key wrapped for the new device. If they differ, enrollment aborts.The new device verifies first
It checks the signed member list and wrap set against the owner's signature before it unwraps anything.
The protocol defines this flow; the enrollment experience in ak is planned. If every device is lost, a fresh device can enroll itself with your password or recovery key through the encrypted recovery bundle.
Two edits, one secret. Neither one is lost.
Many sync tools keep whichever write lands last. ak-vault never does. Every write names the revision it started from, and the server accepts it only if that is still the current revision.
-
LaptopHas revision 7Desktop · offlineHas revision 7
-
LaptopWrites a new value,
base_revision: 7WorkerAccepted · revision 87 was still current -
Desktop · back onlineWrites its edit,
base_revision: 7Worker409 conflictKept as a branch, not dropped -
Laptop or desktopYou choose a value; the record lists the branch in
resolvesWorkerAccepted · revision 9Branch resolved
Deleted stays deleted. Old state stays old.
A server, or a device that slept for a month, should not be able to bring back history you moved past.
Tombstones
409 tombstoned, and its write is not kept as a branch.Checkpoints
Limitation: withheld updates. A malicious server can still hide updates from a device that has never seen them, such as a new or long-offline device. Detecting that needs a second channel, like another device's checkpoint or the recovery bundle. Transparency logs are out of scope for v1.
Small server, strong clients.
All encryption, decryption and signing of vault data happens in clients, using audited libraries. The server verifies tokens and request signatures, orders writes and stores ciphertext. There is no custom cryptography anywhere.
Clients
- Device signing and exchange keys
- Encrypt, sign, verify, decrypt
- Local vault stays authoritative
Cloudflare Worker
- Token, device proof and revocation checks
- Versioned protocol and test vectors
- Production sync switched off until review
Durable Object per vault
- SQLite-backed, one per vault
- Revisions, conflicts, tombstones
- Ciphertext and minimal metadata
Sign-in and dashboard
- Issues device-bound ES256 tokens and feeds revocations to the Worker
- The browser never decrypts vault data
- Encryption
- XChaCha20-Poly1305
- Signatures
- Ed25519
- Key agreement
- X25519
- Key derivation
- HKDF-SHA256
- Password KDF
- Argon2id
- Server tokens
- ES256 JWT
A short, explicit list of what leaves your device.
The Worker and its per-vault storage hold ciphertext and the metadata needed to order, authorize and rate-limit writes. The data classification owns the full list.
| Data | Server access | Notes |
|---|---|---|
| Secret values | encrypted | Inside the AEAD ciphertext; never in logs, errors or telemetry. |
| Secret names, descriptions, tags, profiles, sensitivity | encrypted | Item IDs are random, never derived from names. |
| Portable policy and audit entries | encrypted | Audit is a hash-chained, encrypted stream. |
| Vault key and recovery bundle | wrapped | The vault key is wrapped to member devices, your password-derived key and your recovery key; the recovery bundle only to the last two. |
| Vault and item IDs, revisions, tombstone flags, key epochs | Needed for compare-and-swap ordering, conflicts and refusing stale writes. | |
| Ciphertext sizes and timestamps | Reveals roughly how many items change and when; sizes hint at value length. | |
| Devices, public keys, roles, accounts | The signed membership log is readable so the server can authorize requests. | |
| Token claims and rate-limit counters | Authentication, authorization, revocation and abuse protection. | |
| Request metadata | As with any HTTPS service, the edge sees IP address and request timing. | |
| Master password, recovery key, device private keys | never sent | They never leave your devices. |
Keep reading
Reading this with an AI assistant? Grab the Markdown version, or open it straight in your assistant.