++ {reclaimSpace &&
++ `${formatSize(localStatus.reclaimableBytes)} of ${formatSize(localStatus.databaseBytes)} can be returned to disk. Reclaiming it once turns on automatic shrinking and restarts the local server.`}
++ {reclaimSpace && legacyDatabaseBytes !== null && " "}
++ {legacyDatabaseBytes !== null &&
++ `The database from before 0.1.0 (${formatSize(legacyDatabaseBytes)}) is kept for rolling back.`}
++ >
+ }
- description={
-- <>
-- Old history is cleaned up automatically, but this database still holds the freed
-- space. Run the command on {label} to return it to disk; it pauses and restarts the
-- background service. Quit the desktop app first if it hosts this environment.
-- {status.compactCommand}
-- >
-+ maintenance ? (
-+ <>
-+ The database uses {(status.databaseBytes / 1024 ** 2).toFixed(1)} MiB, with{" "}
-+ {(status.reclaimableBytes / 1024 ** 2).toFixed(1)} MiB available to reclaim.
-+ Reclaiming space temporarily stops the local server. Finish active work first;
-+ cleanup can take several minutes and needs temporary disk space. The server restarts
-+ afterward.
-+ >
-+ ) : (
-+ <>
-+ Old history is cleaned up automatically, but this database still holds the freed
-+ space. Run the command on {label} to return it to disk; it pauses and restarts the
-+ background service. Quit the desktop app first if it hosts this environment.
-+ {status.compactCommand}
-+ >
-+ )
- }
- control={
-
--
-+ {maintenance ? (
-+
-+ ) : (
-+
-+ )}
-
++ control={
++
++ }
++ />
++ )}
++ {visible.length > 0 && (
++
++ )}
+ {visible.map(({ environmentId, status }) => (
+ Promise;
installUpdate: () => Promise;
onUpdateState: (listener: (state: DesktopUpdateState) => void) => () => void;
-+ /** Offline cleanup of this Flatpak desktop's local database, with confirmation and restart. */
++ /** The `t3 doctor` database repairs for this Flatpak desktop's local database. */
+ databaseMaintenance?: {
-+ compact: () => Promise;
++ /** Size of the database kept from before 0.1.0 for rolling back, or null once it is gone. */
++ legacyDatabaseBytes: () => Promise;
++ /** Confirms, then reclaims space (restarting the server) and/or deletes the legacy database. */
++ run: (input: { reclaimSpace: boolean }) => Promise;
+ };
cliCommand?: {
getState: () => Promise;
diff --git a/patches/0012-flatpak-update-channel.patch b/patches/0012-flatpak-update-channel.patch
index 96fa8ed..a60a50d 100644
--- a/patches/0012-flatpak-update-channel.patch
+++ b/patches/0012-flatpak-update-channel.patch
@@ -1,8 +1,8 @@
diff --git a/apps/desktop/src/updates/DesktopUpdates.test.ts b/apps/desktop/src/updates/DesktopUpdates.test.ts
-index 9a069527d2..919619b47a 100644
+index 3bbff03927..5787c78380 100644
--- a/apps/desktop/src/updates/DesktopUpdates.test.ts
+++ b/apps/desktop/src/updates/DesktopUpdates.test.ts
-@@ -37,6 +37,11 @@ describe("DesktopUpdates", () => {
+@@ -36,6 +36,11 @@ describe("DesktopUpdates", () => {
const result = yield* updates.check("manual");
assert.isFalse(result.checked);
assert.equal(harness.checkCount(), 0);
@@ -29,10 +29,10 @@ index 0c65574646..94737ae8f2 100644
if (Option.isSome(activeAction)) {
return yield* new DesktopUpdateActionInProgressError({
diff --git a/apps/web/src/components/settings/SettingsPanels.tsx b/apps/web/src/components/settings/SettingsPanels.tsx
-index 16c6ff3fb8..ed66cdcb3b 100644
+index c733fae935..a890471f99 100644
--- a/apps/web/src/components/settings/SettingsPanels.tsx
+++ b/apps/web/src/components/settings/SettingsPanels.tsx
-@@ -295,6 +295,7 @@ function AboutVersionSection() {
+@@ -157,6 +157,7 @@ function AboutVersionSection() {
if (
!bridge ||
typeof bridge.setUpdateChannel !== "function" ||
@@ -40,7 +40,7 @@ index 16c6ff3fb8..ed66cdcb3b 100644
channel === selectedUpdateChannel
) {
return;
-@@ -449,10 +450,15 @@ function AboutVersionSection() {
+@@ -311,10 +312,15 @@ function AboutVersionSection() {
{hasDesktopBridge ? (
{
handleUpdateChannelChange(value as DesktopUpdateChannel);
}}
-@@ -461,7 +467,7 @@ function AboutVersionSection() {
+@@ -323,7 +329,7 @@ function AboutVersionSection() {
size="sm"
className="w-full sm:w-40"
aria-label="Update track"
diff --git a/patches/0013-electron-security-update.patch b/patches/0013-electron-security-update.patch
deleted file mode 100644
index 79e0966..0000000
--- a/patches/0013-electron-security-update.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-Temporary: bump Electron from 44.4.5 to 44.9.1, which stops trusting the
-compromised eMudhra intermediate CAs. Drop this patch once upstream T3 Code
-ships Electron 44.9.1 or newer.
-
-diff --git a/apps/desktop/package.json b/apps/desktop/package.json
-index a6fe979100..14dd7e5479 100644
---- a/apps/desktop/package.json
-+++ b/apps/desktop/package.json
-@@ -24,7 +24,7 @@
- "@t3tools/tailscale": "workspace:*",
- "dbus-next": "0.10.2",
- "effect": "catalog:",
-- "electron": "44.4.5",
-+ "electron": "44.9.1",
- "electron-store": "^8.2.0",
- "electron-updater": "^6.8.9",
- "electron-webauthn": "catalog:",
-diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
-index d71e2422a3..32bea9ebfb 100644
---- a/pnpm-lock.yaml
-+++ b/pnpm-lock.yaml
-@@ -229,7 +229,7 @@ importers:
- dependencies:
- '@clerk/electron':
- specifier: 0.1.1
-- version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
-+ version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.9.1)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
- '@clerk/electron-passkeys':
- specifier: 0.0.3
- version: 0.0.3
-@@ -264,8 +264,8 @@ importers:
- specifier: 4.0.5
- version: 4.0.5
- electron:
-- specifier: 44.4.5
-- version: 44.4.5
-+ specifier: 44.9.1
-+ version: 44.9.1
- electron-store:
- specifier: ^8.2.0
- version: 8.2.0
-@@ -824,7 +824,7 @@ importers:
- version: 6.39.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
- '@clerk/electron':
- specifier: 0.1.1
-- version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
-+ version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.9.1)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
- '@clerk/react':
- specifier: 6.17.8
- version: 6.17.8(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
-@@ -8719,8 +8719,8 @@ packages:
- resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==}
- engines: {node: '>=8.0.0'}
-
-- electron@44.4.5:
-- resolution: {integrity: sha512-SjgoaeYsSWZfJzubgQU7juvuXMTvn6/e1gAHdGFA/yuMbpF+I+skYqIJ6DdXBXHeWbkbpNpmwZCTpiivtlWZSw==}
-+ electron@44.9.1:
-+ resolution: {integrity: sha512-x/tECFFDa2OFNjMObFH0EGKWrCnFZPOR1wP2iMIVt0STecGl8FmDXdaiaryY0kf8mtw9kSLP2yK8dfdaWkfDmw==}
- engines: {node: '>= 22.12.0'}
- hasBin: true
-
-@@ -13898,12 +13898,12 @@ snapshots:
- '@clerk/electron-passkeys-win32-arm64-msvc': 0.0.3
- '@clerk/electron-passkeys-win32-x64-msvc': 0.0.3
-
-- '@clerk/electron@0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)':
-+ '@clerk/electron@0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.9.1)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)':
- dependencies:
- '@clerk/clerk-js': 6.39.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
- '@clerk/react': 6.17.8(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
- '@clerk/shared': 4.40.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
-- electron: 44.4.5
-+ electron: 44.9.1
- react: 19.3.0
- tslib: 2.8.1
- optionalDependencies:
-@@ -19406,7 +19406,7 @@ snapshots:
- transitivePeerDependencies:
- - supports-color
-
-- electron@44.4.5:
-+ electron@44.9.1:
- dependencies:
- '@electron-internal/extract-zip': 1.0.5
- '@electron/get': 5.1.0