> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Encryption at Rest

> Encrypt the local Ditto store with a key your app supplies, so documents and attachments on disk are unreadable without it.

Encryption at rest means Ditto's documents and attachments on disk are
unreadable without a key. Copying the app's files or pulling the storage from a
device doesn't expose your data. It matters most for apps that handle sensitive
data on devices that can be lost or stolen, such as handhelds, tablets, and
kiosks.

> [!NOTE]
>
> App-level encryption at rest is available in the Swift and Kotlin SDKs in
> version 5.2 and later. Support for other SDKs is planned.

## How it works

App-level encryption at rest lets your app encrypt all locally persisted Ditto
data, including attachments, with key material that your app supplies. The
feature is opt-in. When no encryption key is configured, Ditto creates or opens
an unencrypted local store.

Ditto uses a 32-byte encryption key directly, so generate high-entropy key
material with a cryptographically secure random number generator. Ditto does
not store, generate, or manage your encryption key. Your app must provide the
same key whenever it opens an encrypted local store, including after each cold
start. Store the key outside the Ditto persistence directory, such as in
[Apple Keychain Services](https://developer.apple.com/documentation/security/keychain-services/)
on iOS or the [Android Keystore system](https://developer.android.com/privacy-and-security/keystore)
on Android.

Encryption at rest protects locally persisted data only. It does not change
Ditto sync behavior, peer discovery, queries, subscriptions, or data
transmitted between peers.

## Encrypt the local store

Pass the key through `DittoConfig.encryptionKey` before you open Ditto. Use the
same key every time you reopen the store.

```swift Swift theme={null}
// Load exactly 32 high-entropy bytes from the Keychain.
// Your app is responsible for creating and storing this key material.
let keyBytes: Data = try loadEncryptionKeyFromKeychain()
let encryptionKey = try DittoEncryptionKey.raw(keyBytes)

var config = DittoConfig(
    databaseID: "REPLACE_ME_WITH_YOUR_DATABASE_ID",
    connect: .server(url: "REPLACE_ME_WITH_YOUR_URL")
)
config.encryptionKey = encryptionKey

let ditto = try await Ditto.open(config: config)
print(ditto.isEncrypted)
```

```kotlin Kotlin theme={null}
// Load exactly 32 high-entropy bytes from the Android Keystore.
// Your app is responsible for creating and storing this key material.
val keyBytes: ByteArray = loadEncryptionKeyFromKeystore()
val encryptionKey = DittoEncryptionKey.raw(keyBytes)

val config = DittoConfig(
    databaseId = "REPLACE_ME_WITH_YOUR_DATABASE_ID",
    connect = DittoConfig.Connect.Server(url = "REPLACE_ME_WITH_YOUR_URL"),
    encryptionKey = encryptionKey,
)

val ditto = DittoFactory.create(config)
```

## Handle encryption errors

Opening a store can fail with a typed encryption error. Your app can tell a
missing key from a corrupted store and respond to each one instead of treating
every failure the same way.

| Reason | Meaning |
| - | - |
| Key required | The store is encrypted and no key was supplied. |
| Key unexpected | The store is unencrypted and a key was supplied. |
| Key invalid | The supplied key is wrong, or the encrypted store is severely corrupted. |
| Unsupported scheme | The store was encrypted with a scheme this SDK version doesn't support. |
| Metadata corrupt | The store's encryption metadata can't be read. |

In Swift these are cases on the error thrown by `Ditto.open`: `keyRequired`,
`keyUnexpected`, `keyInvalid`, `unsupportedScheme`, and `metadataCorrupt`. In
Kotlin they are reasons on `DittoException.EncryptionException`: `KeyRequired`,
`KeyUnexpected`, `KeyInvalid`, `UnsupportedScheme`, and `MetadataCorrupt`. The
older passphrase error cases are deprecated.

Opening an existing encrypted store without a key, or with the wrong key, fails
without modifying the store. Opening an existing unencrypted store with a key
also fails.

## Switch between encrypted and unencrypted storage

Ditto doesn't convert a store in place. To change between encrypted and
unencrypted local storage, close Ditto, delete the existing local store, and
reopen it with the desired configuration.

```swift Swift theme={null}
ditto.close()
try Ditto.deleteStore(
    databaseID: config.databaseID,
    persistenceDirectory: config.persistenceDirectory
)
let ditto = try await Ditto.open(config: config)
```

```kotlin Kotlin theme={null}
ditto.close()
DittoFactory.deleteStore(
    databaseId = config.databaseId,
    persistenceDirectory = config.persistenceDirectory,
)
val ditto = DittoFactory.create(config)
```

> [!WARNING]
>
> Deleting the local store permanently removes unsynced local changes and resets
> the local peer identity. Only delete the store when you intend to remove local
> data, such as when recovering from a lost key, switching between encrypted and
> unencrypted storage, or wiping a device on logout.

## Recover from a lost key

If the encryption key is lost, the local encrypted store cannot be recovered.
Delete the local store and let Ditto resync data from other peers. Any changes
that hadn't synced before the key was lost are gone.

## Related documentation

* [Device Storage Management](https://docs.ditto.live/sdk/latest/sync/device-storage-management.md) for
  controlling what the local store holds
* [SDK 5.2 release notes](https://docs.ditto.live/release-notes.md#5-encryption-at-rest-for-the-sdk-data-store-swift-&-kotlin)
  for the full list of encryption changes


## Related topics

- [Release Notes](https://docs.ditto.live/sdk/latest/release-notes-hub/swift.md)
