From 7bd6ba09a7bcf1e94656f8369d93cb468ff0f799 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez <11448715+al3mart@users.noreply.github.com> Date: Thu, 2 Apr 2026 20:08:04 +0200 Subject: [PATCH] fix(matrix): allow secret storage recreation during repair bootstrap The gateway repair path calls forceResetCrossSigning but does not pass allowSecretStorageRecreateWithoutRecoveryKey. When a device loses its recovery key the repair creates new cross-signing keys but cannot persist them in secret storage. On next restart the gateway fails again with "getSecretStorageKey callback returned falsey". Pass allowSecretStorageRecreateWithoutRecoveryKey: true in the repair bootstrap call only. The initial conservative bootstrap remains unchanged so operators are notified when secret storage becomes inaccessible. --- extensions/matrix/src/matrix/sdk.test.ts | 1 + extensions/matrix/src/matrix/sdk.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/extensions/matrix/src/matrix/sdk.test.ts b/extensions/matrix/src/matrix/sdk.test.ts index 2faa518f472..d9f869551ff 100644 --- a/extensions/matrix/src/matrix/sdk.test.ts +++ b/extensions/matrix/src/matrix/sdk.test.ts @@ -1050,6 +1050,7 @@ describe("MatrixClient crypto bootstrapping", () => { expect(bootstrapSpy).toHaveBeenCalledTimes(2); expect((bootstrapSpy.mock.calls as unknown[][])[1]?.[1] ?? {}).toEqual({ forceResetCrossSigning: true, + allowSecretStorageRecreateWithoutRecoveryKey: true, strict: true, }); }); diff --git a/extensions/matrix/src/matrix/sdk.ts b/extensions/matrix/src/matrix/sdk.ts index 6a4c365283c..314b675c027 100644 --- a/extensions/matrix/src/matrix/sdk.ts +++ b/extensions/matrix/src/matrix/sdk.ts @@ -465,8 +465,13 @@ export class MatrixClient { ); } else if (this.password?.trim()) { try { + // The repair path already force-resets cross-signing; allow secret storage + // recreation so the new keys can be persisted. Without this, a device that + // lost its recovery key enters a permanent failure loop because the new + // cross-signing keys have nowhere to be stored. const repaired = await cryptoBootstrapper.bootstrap(crypto, { forceResetCrossSigning: true, + allowSecretStorageRecreateWithoutRecoveryKey: true, strict: true, }); if (repaired.crossSigningPublished && repaired.ownDeviceVerified !== false) {