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.

Server-side key backup allows Megolm session keys to be uploaded to the homeserver in an encrypted form. When a user signs in on a new device, the key backup can be restored to decrypt historical messages.
Key backup works best in combination with secret storage. When secret storage is configured, the backup decryption key is stored there automatically, so new devices can restore the backup without manual intervention.

Core interfaces

KeyBackupInfo

Represents the server-side key backup version, returned by GET /_matrix/client/v3/room_keys/version:
export interface KeyBackupInfo {
    algorithm: string;
    auth_data: ISigned & (Curve25519AuthData | Aes256AuthData);
    count?: number;   // number of keys stored in this backup
    etag?: string;
    version?: string; // version identifier (a number stored as a string)
}

BackupTrustInfo

Indicates whether the SDK trusts a given backup version:
export interface BackupTrustInfo {
    /**
     * True if there is a valid signature on the backup from a trusted device.
     */
    readonly trusted: boolean;

    /**
     * True if this backup matches the stored decryption key.
     */
    readonly matchesDecryptionKey: boolean;
}

KeyBackupCheck

The result of CryptoApi.checkKeyBackupAndEnable():
export interface KeyBackupCheck {
    backupInfo: KeyBackupInfo;
    trustInfo: BackupTrustInfo;
}

Checking and enabling an existing backup

checkKeyBackupAndEnable() fetches the current backup version from the server. If a trusted backup exists, it starts backing up new keys to it automatically and returns a KeyBackupCheck. If there is no backup, it returns null:
const crypto = matrixClient.getCrypto();

// Returns null if there is no backup on the server.
const check = await crypto.checkKeyBackupAndEnable();

if (check === null) {
    console.log("No key backup found");
} else {
    console.log("Backup version:", check.backupInfo.version);
    console.log("Trusted:", check.trustInfo.trusted);
    console.log("Matches decryption key:", check.trustInfo.matchesDecryptionKey);
}

Creating a new backup

If no backup exists, call resetKeyBackup() to create one. This also starts uploading keys to the new backup automatically:
// Check if we have a key backup.
// If checkKeyBackupAndEnable returns null, there is no key backup.
const hasKeyBackup = (await crypto.checkKeyBackupAndEnable()) !== null;

// Create a new backup if none exists
if (!hasKeyBackup) {
    await crypto.resetKeyBackup();
}
resetKeyBackup() will:
  1. Create a new backup version on the server (replacing any existing one).
  2. Generate a new backup decryption key.
  3. Store the decryption key in secret storage (if configured).
  4. Call checkKeyBackupAndEnable() to activate the new backup.
Calling resetKeyBackup() replaces any existing backup. Keys in the old backup will no longer be actively maintained. Before calling this, ensure the old backup’s decryption key is stored somewhere safe if you may need it.

Key backup events

The MatrixClient emits events related to key backup status:
EventPayloadDescription
CryptoEvent.KeyBackupStatusbooleanFires when key backup is enabled or disabled.
CryptoEvent.KeyBackupFailedstring (error code)Fires when a key backup upload fails.
CryptoEvent.KeyBackupSessionsRemainingnumberFires when the count of sessions awaiting backup changes.
CryptoEvent.KeyBackupDecryptionKeyCachedstring (version)Fires when a new valid backup decryption key is cached locally. Only fired by the Rust crypto backend.
import { CryptoEvent } from "matrix-js-sdk/lib/crypto-api";

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

matrixClient.on(CryptoEvent.KeyBackupFailed, (errorCode) => {
    console.error("Key backup failed with error:", errorCode);
});

matrixClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => {
    console.log(`${remaining} sessions remaining to back up`);
});

matrixClient.on(CryptoEvent.KeyBackupDecryptionKeyCached, (version) => {
    console.log(`Decryption key cached for backup version ${version}`);
});

Restoring keys from backup

KeyBackupRestoreOpts and KeyBackupRestoreResult

export interface KeyBackupRestoreOpts {
    /**
     * Called periodically to report the progress of the restore operation.
     */
    progressCallback?: (progress: ImportRoomKeyProgressData) => void;
}

export interface KeyBackupRestoreResult {
    /** The total number of keys found in the backup. */
    total: number;
    /** The number of keys that were successfully imported. */
    imported: number;
}

Restoring the full backup

Before calling restoreKeyBackup(), a decryption key must be available in the crypto store. This can be achieved in several ways:
  • The key was cached by resetKeyBackup() when the backup was created.
  • The key was loaded from secret storage via loadSessionBackupPrivateKeyFromSecretStorage().
  • The key was received from another device via interactive verification.
  • The key was set explicitly via storeSessionBackupPrivateKey().
// Load the key from secret storage (requires getSecretStorageKey callback to return the key)
await crypto.loadSessionBackupPrivateKeyFromSecretStorage();

// Restore the full backup
const result = await crypto.restoreKeyBackup({
    progressCallback: (progress) => {
        if (progress.stage === "load_keys") {
            console.log(
                `Restored ${progress.successes} / ${progress.total} keys` +
                ` (${progress.failures} failures)`
            );
        } else {
            console.log("Fetching keys from server...");
        }
    },
});

console.log(`Restore complete: ${result.imported} / ${result.total} keys imported`);
A full key backup restore can be very large and may take a significant amount of time. Use progressCallback to keep users informed and avoid timing out.

Checking the active backup version

To determine which backup version is currently active for automatic uploads:
const version = await crypto.getActiveSessionBackupVersion();
if (version !== null) {
    console.log("Active backup version:", version);
} else {
    console.log("Automatic key backup is not enabled");
}

Determining backup trust

To check whether a specific backup is trusted without enabling it:
const backupInfo = await crypto.getKeyBackupInfo();
if (backupInfo) {
    const trustInfo = await crypto.isKeyBackupTrusted(backupInfo);
    console.log("Backup trusted:", trustInfo.trusted);
    console.log("Matches decryption key:", trustInfo.matchesDecryptionKey);
}

Complete key backup setup flow

1

Create the client with cryptoCallbacks

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 }) => {
            const key = await promptUserForRecoveryKey();
            return [Object.keys(keys)[0], key];
        },
    },
});
2

Initialize encryption and secret storage

await matrixClient.initRustCrypto();
const crypto = matrixClient.getCrypto();

await crypto.bootstrapSecretStorage({
    createSecretStorageKey: async () => {
        const key = await crypto.createRecoveryKeyFromPassphrase();
        await showRecoveryKeyToUser(key.encodedPrivateKey);
        return key;
    },
});
3

Check for an existing backup and create one if needed

const check = await crypto.checkKeyBackupAndEnable();

if (check === null) {
    // No backup exists — create one.
    // resetKeyBackup() also stores the key in secret storage.
    await crypto.resetKeyBackup();
    console.log("New key backup created");
} else {
    console.log(
        `Using existing backup version ${check.backupInfo.version},` +
        ` trusted: ${check.trustInfo.trusted}`
    );
}
4

Listen for backup events

matrixClient.on(CryptoEvent.KeyBackupStatus, (enabled) => {
    updateBackupStatusUI(enabled);
});

matrixClient.on(CryptoEvent.KeyBackupFailed, (errorCode) => {
    showBackupError(errorCode);
});
5

Restore keys on a new device (optional)

When signing into a new device that has no local key history:
// Load the decryption key from secret storage
await crypto.loadSessionBackupPrivateKeyFromSecretStorage();

// Restore all backed-up keys
const result = await crypto.restoreKeyBackup({
    progressCallback: (progress) => {
        if (progress.stage === "load_keys") {
            updateProgressUI(progress.successes, progress.total);
        }
    },
});

console.log(`Restored ${result.imported} of ${result.total} keys`);