---
title: End-to-End Encryption
description: Enable P2P encryption and zero-knowledge data protection in Persist stores.
section: Persist
order: 4
updatedAt: 2026-05-12
slug: persist/encryption
---
# End-to-End Encryption

## Enable encryption

```ts
import { createStore } from "@cuitty/persist";

const store = await createStore({
  name: "my-app",
  adapter: "sqlite",
  path: "./data/my-app.db",
  encrypt: true,
});
```

When `encrypt: true` is set, all data is encrypted on the device before it is written to disk or sent over the network. The sync server, storage backend, and any intermediary never see plaintext.

## How it works

Persist generates a 256-bit device key on first run and stores it in the OS keychain (or a local keyfile as fallback). Every record is encrypted with AES-256-GCM using a per-record nonce derived from the key and the record's path.

```
plaintext --> AES-256-GCM encrypt --> ciphertext --> store / sync
```

Reads reverse the process transparently. Application code works with plain objects -- encryption and decryption are invisible to the caller.

## Key management

### Device keys

Each device generates its own key on first use. The key never leaves the device unless explicitly exported.

```ts
// Export the device key (for backup or migration)
const exportedKey = await store.crypto.exportKey();

// Import a key on a new device
const store = await createStore({
  name: "my-app",
  adapter: "sqlite",
  path: "./data/my-app.db",
  encrypt: true,
  key: exportedKey,
});
```

### Key rotation

Rotate the encryption key without downtime. Persist re-encrypts existing records in the background.

```ts
await store.crypto.rotateKey();
```

After rotation the old key is kept in a sealed keyring so previously-synced peers can still decrypt historical data until they receive the new key.

## Encrypted sync

When encryption and sync are both enabled, peers exchange ciphertext. Decryption happens only on the receiving device using a shared key.

```ts
const store = await createStore({
  name: "my-app",
  adapter: "sqlite",
  path: "./data/my-app.db",
  encrypt: true,
  sync: {
    remote: "postgres",
    strategy: "last-write-wins",
  },
});
```

### Sharing keys between peers

Peers use a Diffie-Hellman key exchange to establish a shared secret. No key material is transmitted in the clear.

```ts
// On device A: generate an invite
const invite = await store.crypto.createInvite();
// --> send `invite.code` to device B out-of-band

// On device B: accept the invite
await store.crypto.acceptInvite(invite.code);
```

Once paired, both devices derive the same data key and can decrypt each other's records.

## Zero-knowledge architecture

The sync server stores only ciphertext and opaque metadata (record size, sync timestamps). It cannot:

- Read record contents
- Identify record types or schemas
- Correlate records across stores (keys are encrypted too)

Even if the server is compromised, an attacker gains no usable data without the device keys.