E2E Encryption

Overview

Ravi uses end-to-end encryption for all sensitive data in the credential vault. Credentials are encrypted client-side before being sent to the server. The server stores only ciphertext and can never read your passwords or secrets.

How it works

Key derivation

When you first log in, you choose a 6-digit PIN. This PIN is used to derive your encryption keys:

PIN + server-stored salt
    → Argon2id (time=3, mem=64MB, threads=1)
    → 32-byte seed
    → X25519 keypair (public + private key)
  • The PIN never leaves your machine
  • The salt is stored server-side (one per account)
  • The derived public key is uploaded to the server
  • The derived private key stays local in ~/.ravi/auth.json

Encryption

Data is encrypted using NaCl SealedBox:

plaintext
    → SealedBox seal (ephemeral X25519 + Poly1305 MAC)
    → ciphertext stored as "e2e::<base64>"

SealedBox provides anonymous encryption — anyone can encrypt to your public key, but only the holder of the private key can decrypt.

What’s encrypted

DataEncrypted?
Passwords (password field)Yes
Passwords (username, notes)Yes
Vault secrets (value field)Yes
Vault secrets (notes)Yes
Email content at restYes
Email content in real-time eventsNo (plaintext via SSE)
Secret keys (names)No (needed for lookup)
Password domainsNo (needed for lookup)

Zero-knowledge guarantee

The server never sees:

  • Your PIN
  • Your private key
  • Plaintext passwords or secret values

A server-side breach exposes only ciphertext. Without your PIN, the data is unreadable.

PIN verification

To prevent wrong-PIN errors from silently corrupting data, the server stores a known ciphertext (the encrypted string "ravi-e2e-verify"). On login, the client:

  1. Derives the keypair from PIN + salt
  2. Checks that the derived public key matches the server’s stored public key
  3. Decrypts the verifier to confirm the PIN is correct

You get 3 PIN attempts per login. There is no server-side lockout — the verification is purely cryptographic.

Recovery key

During first-time setup, a recovery key is saved to ~/.ravi/recovery-key.txt. Back this up. If you forget your PIN, the recovery key is the only way to regain access to your encrypted data.

Cross-platform compatibility

The same encryption format is used across all Ravi clients:

  • Go CLIinternal/crypto/ package
  • OpenClaw pluginsrc/crypto.ts (TypeScript)
  • API — ciphertext format "e2e::<base64>"

Data encrypted by the CLI can be decrypted by the OpenClaw plugin and vice versa, as long as the same PIN is used.

Transparent operation

As a user, you never need to think about encryption. The CLI and plugins handle it automatically:

# This auto-encrypts before sending to the server
ravi passwords create example.com --password "S3cret!" --json

# This auto-decrypts when reading from the server
ravi passwords get <uuid> --json
# → {"password": "S3cret!"}

Next steps