Skip to main content
AgentVault is built on a zero-knowledge architecture. The backend acts solely as an encrypted relay — it stores and forwards ciphertext but cannot decrypt message content, even if fully compromised.

What “Zero-Knowledge” Means

Zero-knowledge in this context means the server has zero knowledge of message content. All encryption and decryption happens exclusively on client devices (the owner’s app and the agent’s plugin). The server never possesses private keys, never performs decryption, and cannot read messages.
This is the same architectural model used by Signal, but purpose-built for AI agent communications.

The Server Cannot See

DataProtection
Message contentEncrypted client-side with XChaCha20-Poly1305 before transmission. Stored as BYTEA (binary) in PostgreSQL.
Private keysGenerated locally on each device. Never transmitted to the server.
Invite tokens (raw)Only the BLAKE2b hash is stored. The raw token is returned to the owner once and never persisted server-side.
Key exchange materialX3DH agreement computed locally on both sides. The shared secret never touches the wire.
Ratchet stateChain keys, message keys, and DH ratchet keys live exclusively in client memory/storage.

The Server Can See

Transparency about what the server does know is essential for honest security claims:
DataWhyMitigation
Ciphertext blobsMust store and relay encrypted messagesContent is indistinguishable from random bytes without the key
Public keysRequired for enrollment coordinationPublic keys reveal nothing about private keys
Message metadataTimestamps, sender/receiver device IDs, conversation IDsMetadata is tenant-scoped via RLS; cross-tenant correlation is prevented at the database level
Connection metadataIP addresses, WebSocket session timingStandard for any network service; Cloudflare provides an additional proxy layer
Device fingerprintsDisplayed to owner for manual verification during enrollmentDerived from public key via BLAKE2b; not sensitive
AgentVault does not claim to protect against traffic analysis or metadata correlation attacks. An adversary observing network traffic can determine that two parties are communicating and when, but not what they are saying. This is an honest limitation shared by all E2E encrypted messaging systems that do not use onion routing.

Cryptographic Guarantees

End-to-End Encryption

Every message is encrypted with XChaCha20-Poly1305 using a unique message key derived from the Double Ratchet:
plaintext
    |
    v
[XChaCha20-Poly1305 encrypt]  <-- message key (from ratchet)
    |                          <-- random 24-byte nonce
    v
ciphertext (BYTEA)  -->  server stores this  -->  recipient decrypts
The 192-bit nonce in XChaCha20 is safe to generate randomly — the collision probability is negligible even across billions of messages. This avoids the nonce-reuse catastrophe of AES-GCM (96-bit nonce), where reuse results in full plaintext recovery.

Forward Secrecy

The Double Ratchet protocol provides forward secrecy: every message advances the chain key, and old message keys are deleted after decryption. What this means in practice: If an attacker compromises a device’s current keys, they cannot decrypt past messages. The ratchet has already moved forward, and the old keys no longer exist.

Post-Compromise Security

The DH ratchet rotates the key agreement with every exchange direction change. If an attacker compromises current keys but the compromise is later resolved (e.g., device is revoked and re-enrolled), future messages become secure again after a single round trip.

Key Agreement (X3DH)

During enrollment, both parties perform a triple Diffie-Hellman key agreement:
DH1 = DH(owner_identity, agent_ephemeral)
DH2 = DH(owner_ephemeral, agent_identity)
DH3 = DH(owner_ephemeral, agent_ephemeral)

shared_secret = HKDF(DH1 || DH2 || DH3)
This shared secret initializes the Double Ratchet. The server facilitates the exchange of public keys but never learns the shared secret.

AEAD Encryption

XChaCha20-Poly1305 is an Authenticated Encryption with Associated Data (AEAD) cipher. This means:
  • Confidentiality — message content is encrypted
  • Integrity — any modification to the ciphertext is detected
  • Authentication — the recipient can verify the message was encrypted by someone holding the correct key
Combined with Ed25519 header signatures, recipients can verify both the encryption integrity and the sender’s identity.

Database-Level Protections

Ciphertext-Only Storage

The messages table contains exactly two content columns, both BYTEA:
CREATE TABLE messages (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL,
    conversation_id UUID NOT NULL,
    sender_device_id UUID NOT NULL,
    header_blob BYTEA NOT NULL,   -- encrypted header
    ciphertext BYTEA NOT NULL,    -- encrypted message body
    created_at TIMESTAMPTZ
);
There are no plaintext, preview, subject, or body TEXT columns. This is by design and enforced at the schema level.

Row-Level Security

Every table has RLS enabled with tenant isolation policies:
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON messages
  USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
Even if an attacker bypasses application logic, the database itself prevents cross-tenant data access.

Invite Token Hashing

Raw invite tokens are never stored. The server stores only the BLAKE2b hash:
Owner receives:  av_tok_8f3a2b...  (returned once, then forgotten by server)
Server stores:   BLAKE2b(av_tok_8f3a2b...)  (one-way hash)
This means a database breach does not expose usable invite tokens.

Compromise Scenarios

What the attacker gets: Ciphertext blobs, public keys, message metadata, hashed invite tokens.What the attacker cannot do: Decrypt any messages. Derive private keys from public keys. Use hashed invite tokens to enroll new devices.Why: Private keys exist only on client devices. The ciphertext is encrypted with keys derived from the Double Ratchet, which the server never possesses.
What the attacker gets: The ability to intercept ciphertext in transit, modify relay behavior, or inject messages.What the attacker cannot do: Decrypt messages in transit (no keys). Forge messages (Ed25519 signatures verify sender identity). Read historical messages (ciphertext-only storage).Why: The backend never handles plaintext or private keys, so there is nothing to intercept even with full code control.
What the attacker gets: Access to the device’s private keys, ratchet state, and the ability to decrypt messages for that device.What the attacker cannot do: Access messages from other tenants. Decrypt messages from before the compromise began (forward secrecy). Continue access after the device is revoked.Mitigation: Device revocation immediately terminates WebSocket connections and prevents further authentication. The owner re-enrolls with a new device, establishing fresh keys.
What the attacker gets: Encrypted WebSocket traffic (TLS + E2E encrypted payloads).What the attacker cannot do: Decrypt the TLS layer (Cloudflare-managed certificates). Decrypt the E2E layer inside TLS (XChaCha20-Poly1305). Forge messages (Ed25519 signatures).Why: Two layers of encryption — TLS for transport, E2E for content. Breaking TLS still leaves the attacker with indecipherable ciphertext.

Honest Limitations

AgentVault does not protect against:
LimitationExplanation
Compromised host OSAn attacker with root access to the machine running the owner app or agent plugin can extract keys from memory. This is true of all software-based encryption.
Malicious tenant administratorIn single-owner deployments (the common case), this is N/A. In multi-user tenants, a malicious admin could revoke and re-enroll devices but cannot decrypt existing messages.
Screenshots / screen recordingOnce a message is decrypted and displayed, screen capture is outside the encryption boundary.
Traffic analysisAn observer can determine that communication is occurring, its timing, and approximate volume. Content remains protected.
Metadata correlationMessage timestamps, sender/receiver pairs, and conversation patterns are visible to the server (within tenant scope).
These limitations are shared by virtually all E2E encrypted messaging systems, including Signal. Documenting them honestly is a deliberate choice — transparency about what we do and do not protect against builds trust.

Comparison with Signal’s Architecture

AgentVault follows the same zero-knowledge relay model as Signal with adaptations for agent communications:
PropertySignalAgentVault
E2E encryptionDouble RatchetDouble Ratchet
Symmetric cipherAES-256-CBC + HMAC-SHA256XChaCha20-Poly1305 (AEAD, 192-bit nonce)
Key exchangeX3DH with signed prekeysX3DH (synchronous enrollment)
Server roleEncrypted relayEncrypted relay
Forward secrecyYesYes
Post-compromise securityYesYes
Group protocolSender KeysRoom-scoped Double Ratchet (Sender Keys on roadmap)
Nonce safetyCounter-managedRandom-safe (192-bit)
The key difference in cipher choice is intentional: XChaCha20-Poly1305’s 192-bit nonce eliminates the nonce management complexity that AES-GCM/CBC requires, reducing the implementation surface area for bugs.