Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/matrix-org/matrix-js-sdk/llms.txt

Use this file to discover all available pages before exploring further.

End-to-end encryption (E2EE) in matrix-js-sdk ensures that message contents are only readable by the intended recipients. The SDK’s encryption support is built on the WebAssembly bindings of the Rust matrix-sdk-crypto library.

Rust crypto stack

The current encryption backend is the Rust crypto stack: matrix-sdk-crypto compiled to WebAssembly. It supersedes the older JavaScript-based implementation and is the only supported backend for new integrations. The Rust stack provides:
  • Megolm-based room message encryption
  • Olm-based to-device messages
  • Cross-signing for device and user verification
  • Server-side key backup
  • Secret storage (4S) integration
The MatrixClient.crypto property from the legacy stack is no longer available. Use MatrixClient.getCrypto() to obtain a CryptoApi instance.

Initializing encryption

Call initRustCrypto() on the MatrixClient after creating it, before calling startClient().
1

Create the client

import * as sdk from "matrix-js-sdk";

const matrixClient = sdk.createClient({
    baseUrl: "http://localhost:8008",
    accessToken: myAccessToken,
    userId: myUserId,
});
2

Initialize Rust crypto

// Enables E2EE support using the Rust crypto backend.
await matrixClient.initRustCrypto();
By default, initRustCrypto() attempts to use IndexedDB (available in browsers) as the crypto store. When running in Node.js or another environment without IndexedDB, pass useIndexedDB: false to use an ephemeral in-memory store:
await matrixClient.initRustCrypto({ useIndexedDB: false });
Without a persistent store, the crypto state is lost when the process exits. You will need to create a new device on the server (via MatrixClient.loginRequest) each time your application starts.
3

Obtain the CryptoApi interface

After initialization, use getCrypto() to access the CryptoApi:
const crypto = matrixClient.getCrypto();
// crypto is the main entry point for all E2EE operations
4

Start the client

await matrixClient.startClient({ initialSyncLimit: 10 });

Thread safety

The cryptography stack is not thread-safe. Having multiple MatrixClient instances connected to the same IndexedDB will cause data corruption and decryption failures. The application layer is responsible for ensuring that only one MatrixClient instance is active at a time.

CryptoApi interface

getCrypto() returns a CryptoApi object, which is the primary interface for encryption operations. Key methods include:
  • isEncryptionEnabledInRoom(roomId) — Returns true if the room is configured for encryption.
  • getVersion() — Returns the current version string of the crypto module (e.g. Rust SDK 0.x.x (...), Vodozemac 0.x.x).
  • getOwnDeviceKeys() — Returns the public Ed25519 and Curve25519 keys for the current device.
  • getUserVerificationStatus(userId) — Returns a UserVerificationStatus for the given user.
  • getDeviceVerificationStatus(userId, deviceId) — Returns a DeviceVerificationStatus or null.
  • bootstrapCrossSigning(opts) — Sets up cross-signing keys. See Cross-signing.
  • bootstrapSecretStorage(opts) — Sets up server-side secret storage. See Secret storage.
  • isSecretStorageReady() — Returns true if secret storage is fully configured.
  • checkKeyBackupAndEnable() — Checks the server for a trusted backup and enables it.
  • resetKeyBackup() — Creates a new key backup version. See Key backup.

CryptoEvent enum

The MatrixClient emits crypto-related events defined in the CryptoEvent enum:
EventDescription
CryptoEvent.UserTrustStatusChangedThe trust status of a user has changed. Payload: (userId, UserVerificationStatus).
CryptoEvent.KeyBackupStatusKey backup enabled/disabled. Payload: boolean.
CryptoEvent.KeyBackupFailedKey backup upload failed. Payload: error code string.
CryptoEvent.KeyBackupSessionsRemainingNumber of sessions awaiting backup changed. Payload: number.
CryptoEvent.KeyBackupDecryptionKeyCachedA new valid backup decryption key is in cache. Payload: backup version string.
CryptoEvent.VerificationRequestReceivedAn incoming verification request. Payload: VerificationRequest.
CryptoEvent.DevicesUpdatedStored devices for one or more users have been updated. Payload: (userIds, initialFetch).
CryptoEvent.KeysChangedCross-signing keys changed or enabled/disabled.
CryptoEvent.LegacyCryptoStoreMigrationProgressMigration progress from legacy crypto. Payload: (progress, total).

Complete setup example

The following is a full initialization flow including secret storage and cross-signing:
import * as sdk from "matrix-js-sdk";
import { CryptoEvent } from "matrix-js-sdk/lib/crypto-api";

const matrixClient = sdk.createClient({
    baseUrl: "http://localhost:8008",
    accessToken: myAccessToken,
    userId: myUserId,
    cryptoCallbacks: {
        getSecretStorageKey: async ({ keys }) => {
            // Prompt the user to enter their recovery key.
            // Return [keyId, privateKeyBytes] or null.
            const key = await promptUserForRecoveryKey();
            const keyId = Object.keys(keys)[0];
            return [keyId, key];
        },
    },
});

// Initialize encryption
await matrixClient.initRustCrypto();

const crypto = matrixClient.getCrypto();

// Set up secret storage (idempotent — safe to call unconditionally)
await crypto.bootstrapSecretStorage({
    createSecretStorageKey: async () => {
        // Called only when a new key needs to be created.
        // Prompt the user to save this key safely.
        return generateAndDisplayRecoveryKey();
    },
});

// Set up cross-signing
await crypto.bootstrapCrossSigning({
    authUploadDeviceSigningKeys: async (makeRequest) => {
        return makeRequest(authDict);
    },
});

// Enable key backup
const hasKeyBackup = (await crypto.checkKeyBackupAndEnable()) !== null;
if (!hasKeyBackup) {
    await crypto.resetKeyBackup();
}

// Listen for crypto events
matrixClient.on(CryptoEvent.UserTrustStatusChanged, (userId, status) => {
    console.log(`Trust status changed for ${userId}:`, status.isVerified());
});

matrixClient.on(CryptoEvent.KeyBackupStatus, (enabled) => {
    console.log("Key backup enabled:", enabled);
});

await matrixClient.startClient({ initialSyncLimit: 10 });

Migrating from legacy crypto

If your application previously called MatrixClient.initLegacyCrypto(), migration to the Rust stack happens automatically when you switch to initRustCrypto(). You must supply the legacy cryptoStore and pickleKey to createClient so the migration can read the existing data:
const matrixClient = sdk.createClient({
    // Provide the legacy crypto store and pickle key for migration.
    cryptoStore: myCryptoStore,
    pickleKey: myPickleKey,
    baseUrl: "http://localhost:8008",
    accessToken: myAccessToken,
    userId: myUserId,
});

// Migration runs automatically when initRustCrypto is called.
await matrixClient.initRustCrypto();
To track migration progress, listen for the LegacyCryptoStoreMigrationProgress event. When progress === total === -1, migration is complete:
matrixClient.on(CryptoEvent.LegacyCryptoStoreMigrationProgress, (progress, total) => {
    if (progress === -1 && total === -1) {
        console.log("Migration complete");
    } else {
        console.log(`Migration progress: ${progress} / ${total}`);
    }
});
After migrating, remove the cryptoStore and pickleKey options and any code that calls initLegacyCrypto(). The Rust stack does not support the deprecated MatrixClient.crypto property.