Protocol v1 · in security review

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.

01Keys

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.
key hierarchy · simplified
Device keysX25519 wrap, one per device
Master passwordArgon2id key-encryption key
Recovery key32 random bytes, HKDF
unwraps
Vault key32 random bytes per key epoch
HKDF per item
Item keyq7N2…
Item key9xKd…
Item keyLp0e…
On the serverWrapped copies of the vault key and the item ciphertext. Never a device private key, your password or your recovery key.
Simplified from spec §3. When a device or member is removed, the vault moves to a new key epoch wrapped only to the devices that remain.
device enrollment · illustrative code
Your laptop · owner Approve the new device? 418207
New desktop · joining Compare with your laptop 418207
Both devices derive the same six digits from one transcript: the vault, both device IDs and a random nonce from each side. A server that swapped a key in transit would make the codes differ. Spec §3.5.
02Enrollment

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.

  1. The new device creates its keys

    It generates its own signing and key-exchange keys. The private halves never leave it.

  2. Both screens show a code

    Each device hashes the same transcript and shows six digits.

  3. You compare, then approve

    If the codes match, the owner device signs a device_add event that commits to the enrollment transcript and to the vault key wrapped for the new device. If they differ, enrollment aborts.

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

03Conflicts

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.

compare-and-swap · no last-writer-wins
  1. LaptopHas revision 7
    Desktop · offlineHas revision 7
  2. LaptopWrites a new value, base_revision: 7
    WorkerAccepted · revision 87 was still current
  3. Desktop · back onlineWrites its edit, base_revision: 7
    Worker409 conflictKept as a branch, not dropped
  4. Laptop or desktopYou choose a value; the record lists the branch in resolves
    WorkerAccepted · revision 9Branch resolved
With last-writer-wins, one of these edits would have silently replaced the other. Here both survive until you decide. Retries are safe too: a repeated request with the same idempotency key returns the original answer. Spec §5.
04Deletes and rollback

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

A delete is a signed record too, kept for the life of the vault. A stale device that writes on top of an older revision gets 409 tombstoned, and its write is not kept as a branch.

Checkpoints

After each verified pull a device signs a checkpoint and keeps the newest one. If the server later serves older or forked history, the device raises a rollback alarm instead of accepting it. Spec §6.

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.

05Architecture

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.

agentkit · CLI and Desktop

Clients

  • Device signing and exchange keys
  • Encrypt, sign, verify, decrypt
  • Local vault stays authoritative
ak-vault · this project

Cloudflare Worker

  • Token, device proof and revocation checks
  • Versioned protocol and test vectors
  • Production sync switched off until review
ak-vault · storage

Durable Object per vault

  • SQLite-backed, one per vault
  • Revisions, conflicts, tombstones
  • Ciphertext and minimal metadata
ak-web · accounts

Sign-in and dashboard

  • Issues device-bound ES256 tokens and feeds revocations to the Worker
  • The browser never decrypts vault data
The Worker never receives the key ak-web uses to sign CLI sessions. Every request carries a token plus a signature from the device's own key, bound to that exact request.
Encryption
XChaCha20-Poly1305
Signatures
Ed25519
Key agreement
X25519
Key derivation
HKDF-SHA256
Password KDF
Argon2id
Server tokens
ES256 JWT
06What the server sees

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 classification for a synced vault (protocol v1, proposed)
DataServer accessNotes
Secret valuesencryptedInside the AEAD ciphertext; never in logs, errors or telemetry.
Secret names, descriptions, tags, profiles, sensitivityencryptedItem IDs are random, never derived from names.
Portable policy and audit entriesencryptedAudit is a hash-chained, encrypted stream.
Vault key and recovery bundlewrappedThe 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 epochsvisibleNeeded for compare-and-swap ordering, conflicts and refusing stale writes.
Ciphertext sizes and timestampsvisibleReveals roughly how many items change and when; sizes hint at value length.
Devices, public keys, roles, accountsvisibleThe signed membership log is readable so the server can authorize requests.
Token claims and rate-limit countersvisibleAuthentication, authorization, revocation and abuse protection.
Request metadatavisibleAs with any HTTPS service, the edge sees IP address and request timing.
Master password, recovery key, device private keysnever sentThey never leave your devices.

Reading this with an AI assistant? Grab the Markdown version, or open it straight in your assistant.