Generate a secure 32-byte encryption key and store it so the app can use the same key on every launch. If the key changes or is lost, the client creates a new installation and cannot read the former local database.
dbEncryptionKey and the database path decide whether a client reopens its installation. If either changes, the client creates a new installation without reporting that the old installation was replaced. The new installation needs a signature and counts against the limit of 10.
Store the key and path as durable app state. Keep the same env value because it selects the default file name. An agent deployment must mount durable storage for XMTP_DB_DIRECTORY.
Database encryption key loss during iOS device transfers
When you transfer data to a new iOS device, the local database file may be moved without the encryption key, causing decryption errors. This commonly occurs when users choose Apple’s direct transfer option during new device setup, as Apple aggressively moves files to the new device. To prevent this issue, exclude the database directory from backups and device transfers.
For example, if you set a custom dbDirectory to a known directory, you can mark it as excluded from backups in iOS:
For debugging, it can be useful to decrypt a locally stored database. When a dbEncryptionKey is used, the XMTP client creates a SQLCipher database which applies transparent 256-bit AES encryption. A .sqlcipher_salt file is also generated alongside the database.
To open this database, you need to construct the password by prefixing 0x (to indicate hexadecimal numbers), then appending the encryption key (64 hex characters, 32 bytes) and the salt (32 hex characters, 16 bytes). For example, if your encryption key is A and your salt is B, the resulting password would be 0xAB.
If you want to inspect the database visually, you can use DB Browser for SQLite, an open source tool that supports SQLite and SQLCipher. In its Custom encryption settings, set the Plaintext Header Size to 32, and use the full Password as a Raw key:
PRAGMA key or salt has incorrect value means the supplied encryption key does not match the database. It can also mean that the file is not an XMTP database. Do not replace the stored key automatically, because that creates another installation.
dbPath: null creates an in-memory client. A string selects that exact path. Node also accepts a callback that receives the inbox ID. Browser does not accept the callback form.
Browser and Node can take a pre-built Backend instead of inline network options. Do not pass both. createBackend takes backendUrl, env, and appVersion.
Build, or resume, an existing client (created using Client.create()) that’s logged in and has an existing local database.
For Android and iOS SDKs, when building a client with an existing inboxId, the client automatically operates in offline mode since no backend request is needed to check the identity ledger. In offline mode, the client:
Skips all backend requests, including preference sync and inbox validation
Works entirely from the local database
Can be synchronized later with syncAllConversations() or by recreating the client without the offline flag
In some scenarios, you may need to temporarily release the local database connection, such as when performing database maintenance or when the app goes into the background for an extended period.
Call reconnectLocalDatabase() before another operation needs the database. Browser and Node do not provide this API. Both mobile calls are no-ops for an in-memory client.
For a file-backed client, operations that need the database fail after dropLocalDatabaseConnection() until you call reconnectLocalDatabase().