@rapidmx/web-client 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (906) hide show
  1. package/README.md +415 -381
  2. package/apps/admin/_layout.tsx +2 -2
  3. package/apps/admin/audit-log/index.tsx +4 -4
  4. package/apps/admin/branding/index.tsx +39 -39
  5. package/apps/admin/data-requests/index.tsx +482 -482
  6. package/apps/admin/distribution-lists/[uid].tsx +3 -3
  7. package/apps/admin/distribution-lists/index.tsx +4 -4
  8. package/apps/admin/distribution-lists/new/index.tsx +5 -5
  9. package/apps/admin/domains/[uid].tsx +166 -166
  10. package/apps/admin/domains/index.tsx +4 -4
  11. package/apps/admin/domains/new/index.tsx +5 -5
  12. package/apps/admin/encryption-policy/index.tsx +1 -1
  13. package/apps/admin/escrow-scopes/[uid].tsx +350 -350
  14. package/apps/admin/escrow-scopes/index.tsx +4 -4
  15. package/apps/admin/escrow-scopes/new/index.tsx +5 -5
  16. package/apps/admin/index.tsx +129 -129
  17. package/apps/admin/ingest-queue/index.tsx +4 -4
  18. package/apps/admin/mailbox-policy/index.tsx +1 -1
  19. package/apps/admin/mailboxes/[uid].tsx +271 -271
  20. package/apps/admin/mailboxes/new/index.tsx +30 -30
  21. package/apps/admin/plugins/index.tsx +15 -15
  22. package/apps/admin/quarantine/index.tsx +5 -5
  23. package/apps/admin/retention-policy/index.tsx +39 -39
  24. package/apps/admin/signing-certificates/index.tsx +343 -343
  25. package/apps/admin/transport-rules/[uid].tsx +5 -5
  26. package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
  27. package/apps/admin/transport-rules/index.tsx +4 -4
  28. package/apps/admin/transport-rules/new/index.tsx +5 -5
  29. package/apps/escrow/_layout.tsx +1 -1
  30. package/apps/escrow/audit-log/index.tsx +196 -196
  31. package/apps/escrow/index.tsx +4 -4
  32. package/apps/escrow/matters/[uid].tsx +621 -621
  33. package/apps/escrow/matters/new/index.tsx +6 -6
  34. package/apps/shared/appearance/AppearanceForm.tsx +3 -3
  35. package/apps/shared/appearance/AppearanceHead.tsx +1 -1
  36. package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
  37. package/apps/shared/appearance/appearanceCache.ts +1 -1
  38. package/apps/shared/appearance/appearanceContext.ts +1 -1
  39. package/apps/shared/appearance/theme.ts +1 -1
  40. package/apps/shared/auth/adminAccess.ts +99 -99
  41. package/apps/shared/calendar/bookingPlugin.ts +1 -1
  42. package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
  43. package/apps/shared/calendar/calendarReminders.ts +1 -1
  44. package/apps/shared/calendar/useCalendarReminders.ts +1 -1
  45. package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
  46. package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
  47. package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
  48. package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
  49. package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
  50. package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
  51. package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
  52. package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
  53. package/apps/shared/components/admin/diagnostics/format.ts +1 -1
  54. package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
  55. package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
  56. package/apps/shared/components/admin/elevation.ts +1 -1
  57. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
  58. package/apps/shared/components/admin/layout/AdminShell.tsx +384 -384
  59. package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +238 -238
  60. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +154 -152
  61. package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +194 -194
  62. package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
  63. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
  64. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
  65. package/apps/shared/components/admin/settings/BrandingForm.tsx +423 -423
  66. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
  67. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -119
  68. package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
  69. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
  70. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +198 -198
  71. package/apps/shared/components/admin/settings/PluginsManager.tsx +2093 -2093
  72. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +182 -182
  73. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -288
  74. package/apps/shared/components/admin/setup/SetupWizard.tsx +450 -446
  75. package/apps/shared/components/admin/signOut.ts +1 -1
  76. package/apps/shared/components/admin/usePagedList.tsx +129 -129
  77. package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
  78. package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
  79. package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
  80. package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
  81. package/apps/shared/components/calendar/EventDetails.tsx +16 -14
  82. package/apps/shared/components/calendar/EventEditor.tsx +54 -39
  83. package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
  84. package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
  85. package/apps/shared/components/calendar/EventModal.tsx +206 -206
  86. package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
  87. package/apps/shared/components/calendar/EventShell.tsx +1 -1
  88. package/apps/shared/components/calendar/FindATime.tsx +6 -4
  89. package/apps/shared/components/calendar/GuestInput.tsx +2 -2
  90. package/apps/shared/components/calendar/MonthView.tsx +185 -185
  91. package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
  92. package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
  93. package/apps/shared/components/calendar/RecurrenceEditor.tsx +227 -227
  94. package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
  95. package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
  96. package/apps/shared/components/calendar/SplitDayView.tsx +144 -144
  97. package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
  98. package/apps/shared/components/calendar/TimeGridView.tsx +246 -246
  99. package/apps/shared/components/calendar/allDay.ts +124 -124
  100. package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
  101. package/apps/shared/components/calendar/eventForm.ts +3 -3
  102. package/apps/shared/components/calendar/eventFormat.ts +3 -3
  103. package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
  104. package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
  105. package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
  106. package/apps/shared/components/contacts/ContactForm.tsx +393 -383
  107. package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
  108. package/apps/shared/components/contacts/ContactsToolbar.tsx +103 -103
  109. package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
  110. package/apps/shared/components/contacts/contactKeys.ts +3 -3
  111. package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
  112. package/apps/shared/components/escrow/layout/EscrowShell.tsx +155 -155
  113. package/apps/shared/components/forms/StringListField.tsx +1 -1
  114. package/apps/shared/components/layout/AppShell.tsx +465 -461
  115. package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
  116. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -398
  117. package/apps/shared/components/layout/MailboxProvisioning.tsx +170 -168
  118. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
  119. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -315
  120. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -84
  121. package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
  122. package/apps/shared/components/layout/UserMenu.tsx +441 -439
  123. package/apps/shared/components/mail/ConversationList.tsx +334 -332
  124. package/apps/shared/components/mail/ConversationThreadPane.tsx +624 -613
  125. package/apps/shared/components/mail/InviteCard.tsx +11 -9
  126. package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
  127. package/apps/shared/components/mail/MailAddress.tsx +1 -1
  128. package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
  129. package/apps/shared/components/mail/MailSelectionBar.tsx +240 -240
  130. package/apps/shared/components/mail/MenuButton.tsx +404 -404
  131. package/apps/shared/components/mail/MessageDetailPane.tsx +1759 -1757
  132. package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
  133. package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
  134. package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
  135. package/apps/shared/components/mail/compose/ComposeContext.tsx +323 -312
  136. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -422
  137. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1709 -1681
  138. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
  139. package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
  140. package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
  141. package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
  142. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -147
  143. package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
  144. package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -60
  145. package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
  146. package/apps/shared/components/mail/compose/quotedBody.ts +161 -161
  147. package/apps/shared/components/mail/compose/recipients.ts +1 -1
  148. package/apps/shared/components/mail/invite/InviteRowChip.tsx +2 -2
  149. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
  150. package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
  151. package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
  152. package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
  153. package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
  154. package/apps/shared/components/mail/labelMenu.tsx +9 -7
  155. package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
  156. package/apps/shared/components/mail/listPreferences.ts +2 -2
  157. package/apps/shared/components/mail/pinnedSigners.ts +45 -19
  158. package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
  159. package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
  160. package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
  161. package/apps/shared/components/mail/reading/MessageMoreMenu.tsx +258 -258
  162. package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +79 -79
  163. package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
  164. package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
  165. package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
  166. package/apps/shared/components/mail/reading/messageExport.ts +59 -59
  167. package/apps/shared/components/mail/reading/printMessage.ts +99 -99
  168. package/apps/shared/components/mail/reading/useMessageActions.ts +450 -443
  169. package/apps/shared/components/mail/unreadStyle.tsx +1 -1
  170. package/apps/shared/components/mail/verificationSeals.ts +129 -125
  171. package/apps/shared/components/mail/writableMailboxes.ts +14 -7
  172. package/apps/shared/components/rules/RuleBuilder.tsx +311 -311
  173. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -344
  174. package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
  175. package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
  176. package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
  177. package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
  178. package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
  179. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
  180. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -51
  181. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -62
  182. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -84
  183. package/apps/shared/keyboard/dispatch.ts +124 -124
  184. package/apps/shared/keyboard/format.ts +89 -89
  185. package/apps/shared/keyboard/keymap.ts +114 -114
  186. package/apps/shared/keyboard/registry.ts +65 -65
  187. package/apps/shared/keyboard/targets.ts +79 -79
  188. package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
  189. package/apps/shared/mail/folderCounts.ts +8 -4
  190. package/apps/shared/mail/folderOfType.ts +51 -49
  191. package/apps/shared/mail/folderTree.ts +143 -143
  192. package/apps/shared/mail/listAllPages.ts +39 -39
  193. package/apps/shared/mail/listSnapshots.ts +2 -2
  194. package/apps/shared/mail/messageReadState.ts +6 -5
  195. package/apps/shared/mail/newMailNotifications.ts +171 -171
  196. package/apps/shared/mail/outbox/composeBridge.ts +2 -2
  197. package/apps/shared/mail/outbox/outboxState.ts +1 -1
  198. package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
  199. package/apps/shared/mail/outbox/sendDecision.ts +2 -2
  200. package/apps/shared/mail/outbox/sendJob.ts +450 -445
  201. package/apps/shared/mail/outbox/sendOutcomes.ts +160 -155
  202. package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
  203. package/apps/shared/mail/permanentDelete.ts +8 -7
  204. package/apps/shared/mail/primaryMailbox.ts +1 -1
  205. package/apps/shared/mail/reportNotices.ts +66 -66
  206. package/apps/shared/mail/senderBlocking.ts +157 -141
  207. package/apps/shared/mail/useMailConnection.ts +207 -205
  208. package/apps/shared/mail/useMailLiveUpdates.ts +277 -277
  209. package/apps/shared/mail/useMailboxUpdateAccess.ts +84 -60
  210. package/apps/shared/mail/useMarkMessageRead.ts +54 -47
  211. package/apps/shared/mail/useNewMailNotifications.ts +178 -178
  212. package/apps/shared/mail/usePermanentDelete.tsx +5 -3
  213. package/apps/shared/navigation/pageTitle.ts +1 -1
  214. package/apps/shared/notifications/NotificationCenter.tsx +1 -1
  215. package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
  216. package/apps/shared/notifications/apiErrors.ts +2 -2
  217. package/apps/shared/notifications/pushStatus.ts +1 -1
  218. package/apps/shared/notifications/store.ts +560 -560
  219. package/apps/shared/notifications/systemErrors.ts +1 -1
  220. package/apps/shared/search/LocalIndexLifecycle.tsx +120 -114
  221. package/apps/shared/search/crossMailboxSearch.ts +3 -3
  222. package/apps/shared/search/localIndexBuilder.ts +493 -481
  223. package/apps/shared/search/localIndexKey.ts +1 -1
  224. package/apps/shared/search/localIndexRpcClient.ts +150 -30
  225. package/apps/shared/search/localIndexTransport.ts +84 -0
  226. package/apps/shared/search/localIndexWorker.ts +1 -1
  227. package/apps/shared/search/searchTier2.ts +3 -3
  228. package/apps/shared/signing/enrollmentStorage.ts +33 -33
  229. package/apps/shared/signing/enrollmentTracker.ts +385 -385
  230. package/apps/shared/signing/enrollmentView.ts +251 -251
  231. package/apps/shared/signing/signingInfo.ts +1 -1
  232. package/apps/shared/signing/useNow.ts +19 -19
  233. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +92 -90
  234. package/apps/shared/styles/app.css +387 -396
  235. package/apps/www/_layout.tsx +2 -2
  236. package/apps/www/calendar/index.tsx +583 -581
  237. package/apps/www/contacts/[uid].tsx +114 -112
  238. package/apps/www/contacts/index.tsx +19 -17
  239. package/apps/www/index.tsx +3021 -3012
  240. package/apps/www/messages/[uid].tsx +141 -139
  241. package/apps/www/settings/auto-reply/index.tsx +138 -136
  242. package/apps/www/settings/blocked-senders/index.tsx +305 -303
  243. package/apps/www/settings/encryption/index.tsx +1296 -1290
  244. package/apps/www/settings/filters/[uid].tsx +184 -179
  245. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
  246. package/apps/www/settings/filters/index.tsx +105 -105
  247. package/apps/www/settings/filters/new/index.tsx +167 -165
  248. package/apps/www/settings/labels/index.tsx +209 -207
  249. package/apps/www/settings/privacy/index.tsx +499 -495
  250. package/apps/www/settings/profile/index.tsx +256 -251
  251. package/apps/www/settings/read-receipts/index.tsx +155 -150
  252. package/apps/www/settings/sharing/index.tsx +259 -259
  253. package/apps/www/settings/signatures/[uid].tsx +175 -175
  254. package/apps/www/settings/signatures/index.tsx +91 -91
  255. package/apps/www/settings/signatures/new/index.tsx +138 -138
  256. package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
  257. package/apps/www/tasks/index.tsx +659 -654
  258. package/dist/apps/admin/_layout.d.ts +1 -1
  259. package/dist/apps/admin/_layout.js +1 -1
  260. package/dist/apps/admin/audit-log/index.d.ts +1 -1
  261. package/dist/apps/admin/audit-log/index.js +4 -4
  262. package/dist/apps/admin/branding/index.js +3 -3
  263. package/dist/apps/admin/data-requests/index.js +8 -8
  264. package/dist/apps/admin/distribution-lists/[uid].js +3 -3
  265. package/dist/apps/admin/distribution-lists/index.js +4 -4
  266. package/dist/apps/admin/distribution-lists/new/index.js +5 -5
  267. package/dist/apps/admin/domains/[uid].js +6 -6
  268. package/dist/apps/admin/domains/index.js +4 -4
  269. package/dist/apps/admin/domains/new/index.js +5 -5
  270. package/dist/apps/admin/encryption-policy/index.js +1 -1
  271. package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
  272. package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
  273. package/dist/apps/admin/escrow-scopes/index.js +4 -4
  274. package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
  275. package/dist/apps/admin/index.js +6 -6
  276. package/dist/apps/admin/ingest-queue/index.js +4 -4
  277. package/dist/apps/admin/mailbox-policy/index.js +1 -1
  278. package/dist/apps/admin/mailboxes/[uid].js +5 -5
  279. package/dist/apps/admin/quarantine/index.js +5 -5
  280. package/dist/apps/admin/retention-policy/index.js +3 -3
  281. package/dist/apps/admin/signing-certificates/index.js +5 -5
  282. package/dist/apps/admin/transport-rules/[uid].js +5 -5
  283. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
  284. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
  285. package/dist/apps/admin/transport-rules/index.js +4 -4
  286. package/dist/apps/admin/transport-rules/new/index.js +5 -5
  287. package/dist/apps/escrow/_layout.d.ts +1 -1
  288. package/dist/apps/escrow/audit-log/index.d.ts +1 -1
  289. package/dist/apps/escrow/audit-log/index.js +4 -4
  290. package/dist/apps/escrow/index.js +4 -4
  291. package/dist/apps/escrow/matters/[uid].js +10 -10
  292. package/dist/apps/escrow/matters/new/index.js +6 -6
  293. package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
  294. package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
  295. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
  296. package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
  297. package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
  298. package/dist/apps/shared/appearance/appearanceCache.js +1 -1
  299. package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
  300. package/dist/apps/shared/appearance/theme.d.ts +1 -1
  301. package/dist/apps/shared/auth/adminAccess.js +1 -1
  302. package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
  303. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
  304. package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
  305. package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
  306. package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
  307. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
  308. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
  309. package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
  310. package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
  311. package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
  312. package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
  313. package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
  314. package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
  315. package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
  316. package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
  317. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
  318. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
  319. package/dist/apps/shared/components/admin/elevation.js +1 -1
  320. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
  321. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  322. package/dist/apps/shared/components/admin/layout/AdminShell.js +8 -8
  323. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
  324. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
  325. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
  326. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
  327. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
  328. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
  329. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
  330. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
  331. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
  332. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
  333. package/dist/apps/shared/components/admin/settings/BrandingForm.js +8 -8
  334. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
  335. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
  336. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
  337. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
  338. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
  339. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
  340. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
  341. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
  342. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
  343. package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
  344. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
  345. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
  346. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
  347. package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
  348. package/dist/apps/shared/components/admin/signOut.js +1 -1
  349. package/dist/apps/shared/components/admin/usePagedList.js +2 -2
  350. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
  351. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
  352. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
  353. package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
  354. package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
  355. package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
  356. package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
  357. package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
  358. package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
  359. package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
  360. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
  361. package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
  362. package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
  363. package/dist/apps/shared/components/calendar/EventModal.js +3 -3
  364. package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
  365. package/dist/apps/shared/components/calendar/EventShell.js +1 -1
  366. package/dist/apps/shared/components/calendar/FindATime.js +6 -4
  367. package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
  368. package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
  369. package/dist/apps/shared/components/calendar/MonthView.js +1 -1
  370. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
  371. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
  372. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
  373. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
  374. package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
  375. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
  376. package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
  377. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
  378. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
  379. package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
  380. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
  381. package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
  382. package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
  383. package/dist/apps/shared/components/calendar/allDay.js +1 -1
  384. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
  385. package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
  386. package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
  387. package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
  388. package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
  389. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
  390. package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
  391. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  392. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
  393. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
  394. package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
  395. package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
  396. package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
  397. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
  398. package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
  399. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
  400. package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
  401. package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
  402. package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
  403. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  404. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
  405. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -7
  406. package/dist/apps/shared/components/forms/StringListField.js +1 -1
  407. package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
  408. package/dist/apps/shared/components/layout/AppShell.js +14 -10
  409. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
  410. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
  411. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
  412. package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
  413. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
  414. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
  415. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
  416. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
  417. package/dist/apps/shared/components/layout/UserMenu.js +6 -4
  418. package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
  419. package/dist/apps/shared/components/mail/ConversationList.js +6 -4
  420. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
  421. package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
  422. package/dist/apps/shared/components/mail/InviteCard.js +10 -8
  423. package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
  424. package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
  425. package/dist/apps/shared/components/mail/MailAddress.js +1 -1
  426. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
  427. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
  428. package/dist/apps/shared/components/mail/MenuButton.js +1 -1
  429. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
  430. package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
  431. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
  432. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
  433. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
  434. package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
  435. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
  436. package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
  437. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +7 -1
  438. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +54 -31
  439. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
  440. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
  441. package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
  442. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
  443. package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
  444. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
  445. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
  446. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
  447. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
  448. package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
  449. package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
  450. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
  451. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
  452. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
  453. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
  454. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
  455. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
  456. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
  457. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
  458. package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
  459. package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
  460. package/dist/apps/shared/components/mail/labelMenu.js +9 -7
  461. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
  462. package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
  463. package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
  464. package/dist/apps/shared/components/mail/listPreferences.js +1 -1
  465. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
  466. package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
  467. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
  468. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
  469. package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
  470. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
  471. package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
  472. package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
  473. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
  474. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
  475. package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
  476. package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
  477. package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
  478. package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
  479. package/dist/apps/shared/components/mail/reading/printMessage.js +11 -11
  480. package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
  481. package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
  482. package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
  483. package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
  484. package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
  485. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
  486. package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
  487. package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
  488. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
  489. package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
  490. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  491. package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
  492. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
  493. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
  494. package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
  495. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
  496. package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
  497. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
  498. package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
  499. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  500. package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
  501. package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
  502. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
  503. package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
  504. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  505. package/dist/apps/shared/mail/folderCounts.js +6 -4
  506. package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
  507. package/dist/apps/shared/mail/folderOfType.js +4 -4
  508. package/dist/apps/shared/mail/folderTree.d.ts +1 -1
  509. package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
  510. package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
  511. package/dist/apps/shared/mail/messageReadState.js +5 -5
  512. package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
  513. package/dist/apps/shared/mail/newMailNotifications.js +1 -1
  514. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
  515. package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
  516. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
  517. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
  518. package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
  519. package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
  520. package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
  521. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
  522. package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
  523. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
  524. package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
  525. package/dist/apps/shared/mail/permanentDelete.js +8 -8
  526. package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
  527. package/dist/apps/shared/mail/primaryMailbox.js +1 -1
  528. package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
  529. package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
  530. package/dist/apps/shared/mail/senderBlocking.js +19 -16
  531. package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
  532. package/dist/apps/shared/mail/useMailConnection.js +12 -10
  533. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
  534. package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
  535. package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
  536. package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
  537. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
  538. package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
  539. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
  540. package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
  541. package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
  542. package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
  543. package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
  544. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
  545. package/dist/apps/shared/notifications/apiErrors.js +2 -2
  546. package/dist/apps/shared/notifications/pushStatus.js +1 -1
  547. package/dist/apps/shared/notifications/systemErrors.js +1 -1
  548. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
  549. package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
  550. package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
  551. package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
  552. package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
  553. package/dist/apps/shared/search/localIndexBuilder.js +15 -12
  554. package/dist/apps/shared/search/localIndexKey.js +1 -1
  555. package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
  556. package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
  557. package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
  558. package/dist/apps/shared/search/localIndexTransport.js +1 -0
  559. package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
  560. package/dist/apps/shared/search/searchTier2.d.ts +3 -3
  561. package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
  562. package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
  563. package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
  564. package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
  565. package/dist/apps/shared/signing/signingInfo.js +1 -1
  566. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
  567. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
  568. package/dist/apps/shared/styles/app.css +387 -396
  569. package/dist/apps/www/_layout.d.ts +1 -1
  570. package/dist/apps/www/_layout.js +1 -1
  571. package/dist/apps/www/calendar/index.d.ts +1 -1
  572. package/dist/apps/www/calendar/index.js +20 -18
  573. package/dist/apps/www/contacts/[uid].d.ts +1 -1
  574. package/dist/apps/www/contacts/[uid].js +8 -6
  575. package/dist/apps/www/contacts/index.d.ts +1 -1
  576. package/dist/apps/www/contacts/index.js +19 -17
  577. package/dist/apps/www/index.d.ts +1 -1
  578. package/dist/apps/www/index.js +54 -50
  579. package/dist/apps/www/messages/[uid].d.ts +1 -1
  580. package/dist/apps/www/messages/[uid].js +11 -9
  581. package/dist/apps/www/settings/appearance/index.d.ts +1 -1
  582. package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
  583. package/dist/apps/www/settings/auto-reply/index.js +6 -4
  584. package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
  585. package/dist/apps/www/settings/blocked-senders/index.js +6 -4
  586. package/dist/apps/www/settings/encryption/index.d.ts +1 -1
  587. package/dist/apps/www/settings/encryption/index.js +31 -29
  588. package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
  589. package/dist/apps/www/settings/filters/[uid].js +11 -9
  590. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
  591. package/dist/apps/www/settings/filters/index.d.ts +1 -1
  592. package/dist/apps/www/settings/filters/index.js +4 -4
  593. package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
  594. package/dist/apps/www/settings/filters/new/index.js +10 -8
  595. package/dist/apps/www/settings/labels/index.d.ts +1 -1
  596. package/dist/apps/www/settings/labels/index.js +12 -10
  597. package/dist/apps/www/settings/privacy/index.d.ts +1 -1
  598. package/dist/apps/www/settings/privacy/index.js +18 -14
  599. package/dist/apps/www/settings/profile/index.d.ts +1 -1
  600. package/dist/apps/www/settings/profile/index.js +7 -5
  601. package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
  602. package/dist/apps/www/settings/read-receipts/index.js +5 -3
  603. package/dist/apps/www/settings/sharing/index.d.ts +1 -1
  604. package/dist/apps/www/settings/sharing/index.js +6 -6
  605. package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
  606. package/dist/apps/www/settings/signatures/[uid].js +5 -5
  607. package/dist/apps/www/settings/signatures/index.d.ts +1 -1
  608. package/dist/apps/www/settings/signatures/index.js +4 -4
  609. package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
  610. package/dist/apps/www/settings/signatures/new/index.js +4 -4
  611. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
  612. package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
  613. package/dist/apps/www/tasks/index.d.ts +1 -1
  614. package/dist/apps/www/tasks/index.js +17 -15
  615. package/dist/lib/admin/auditLogApi.d.ts +32 -0
  616. package/dist/lib/admin/auditLogApi.js +27 -0
  617. package/dist/lib/admin/distributionListsApi.d.ts +47 -0
  618. package/dist/lib/admin/distributionListsApi.js +34 -0
  619. package/dist/lib/admin/domainsApi.d.ts +75 -0
  620. package/dist/lib/admin/domainsApi.js +44 -0
  621. package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
  622. package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
  623. package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
  624. package/dist/lib/admin/escrowAuditLogApi.js +31 -0
  625. package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
  626. package/dist/lib/admin/escrowScopesApi.js +50 -0
  627. package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
  628. package/dist/lib/admin/leftoverMailboxApi.js +67 -0
  629. package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
  630. package/dist/lib/admin/mailboxPolicyApi.js +21 -0
  631. package/dist/lib/admin/matterExportApi.d.ts +40 -0
  632. package/dist/lib/admin/matterExportApi.js +39 -0
  633. package/dist/lib/admin/matterSearchApi.d.ts +20 -0
  634. package/dist/lib/admin/matterSearchApi.js +27 -0
  635. package/dist/lib/admin/mattersApi.d.ts +56 -0
  636. package/dist/lib/admin/mattersApi.js +40 -0
  637. package/dist/lib/admin/pluginsApi.d.ts +265 -0
  638. package/dist/lib/admin/pluginsApi.js +102 -0
  639. package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
  640. package/dist/lib/admin/retentionPolicyApi.js +29 -0
  641. package/dist/lib/admin/setupApi.d.ts +23 -0
  642. package/dist/lib/admin/setupApi.js +26 -0
  643. package/dist/lib/admin/transportRulesApi.d.ts +66 -0
  644. package/dist/lib/admin/transportRulesApi.js +43 -0
  645. package/dist/lib/appearance/preferencesApi.d.ts +128 -0
  646. package/dist/lib/appearance/preferencesApi.js +257 -0
  647. package/dist/lib/auth/profileApi.d.ts +33 -0
  648. package/dist/lib/auth/profileApi.js +52 -0
  649. package/dist/lib/auth/session.d.ts +70 -0
  650. package/dist/lib/auth/session.js +210 -0
  651. package/dist/lib/branding/brandingApi.d.ts +47 -0
  652. package/dist/lib/branding/brandingApi.js +61 -0
  653. package/dist/lib/branding/useBranding.d.ts +23 -0
  654. package/dist/lib/branding/useBranding.js +65 -0
  655. package/dist/lib/calendar/calendarApi.d.ts +230 -0
  656. package/dist/lib/calendar/calendarApi.js +106 -0
  657. package/dist/lib/calendar/calendarColors.d.ts +18 -0
  658. package/dist/lib/calendar/calendarColors.js +39 -0
  659. package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
  660. package/dist/lib/calendar/calendarDragIds.js +92 -0
  661. package/dist/lib/calendar/calendarMutations.d.ts +68 -0
  662. package/dist/lib/calendar/calendarMutations.js +190 -0
  663. package/dist/lib/calendar/eventDescription.d.ts +43 -0
  664. package/dist/lib/calendar/eventDescription.js +195 -0
  665. package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
  666. package/dist/lib/calendar/freeBusyApi.js +119 -0
  667. package/dist/lib/calendar/inviteApi.d.ts +121 -0
  668. package/dist/lib/calendar/inviteApi.js +54 -0
  669. package/dist/lib/calendar/recurrence.d.ts +60 -0
  670. package/dist/lib/calendar/recurrence.js +225 -0
  671. package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
  672. package/dist/lib/components/avatar/ContactAvatar.js +26 -0
  673. package/dist/lib/components/buttons/Button.d.ts +9 -0
  674. package/dist/lib/components/buttons/Button.js +14 -0
  675. package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
  676. package/dist/lib/components/buttons/CopyButton.js +14 -0
  677. package/dist/lib/components/feedback/Alert.d.ts +4 -0
  678. package/dist/lib/components/feedback/Alert.js +4 -0
  679. package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
  680. package/dist/lib/components/feedback/Skeleton.js +14 -0
  681. package/dist/lib/components/forms/FormField.d.ts +8 -0
  682. package/dist/lib/components/forms/FormField.js +5 -0
  683. package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
  684. package/dist/lib/components/navigation/BottomTabBar.js +32 -0
  685. package/dist/lib/components/overlays/Drawer.d.ts +20 -0
  686. package/dist/lib/components/overlays/Drawer.js +27 -0
  687. package/dist/lib/components/overlays/Modal.d.ts +17 -0
  688. package/dist/lib/components/overlays/Modal.js +25 -0
  689. package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
  690. package/dist/lib/components/overlays/PopoverPortal.js +53 -0
  691. package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
  692. package/dist/lib/components/overlays/overlayStack.js +143 -0
  693. package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
  694. package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
  695. package/dist/lib/contacts/contactsApi.d.ts +174 -0
  696. package/dist/lib/contacts/contactsApi.js +139 -0
  697. package/dist/lib/contacts/vcard.d.ts +23 -0
  698. package/dist/lib/contacts/vcard.js +179 -0
  699. package/dist/lib/crypto/composeSecurity.d.ts +67 -0
  700. package/dist/lib/crypto/composeSecurity.js +93 -0
  701. package/dist/lib/crypto/encoding.d.ts +9 -0
  702. package/dist/lib/crypto/encoding.js +32 -0
  703. package/dist/lib/crypto/escrowKeys.d.ts +25 -0
  704. package/dist/lib/crypto/escrowKeys.js +59 -0
  705. package/dist/lib/crypto/idleTimeout.d.ts +16 -0
  706. package/dist/lib/crypto/idleTimeout.js +47 -0
  707. package/dist/lib/crypto/keySession.d.ts +159 -0
  708. package/dist/lib/crypto/keySession.js +367 -0
  709. package/dist/lib/crypto/keys.d.ts +50 -0
  710. package/dist/lib/crypto/keys.js +89 -0
  711. package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
  712. package/dist/lib/crypto/keyvaultApi.js +350 -0
  713. package/dist/lib/crypto/masterKey.d.ts +59 -0
  714. package/dist/lib/crypto/masterKey.js +104 -0
  715. package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
  716. package/dist/lib/crypto/masterKeyWraps.js +228 -0
  717. package/dist/lib/crypto/messageSecurity.d.ts +248 -0
  718. package/dist/lib/crypto/messageSecurity.js +367 -0
  719. package/dist/lib/crypto/mime.d.ts +143 -0
  720. package/dist/lib/crypto/mime.js +504 -0
  721. package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
  722. package/dist/lib/crypto/passkeyUnlock.js +79 -0
  723. package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
  724. package/dist/lib/crypto/passwordUnlock.js +73 -0
  725. package/dist/lib/crypto/recoveryCode.d.ts +28 -0
  726. package/dist/lib/crypto/recoveryCode.js +86 -0
  727. package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
  728. package/dist/lib/crypto/signingProviderApi.js +60 -0
  729. package/dist/lib/crypto/smime.d.ts +128 -0
  730. package/dist/lib/crypto/smime.js +402 -0
  731. package/dist/lib/crypto/smimeMessage.d.ts +132 -0
  732. package/dist/lib/crypto/smimeMessage.js +341 -0
  733. package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
  734. package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
  735. package/dist/lib/crypto/verificationSeal.d.ts +56 -0
  736. package/dist/lib/crypto/verificationSeal.js +220 -0
  737. package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
  738. package/dist/lib/mail/compose/composeQuoting.js +204 -0
  739. package/dist/lib/mail/compose/emojiData.d.ts +9 -0
  740. package/dist/lib/mail/compose/emojiData.js +18 -0
  741. package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
  742. package/dist/lib/mail/compose/giphyApi.js +20 -0
  743. package/dist/lib/mail/conversationsApi.d.ts +80 -0
  744. package/dist/lib/mail/conversationsApi.js +44 -0
  745. package/dist/lib/mail/dataExportApi.d.ts +50 -0
  746. package/dist/lib/mail/dataExportApi.js +40 -0
  747. package/dist/lib/mail/directoryApi.d.ts +49 -0
  748. package/dist/lib/mail/directoryApi.js +101 -0
  749. package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
  750. package/dist/lib/mail/erasureRequestApi.js +43 -0
  751. package/dist/lib/mail/flaggedMessages.d.ts +22 -0
  752. package/dist/lib/mail/flaggedMessages.js +73 -0
  753. package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
  754. package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
  755. package/dist/lib/mail/labelsApi.d.ts +47 -0
  756. package/dist/lib/mail/labelsApi.js +38 -0
  757. package/dist/lib/mail/mailAddress.d.ts +27 -0
  758. package/dist/lib/mail/mailAddress.js +54 -0
  759. package/dist/lib/mail/mailApi.d.ts +850 -0
  760. package/dist/lib/mail/mailApi.js +602 -0
  761. package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
  762. package/dist/lib/mail/mailDetailHooks.js +53 -0
  763. package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
  764. package/dist/lib/mail/mailFilterRulesApi.js +45 -0
  765. package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
  766. package/dist/lib/mail/mailSignaturesApi.js +42 -0
  767. package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
  768. package/dist/lib/mail/mailboxAccessApi.js +56 -0
  769. package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
  770. package/dist/lib/mail/mailboxImportApi.js +51 -0
  771. package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
  772. package/dist/lib/mail/messageBodySanitizer.js +118 -0
  773. package/dist/lib/mail/pushClient.d.ts +106 -0
  774. package/dist/lib/mail/pushClient.js +317 -0
  775. package/dist/lib/mail/sendEvents.d.ts +33 -0
  776. package/dist/lib/mail/sendEvents.js +44 -0
  777. package/dist/lib/mail/sendFailure.d.ts +16 -0
  778. package/dist/lib/mail/sendFailure.js +86 -0
  779. package/dist/lib/mail/senderListsApi.d.ts +69 -0
  780. package/dist/lib/mail/senderListsApi.js +126 -0
  781. package/dist/lib/search/queryGrammar.d.ts +51 -0
  782. package/dist/lib/search/queryGrammar.js +111 -0
  783. package/dist/lib/search/searchApi.d.ts +100 -0
  784. package/dist/lib/search/searchApi.js +107 -0
  785. package/dist/lib/search/searchScoring.d.ts +34 -0
  786. package/dist/lib/search/searchScoring.js +44 -0
  787. package/dist/lib/search/searchTier3.d.ts +64 -0
  788. package/dist/lib/search/searchTier3.js +335 -0
  789. package/dist/lib/tasks/tasksApi.d.ts +85 -0
  790. package/dist/lib/tasks/tasksApi.js +55 -0
  791. package/dist/lib/util/api.d.ts +160 -0
  792. package/dist/lib/util/api.js +251 -0
  793. package/dist/lib/util/apiClientContext.d.ts +7 -0
  794. package/dist/lib/util/apiClientContext.js +32 -0
  795. package/dist/lib/util/apiQuery.d.ts +32 -0
  796. package/dist/lib/util/apiQuery.js +42 -0
  797. package/dist/lib/util/clipboard.d.ts +7 -0
  798. package/dist/lib/util/clipboard.js +56 -0
  799. package/dist/lib/util/dateInput.d.ts +6 -0
  800. package/dist/lib/util/dateInput.js +14 -0
  801. package/dist/lib/util/timeZone.d.ts +12 -0
  802. package/dist/lib/util/timeZone.js +32 -0
  803. package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
  804. package/dist/lib/util/useCopyToClipboard.js +34 -0
  805. package/dist/lib/util/useIsMobile.d.ts +10 -0
  806. package/dist/lib/util/useIsMobile.js +29 -0
  807. package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
  808. package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
  809. package/lib/admin/auditLogApi.ts +48 -0
  810. package/lib/admin/distributionListsApi.ts +77 -0
  811. package/lib/admin/domainsApi.ts +113 -0
  812. package/lib/admin/escrowAccessRequestsApi.ts +110 -0
  813. package/lib/admin/escrowAuditLogApi.ts +101 -0
  814. package/lib/admin/escrowScopesApi.ts +112 -0
  815. package/lib/admin/leftoverMailboxApi.ts +120 -0
  816. package/lib/admin/mailboxPolicyApi.ts +38 -0
  817. package/lib/admin/matterExportApi.ts +62 -0
  818. package/lib/admin/matterSearchApi.ts +35 -0
  819. package/lib/admin/mattersApi.ts +86 -0
  820. package/lib/admin/pluginsApi.ts +339 -0
  821. package/lib/admin/retentionPolicyApi.ts +46 -0
  822. package/lib/admin/setupApi.ts +40 -0
  823. package/lib/admin/transportRulesApi.ts +106 -0
  824. package/lib/appearance/preferencesApi.ts +335 -0
  825. package/lib/auth/profileApi.ts +77 -0
  826. package/lib/auth/session.ts +238 -0
  827. package/lib/branding/brandingApi.ts +97 -0
  828. package/lib/branding/useBranding.ts +81 -0
  829. package/lib/calendar/calendarApi.ts +309 -0
  830. package/lib/calendar/calendarColors.ts +45 -0
  831. package/lib/calendar/calendarDragIds.ts +112 -0
  832. package/lib/calendar/calendarMutations.ts +236 -0
  833. package/lib/calendar/eventDescription.ts +211 -0
  834. package/lib/calendar/freeBusyApi.ts +197 -0
  835. package/lib/calendar/inviteApi.ts +154 -0
  836. package/lib/calendar/recurrence.ts +285 -0
  837. package/lib/components/avatar/ContactAvatar.tsx +47 -0
  838. package/lib/components/buttons/Button.tsx +40 -0
  839. package/lib/components/buttons/CopyButton.tsx +45 -0
  840. package/lib/components/feedback/Alert.tsx +13 -0
  841. package/lib/components/feedback/Skeleton.tsx +38 -0
  842. package/lib/components/forms/FormField.tsx +23 -0
  843. package/lib/components/navigation/BottomTabBar.tsx +66 -0
  844. package/lib/components/overlays/Drawer.tsx +72 -0
  845. package/lib/components/overlays/Modal.tsx +64 -0
  846. package/lib/components/overlays/PopoverPortal.tsx +74 -0
  847. package/lib/components/overlays/overlayStack.ts +165 -0
  848. package/lib/components/pickers/MiniDatePicker.tsx +90 -0
  849. package/lib/contacts/contactsApi.ts +289 -0
  850. package/lib/contacts/vcard.ts +197 -0
  851. package/lib/crypto/composeSecurity.ts +140 -0
  852. package/lib/crypto/encoding.ts +36 -0
  853. package/lib/crypto/escrowKeys.ts +74 -0
  854. package/lib/crypto/idleTimeout.ts +49 -0
  855. package/lib/crypto/keySession.ts +483 -0
  856. package/lib/crypto/keys.ts +105 -0
  857. package/lib/crypto/keyvaultApi.ts +674 -0
  858. package/lib/crypto/masterKey.ts +134 -0
  859. package/lib/crypto/masterKeyWraps.ts +269 -0
  860. package/lib/crypto/messageSecurity.ts +600 -0
  861. package/lib/crypto/mime.ts +568 -0
  862. package/lib/crypto/passkeyUnlock.ts +103 -0
  863. package/lib/crypto/passwordUnlock.ts +98 -0
  864. package/lib/crypto/recoveryCode.ts +93 -0
  865. package/lib/crypto/signingProviderApi.ts +135 -0
  866. package/lib/crypto/smime.ts +459 -0
  867. package/lib/crypto/smimeMessage.ts +491 -0
  868. package/lib/crypto/useIdleKeyTimeout.ts +176 -0
  869. package/lib/crypto/verificationSeal.ts +288 -0
  870. package/lib/mail/compose/composeQuoting.ts +243 -0
  871. package/lib/mail/compose/emojiData.ts +34 -0
  872. package/lib/mail/compose/giphyApi.ts +31 -0
  873. package/lib/mail/conversationsApi.ts +123 -0
  874. package/lib/mail/dataExportApi.ts +73 -0
  875. package/lib/mail/directoryApi.ts +161 -0
  876. package/lib/mail/erasureRequestApi.ts +71 -0
  877. package/lib/mail/flaggedMessages.ts +80 -0
  878. package/lib/mail/focusedInboxOverridesApi.ts +64 -0
  879. package/lib/mail/labelsApi.ts +71 -0
  880. package/lib/mail/mailAddress.ts +76 -0
  881. package/lib/mail/mailApi.ts +1286 -0
  882. package/lib/mail/mailDetailHooks.ts +57 -0
  883. package/lib/mail/mailFilterRulesApi.ts +116 -0
  884. package/lib/mail/mailSignaturesApi.ts +84 -0
  885. package/lib/mail/mailboxAccessApi.ts +106 -0
  886. package/lib/mail/mailboxImportApi.ts +87 -0
  887. package/lib/mail/messageBodySanitizer.ts +129 -0
  888. package/lib/mail/pushClient.ts +375 -0
  889. package/lib/mail/sendEvents.ts +72 -0
  890. package/lib/mail/sendFailure.ts +98 -0
  891. package/lib/mail/senderListsApi.ts +161 -0
  892. package/lib/search/queryGrammar.ts +179 -0
  893. package/lib/search/searchApi.ts +199 -0
  894. package/lib/search/searchScoring.ts +46 -0
  895. package/lib/search/searchTier3.ts +406 -0
  896. package/lib/tasks/tasksApi.ts +128 -0
  897. package/lib/util/api.ts +318 -0
  898. package/lib/util/apiClientContext.ts +35 -0
  899. package/lib/util/apiQuery.ts +61 -0
  900. package/lib/util/clipboard.ts +55 -0
  901. package/lib/util/dateInput.ts +15 -0
  902. package/lib/util/timeZone.ts +33 -0
  903. package/lib/util/useCopyToClipboard.ts +48 -0
  904. package/lib/util/useIsMobile.ts +34 -0
  905. package/lib/videoconf/videoMeetingsApi.ts +144 -0
  906. package/package.json +10 -4
@@ -0,0 +1,74 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * Generates an escrow scope's key pair in the browser, for an administrator setting up escrow without an existing
7
+ * PKI. The certificate goes to the server (as the scope's public key, which mailbox master keys are wrapped to);
8
+ * the private key never does - the administrator must download it and hand it to the scope's holders, whose
9
+ * offline tooling is the only thing that can ever unwrap an escrowed master key. Losing it makes every escrow
10
+ * wrap made to this scope permanently unusable.
11
+ *
12
+ * The key is P-256, like every other key in this app: the certificate is signed with it as ECDSA, and holders
13
+ * re-import the same key material as ECDH to decrypt (see `keys.ts`'s `importPrivateKeyPkcs8()`).
14
+ */
15
+ import "reflect-metadata";
16
+ import * as x509 from "@peculiar/x509";
17
+ import type { EscrowScopePublicKey } from "../admin/escrowScopesApi.js";
18
+ import { toBase64 } from "./encoding.js";
19
+ import { computeCertFingerprint } from "./smime.js";
20
+
21
+ x509.cryptoProvider.set(crypto);
22
+
23
+ const DAY_MS = 24 * 60 * 60 * 1000;
24
+
25
+ export interface GeneratedEscrowKeys {
26
+ certificateDer: Uint8Array;
27
+ certificatePem: string;
28
+ /** PKCS#8, PEM encoded. */
29
+ privateKeyPem: string;
30
+ /** Ready to pass as `createEscrowScope()`'s `publicKey`. */
31
+ publicKey: EscrowScopePublicKey;
32
+ }
33
+
34
+ function toPem(label: string, der: Uint8Array): string {
35
+ const lines = toBase64(der).match(/.{1,64}/g) ?? [];
36
+ return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
37
+ }
38
+
39
+ /** A random, positive certificate serial number, hex encoded. */
40
+ function randomSerial(): string {
41
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
42
+ bytes[0] &= 0x7f;
43
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
44
+ }
45
+
46
+ export async function generateEscrowKeyPair(options: { name: string; validDays: number; now?: Date }): Promise<GeneratedEscrowKeys> {
47
+ const notBefore = options.now ?? new Date();
48
+ const notAfter = new Date(notBefore.getTime() + options.validDays * DAY_MS);
49
+ const keys = (await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]));
50
+ const certificate = await x509.X509CertificateGenerator.createSelfSigned({
51
+ serialNumber: randomSerial(),
52
+ // Structural name, never string-interpolated - see keys.ts's identical note on RDN injection.
53
+ name: [{ CN: [options.name] }],
54
+ notBefore,
55
+ notAfter,
56
+ signingAlgorithm: { name: "ECDSA", hash: "SHA-256" },
57
+ keys,
58
+ extensions: [new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyAgreement | x509.KeyUsageFlags.digitalSignature, true)],
59
+ });
60
+ const certificateDer = new Uint8Array(certificate.rawData);
61
+ const privateKeyDer = new Uint8Array(await crypto.subtle.exportKey("pkcs8", keys.privateKey));
62
+ return {
63
+ certificateDer,
64
+ certificatePem: toPem("CERTIFICATE", certificateDer),
65
+ privateKeyPem: toPem("PRIVATE KEY", privateKeyDer),
66
+ publicKey: {
67
+ publicKey: toBase64(certificateDer),
68
+ type: "x509",
69
+ fingerprint: await computeCertFingerprint(certificateDer),
70
+ notBefore: notBefore.getTime(),
71
+ notAfter: notAfter.getTime(),
72
+ },
73
+ };
74
+ }
@@ -0,0 +1,49 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * The configured idle-timeout duration behind `useIdleKeyTimeout()` — the spec's own "configurable
7
+ * idle period" trigger for `keySession.ts`'s `destroyUnlockedKeys()`. Stored in `localStorage`, not
8
+ * synced to the server: it's a per-device preference about *this browser's* own in-memory key cache,
9
+ * the same "never persisted anywhere durable" posture `keySession.ts` itself takes for the keys this
10
+ * setting protects.
11
+ */
12
+ const STORAGE_KEY = "rapidmx:idle-timeout-minutes";
13
+
14
+ /** The default for a device that has never set a preference - conservative enough for a shared/public
15
+ * machine without being disruptive on a personal one. */
16
+ export const DEFAULT_IDLE_TIMEOUT_MINUTES = 30;
17
+
18
+ /** `0` means "never" - the idle timer is disabled entirely. */
19
+ export const IDLE_TIMEOUT_OPTIONS_MINUTES = [5, 15, 30, 60, 0] as const;
20
+
21
+ /**
22
+ * Reads this device's configured idle-timeout duration, in minutes. Falls back to
23
+ * `DEFAULT_IDLE_TIMEOUT_MINUTES` for a never-configured device, a corrupted/non-numeric stored value,
24
+ * or a `localStorage` access that throws (private-browsing/storage-blocked contexts) - never throws
25
+ * itself, since a broken read here must not be able to prevent the app from rendering at all.
26
+ */
27
+ export function getIdleTimeoutMinutes(): number {
28
+ try {
29
+ const stored = localStorage.getItem(STORAGE_KEY);
30
+ if (stored === null) {
31
+ return DEFAULT_IDLE_TIMEOUT_MINUTES;
32
+ }
33
+ const parsed = Number(stored);
34
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_IDLE_TIMEOUT_MINUTES;
35
+ } catch {
36
+ return DEFAULT_IDLE_TIMEOUT_MINUTES;
37
+ }
38
+ }
39
+
40
+ /** Persists this device's idle-timeout preference. A `localStorage` write failure is swallowed, not
41
+ * thrown - the setting just doesn't survive a reload in that case, same fallback-to-default behavior
42
+ * `getIdleTimeoutMinutes()` already has for a storage-blocked context. */
43
+ export function setIdleTimeoutMinutes(minutes: number): void {
44
+ try {
45
+ localStorage.setItem(STORAGE_KEY, String(minutes));
46
+ } catch {
47
+ // Best-effort - see this function's own doc comment.
48
+ }
49
+ }
@@ -0,0 +1,483 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * The in-memory session key store `specs/end-to-end_encryption.md`'s "Keypair Generation & Storage"
7
+ * requires: "When a user logs in ... the private keys MUST be downloaded from the server, decrypted,
8
+ * and stored locally on the device in a secure location. When the user explicitly logs out ..., the
9
+ * private keys MUST be destroyed." This module is that store — a plain module-level `Map`, deliberately
10
+ * never `localStorage`/IndexedDB (a page reload or new tab correctly starts with no unlocked keys,
11
+ * requiring the unlock credential again, matching "destroyed on explicit logout" while never
12
+ * persisting unwrapped key material anywhere durable).
13
+ *
14
+ * `KeyEnrollmentGate` (in `web-client`) is what actually prompts for the unlock credential and calls
15
+ * `unlockWithPassword()` below, once per mailbox per session, before rendering the mailbox's real
16
+ * content — see that component's own doc comment.
17
+ *
18
+ * **Password and recovery code are the unlock methods implemented so far** (`unlockWithPassword()`,
19
+ * `unlockWithRecoveryCode()`, which share one master-key-to-session step). Passkey (`passkeyUnlock.ts`)
20
+ * derivation exists and could unwrap the same `MasterKeyWrap` shape, but nothing calls it yet — a real
21
+ * follow-up, not a silent gap: a mailbox enrolled *only* with a passkey has no way to unlock through this
22
+ * module today.
23
+ */
24
+ import { type KeyVault, type PublicKey, type WrappedPrivateKey, findActivePublicKey, getKeyVault } from "./keyvaultApi.js";
25
+ import { fromBase64 } from "./encoding.js";
26
+ import { importPrivateKeyPkcs8 } from "./keys.js";
27
+ import { KeysLockedError, buildAad, openWithKey } from "./masterKey.js";
28
+ import { deriveFromPassword, parseArgon2idKdfLabel } from "./passwordUnlock.js";
29
+ import { RECOVERY_KDF_LABEL, deriveFromRecoveryCode } from "./recoveryCode.js";
30
+
31
+ /** AAD purpose labels — MUST exactly match what `KeyEnrollmentGate.tsx` used when it originally
32
+ * sealed each of these values, or `openWithKey()` fails (GCM authenticates the AAD, not just the
33
+ * ciphertext). Centralized here as the one place both wrapping and unwrapping refer to. */
34
+ export const MASTER_KEY_AAD_PURPOSE = "master-key";
35
+ export const SIGNING_PRIVATE_KEY_AAD_PURPOSE = "sign-private-key";
36
+ export const ENCRYPTION_PRIVATE_KEY_AAD_PURPOSE = "encrypt-private-key";
37
+
38
+ export { KeysLockedError };
39
+
40
+ /** How many retained (non-active) encryption keys an unlock opens at most: the most recently issued ones by
41
+ * `notBefore`. Each costs an AEAD open and a key import at unlock, and `smime.ts`'s `decryptEnvelopedDataWithKeys()`
42
+ * considers the active key plus this many (`MAX_DECRYPTION_KEYS`). Mail encrypted to a key older than the 20 most
43
+ * recent retained ones doesn't decrypt on this device; its wrapped key stays in the vault. */
44
+ export const MAX_RETAINED_ENCRYPTION_KEYS = 20;
45
+
46
+ /** An older encryption key an unlock opened besides the active one - see `UnlockedKeys.retainedEncryptionKeys`. */
47
+ export interface RetainedEncryptionKey {
48
+ fingerprint: string;
49
+ /** Base64-decoded DER certificate, from the mailbox's published `PublicKey.publicKey`. */
50
+ certDer: Uint8Array;
51
+ /** Non-extractable ECDH private key. */
52
+ privateKey: CryptoKey;
53
+ }
54
+
55
+ export interface UnlockedKeys {
56
+ /** `true` once `destroyUnlockedKeys()` has destroyed this object's keys (master key zeroed, private key
57
+ * handles dropped). A caller holding an `UnlockedKeys` across an `await` or a user action must treat a
58
+ * destroyed object as locked and re-read `getUnlockedKeys()` instead - `masterKey.ts`'s helpers throw
59
+ * `KeysLockedError` when handed its zeroed master key. Never set on an object still in the store. */
60
+ destroyed?: boolean;
61
+ masterKey: Uint8Array;
62
+ signingPrivateKey?: CryptoKey;
63
+ signingCertDer?: Uint8Array;
64
+ signingFingerprint?: string;
65
+ encryptionPrivateKey?: CryptoKey;
66
+ encryptionCertDer?: Uint8Array;
67
+ encryptionFingerprint?: string;
68
+ /** Every non-active encryption key the mailbox still has a wrapped private key for and a published `encrypt` key
69
+ * listed in its keys - superseded, expired *and* compromised ones - newest first, at most
70
+ * `MAX_RETAINED_ENCRYPTION_KEYS`. The spec retains an old encryption private key indefinitely on rotation, because
71
+ * discarding it makes old mail unreadable. **Decryption only:** `evaluateMessageSecurity()` tries these after the
72
+ * active key; nothing encrypts to or signs with them (`findActivePublicKey()` still picks the only key used for
73
+ * that). A compromised key is kept because reading one's own mail encrypted to it is still needed. Absent when there
74
+ * are none. */
75
+ retainedEncryptionKeys?: RetainedEncryptionKey[];
76
+ }
77
+
78
+ const sessions = new Map<string, UnlockedKeys>();
79
+
80
+ /** The one method of `WeakRef` this module uses - typed locally because the build's `ES2020` lib predates it. */
81
+ interface WeakHandle<T> {
82
+ deref(): T | undefined;
83
+ }
84
+
85
+ /** A weak reference to `target` where the runtime has `WeakRef` (every supported browser and Node), otherwise a
86
+ * strong one - a missing `WeakRef` must never make a lock skip an object. */
87
+ function weakHandle<T extends object>(target: T): WeakHandle<T> {
88
+ const WeakRefCtor = (globalThis as { WeakRef?: new (target: T) => WeakHandle<T> }).WeakRef;
89
+ return WeakRefCtor ? new WeakRefCtor(target) : { deref: () => target };
90
+ }
91
+
92
+ /** Every `UnlockedKeys` object this store has handed out per mailbox and not yet destroyed - a re-unlock
93
+ * replaces the store entry but leaves the previous object usable for in-flight consumers, so a lock must
94
+ * reach all of them, not only the newest. Held *weakly*: the current object is kept alive by `sessions`, and a
95
+ * replaced one (e.g. holding a pre-rotation master key) only for as long as some consumer still references it -
96
+ * once nothing does, it can be collected instead of lingering until the next lock. A lock destroys every one
97
+ * still reachable, which is every one anybody could still use. */
98
+ const issued = new Map<string, Set<WeakHandle<UnlockedKeys>>>();
99
+ /** Bumped by every `destroyUnlockedKeys()` call for that mailbox (`lockAllGeneration` for a destroy-all),
100
+ * so an unlock still awaiting the vault/KDF when a lock happens can tell and discard what it unwrapped. */
101
+ const lockGenerations = new Map<string, number>();
102
+ let lockAllGeneration = 0;
103
+
104
+ function lockGeneration(mailboxUid: string): string {
105
+ return `${lockAllGeneration}:${lockGenerations.get(mailboxUid) ?? 0}`;
106
+ }
107
+
108
+ /** What changed in the session store — passed to every `subscribeKeySession()` listener. */
109
+ export interface KeySessionEvent {
110
+ mailboxUid: string;
111
+ /** `"unlocked"` when keys were stored (a first unlock, or a re-unlock replacing existing keys, e.g.
112
+ * after a key rotation); `"locked"` when they were destroyed. */
113
+ state: "unlocked" | "locked";
114
+ }
115
+
116
+ export type KeySessionListener = (event: KeySessionEvent) => void;
117
+
118
+ const listeners = new Set<KeySessionListener>();
119
+
120
+ function notify(event: KeySessionEvent): void {
121
+ // Iterate a snapshot so a listener that unsubscribes (or subscribes another) mid-dispatch can't skip
122
+ // or double-deliver. A throwing listener must never stop the rest - especially on "locked", where a
123
+ // later listener may be the one clearing decrypted content from the screen - so its error is
124
+ // rethrown asynchronously instead, still surfacing as an uncaught error without aborting delivery.
125
+ for (const listener of [...listeners]) {
126
+ try {
127
+ listener(event);
128
+ } catch (err) {
129
+ queueMicrotask(() => {
130
+ throw err;
131
+ });
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Subscribes to session store changes: fires once per mailbox whenever its keys are unlocked (stored)
138
+ * or destroyed, so a UI can e.g. clear already-decrypted content the moment keys go away instead of
139
+ * polling `getUnlockedKeys()`. A `destroyUnlockedKeys()` call that finds nothing to destroy fires
140
+ * nothing. Returns an unsubscribe function.
141
+ */
142
+ export function subscribeKeySession(listener: KeySessionListener): () => void {
143
+ listeners.add(listener);
144
+ return () => {
145
+ listeners.delete(listener);
146
+ };
147
+ }
148
+
149
+ /** Reads back a mailbox's already-unlocked keys this session, or `undefined` if it hasn't been
150
+ * unlocked yet (or was destroyed). Never triggers an unlock itself. */
151
+ export function getUnlockedKeys(mailboxUid: string): UnlockedKeys | undefined {
152
+ return sessions.get(mailboxUid);
153
+ }
154
+
155
+ /**
156
+ * Destroys one mailbox's unlocked keys, or every mailbox's if called with no argument — the spec's
157
+ * "destroyed on explicit logout" / "session revocation" / "configurable idle period" triggers.
158
+ *
159
+ * The master key's bytes are overwritten with zeros in place before the entry is dropped, so any
160
+ * `UnlockedKeys` object a caller is still holding no longer carries usable key material either (and the
161
+ * bytes don't linger until garbage collection). That object is also marked `destroyed: true` and its
162
+ * private key `CryptoKey` handles are removed (their material is never exposed to JS, so there is nothing
163
+ * to zero - but a stale holder must not keep signing/decrypting with them after a lock). Sealing/opening
164
+ * with the zeroed master key throws `KeysLockedError` (see `masterKey.ts`).
165
+ *
166
+ * Every object handed out for the mailbox is destroyed - including ones a re-unlock already replaced in the
167
+ * store - and an `unlockWithPassword()`/`unlockWithRecoveryCode()` still in flight for it when this runs throws `KeysLockedError`
168
+ * instead of restoring keys after the lock.
169
+ */
170
+ export function destroyUnlockedKeys(mailboxUid?: string): void {
171
+ if (mailboxUid) {
172
+ lockGenerations.set(mailboxUid, (lockGenerations.get(mailboxUid) ?? 0) + 1);
173
+ } else {
174
+ lockAllGeneration++;
175
+ }
176
+ const uids = mailboxUid ? [mailboxUid] : [...issued.keys()];
177
+ for (const uid of uids) {
178
+ const objects = issued.get(uid);
179
+ if (!objects) {
180
+ continue;
181
+ }
182
+ for (const handle of objects) {
183
+ const unlocked = handle.deref();
184
+ if (unlocked) {
185
+ destroyObject(unlocked);
186
+ }
187
+ }
188
+ issued.delete(uid);
189
+ sessions.delete(uid);
190
+ notify({ mailboxUid: uid, state: "locked" });
191
+ }
192
+ }
193
+
194
+ function destroyObject(unlocked: UnlockedKeys): void {
195
+ unlocked.masterKey.fill(0);
196
+ unlocked.destroyed = true;
197
+ delete unlocked.signingPrivateKey;
198
+ delete unlocked.encryptionPrivateKey;
199
+ if (unlocked.retainedEncryptionKeys) {
200
+ // Emptied in place as well as removed, so a consumer that captured the array itself loses the handles too.
201
+ unlocked.retainedEncryptionKeys.length = 0;
202
+ delete unlocked.retainedEncryptionKeys;
203
+ }
204
+ }
205
+
206
+ /** Finds the wrapped private key whose fingerprint matches a given published public key. */
207
+ function findWrappedPrivateKey(vault: KeyVault, fingerprint: string) {
208
+ return vault.wrappedKeys.find((k) => k.fingerprint === fingerprint);
209
+ }
210
+
211
+ /** What `unlockWithPassword()` resolves with (and the base of `unlockWithRecoveryCode()`'s result). */
212
+ export interface UnlockResult {
213
+ /** Fingerprints of the active *signing* key and any retained (non-active) *encryption* keys whose wrapped private
214
+ * key couldn't be opened or imported with the (correctly unwrapped) master key - e.g. a wrap sealed under a master
215
+ * key a later rekey replaced. The unlock still succeeds without them (no `signingPrivateKey`; the retained key is
216
+ * left out of `retainedEncryptionKeys`), so a user isn't locked out of reading mail by a key they can't use. Empty
217
+ * when every key opened. */
218
+ unopenableKeys: string[];
219
+ }
220
+
221
+ /**
222
+ * Thrown by `unlockWithPassword()` when the password was right (the master key unwrapped) but the active
223
+ * *encryption* key's wrapped private key couldn't be opened or imported with it. Distinct from the
224
+ * wrong-password failure (which rejects with the underlying AEAD error from opening the password wrap), so a
225
+ * UI can say "your encryption key can't be opened" instead of "incorrect password".
226
+ */
227
+ export class UnopenableEncryptionKeyError extends Error {
228
+ public readonly fingerprint: string;
229
+ /** The underlying AEAD/import failure. */
230
+ public readonly cause: unknown;
231
+
232
+ constructor(fingerprint: string, cause?: unknown) {
233
+ super(`The encryption key ${fingerprint} for this mailbox couldn't be opened with its master key.`);
234
+ this.name = "UnopenableEncryptionKeyError";
235
+ this.fingerprint = fingerprint;
236
+ this.cause = cause;
237
+ }
238
+ }
239
+
240
+ /** Opens one wrapped PKCS#8 private key under `masterKey` and imports it (non-extractable by default, like
241
+ * `importPrivateKeyPkcs8()` itself), zeroing the transient plaintext whether or not the import succeeds. */
242
+ async function openPrivateKey(
243
+ masterKey: Uint8Array,
244
+ wrapped: WrappedPrivateKey,
245
+ aad: Uint8Array,
246
+ algorithm: EcKeyImportParams,
247
+ usages: KeyUsage[],
248
+ extractable = false,
249
+ ): Promise<CryptoKey> {
250
+ const raw = await openWithKey(masterKey, wrapped, aad);
251
+ try {
252
+ return await importPrivateKeyPkcs8(raw, algorithm, usages, extractable);
253
+ } finally {
254
+ raw.fill(0);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Opens the retained encryption keys onto `unlocked.retainedEncryptionKeys`: every published `encrypt` key in
260
+ * `mailboxKeys` other than the active one (`activeFingerprint`), whatever its revocation or expiry, that has a wrapped
261
+ * private key in the vault - de-duplicated by fingerprint, newest `notBefore` first, at most
262
+ * `MAX_RETAINED_ENCRYPTION_KEYS`. A key that won't open or import, or whose certificate won't decode, is skipped and
263
+ * added to `unopenableKeys`. Retained keys are imported non-extractable. Each key is attached as it opens, so a failed
264
+ * unlock's `destroyObject()` drops those too.
265
+ */
266
+ async function openRetainedEncryptionKeys(
267
+ mailboxUid: string,
268
+ mailboxKeys: PublicKey[],
269
+ vault: KeyVault,
270
+ unlocked: UnlockedKeys,
271
+ activeFingerprint: string | undefined,
272
+ unopenableKeys: string[],
273
+ ): Promise<void> {
274
+ const seen = new Set<string>(activeFingerprint !== undefined ? [activeFingerprint] : []);
275
+ const candidates: { publicKey: PublicKey; wrapped: WrappedPrivateKey }[] = [];
276
+ for (const publicKey of [...mailboxKeys].sort((a, b) => b.notBefore - a.notBefore)) {
277
+ const wrapped = publicKey.useType === "encrypt" && !seen.has(publicKey.fingerprint) ? findWrappedPrivateKey(vault, publicKey.fingerprint) : undefined;
278
+ if (!wrapped) {
279
+ continue;
280
+ }
281
+ seen.add(publicKey.fingerprint);
282
+ candidates.push({ publicKey, wrapped });
283
+ if (candidates.length === MAX_RETAINED_ENCRYPTION_KEYS) {
284
+ break;
285
+ }
286
+ }
287
+
288
+ const aad = buildAad(mailboxUid, ENCRYPTION_PRIVATE_KEY_AAD_PURPOSE);
289
+ for (const { publicKey, wrapped } of candidates) {
290
+ try {
291
+ const certDer = fromBase64(publicKey.publicKey);
292
+ const privateKey = await openPrivateKey(unlocked.masterKey, wrapped, aad, { name: "ECDH", namedCurve: "P-256" }, ["deriveBits"], false);
293
+ (unlocked.retainedEncryptionKeys ??= []).push({ fingerprint: publicKey.fingerprint, certDer, privateKey });
294
+ } catch {
295
+ unopenableKeys.push(publicKey.fingerprint);
296
+ }
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Unlocks a mailbox's key vault with its password unlock method: fetches the vault, derives the
302
+ * wrapping key from `password` using the *exact* KDF parameters that wrap was created with, unwraps
303
+ * the master key, then unwraps and imports whichever signing/encryption private keys the mailbox has
304
+ * currently-active public keys for, plus the retained older encryption keys (`UnlockedKeys.retainedEncryptionKeys`).
305
+ * Stores the result in this module's in-memory session store.
306
+ *
307
+ * Throws (never silently no-ops) when there's no password wrap enrolled, or when the password is
308
+ * wrong (AEAD authentication failure opening the password wrap) — callers should present this as
309
+ * "incorrect password," not a generic error, but this module doesn't presume a specific UI's error copy.
310
+ * Once the master key has opened, the password is known to be right: an active signing key that then
311
+ * won't open is skipped and listed in `UnlockResult.unopenableKeys` (so is a retained encryption key); an active
312
+ * encryption key that won't
313
+ * open still fails the unlock, with `UnopenableEncryptionKeyError`. Throws `KeysLockedError` when
314
+ * `destroyUnlockedKeys()` locked this mailbox (or all mailboxes) while the unlock was in flight.
315
+ */
316
+ export async function unlockWithPassword(mailboxUid: string, mailboxKeys: PublicKey[], password: string): Promise<UnlockResult> {
317
+ const generation = lockGeneration(mailboxUid);
318
+ const vault = await getKeyVault(mailboxUid);
319
+ const passwordWrap = vault.masterKeyWraps.find((w) => w.method === "password");
320
+ if (!passwordWrap) {
321
+ throw new Error("This mailbox has no password unlock method enrolled.");
322
+ }
323
+ const kdfParams = parseArgon2idKdfLabel(passwordWrap.kdf);
324
+ if (!kdfParams) {
325
+ throw new Error(`Unrecognized KDF for this mailbox's password wrap: ${passwordWrap.kdf}`);
326
+ }
327
+
328
+ const salt = fromBase64(passwordWrap.salt);
329
+ const { wrappingKey } = await deriveFromPassword(password, salt, kdfParams);
330
+ const masterKey = await openWithKey(
331
+ wrappingKey,
332
+ { ciphertext: passwordWrap.ciphertext, nonce: passwordWrap.nonce },
333
+ buildAad(mailboxUid, MASTER_KEY_AAD_PURPOSE),
334
+ );
335
+
336
+ return openSession(mailboxUid, mailboxKeys, vault, masterKey, generation);
337
+ }
338
+
339
+ /** What `unlockWithRecoveryCode()` resolves with. */
340
+ export interface RecoveryUnlockResult extends UnlockResult {
341
+ /** The `methodId` of the recovery wrap the code opened (e.g. `"recovery-3"`). Recovery codes are single-use:
342
+ * pass it to `masterKeyWraps.ts`'s `consumeRecoveryCode()` once the unlock flow is done. Absent only for a
343
+ * recovery wrap stored without a `methodId`, which `buildRecoveryWraps()` never produces. */
344
+ recoveryMethodId?: string;
345
+ /** How many *other* recovery wraps the vault holds - the codes left once this one is consumed. */
346
+ remainingRecoveryCodes: number;
347
+ }
348
+
349
+ /** The one rejection for a recovery code that opens nothing - a wrong code and a vault with no recovery wraps
350
+ * alike, so a UI can say "that recovery code didn't work" without learning (or revealing) which it was. */
351
+ const RECOVERY_CODE_REJECTED = "That recovery code didn't unlock this mailbox.";
352
+
353
+ /**
354
+ * Unlocks a mailbox's key vault with one of its recovery codes: fetches the vault, tries every `"recovery"` wrap
355
+ * (`kdf: "hkdf-sha256"`) with `deriveFromRecoveryCode(code, salt)` until one opens the master key, then opens the
356
+ * signing/encryption private keys and stores the session exactly as `unlockWithPassword()` does - the same
357
+ * `unopenableKeys` handling, `UnopenableEncryptionKeyError`, `KeysLockedError` on a lock mid-unlock, and
358
+ * `subscribeKeySession()` notification. `code` is normalized first (case, spacing, dashes, O/I/L slips).
359
+ *
360
+ * A wrong code and a vault with no recovery wraps both reject with the same plain `Error` (never saying which
361
+ * wrap was tried or failed) - the recovery-code counterpart of `unlockWithPassword()`'s wrong-password rejection.
362
+ *
363
+ * A recovery code is single-use, but this function writes nothing: the caller removes the used wrap afterwards
364
+ * with `consumeRecoveryCode(mailboxUid, result.recoveryMethodId)` (and may first offer a new password via
365
+ * `replacePasswordWrap()` - see that function for why that order is the safe one).
366
+ */
367
+ export async function unlockWithRecoveryCode(mailboxUid: string, mailboxKeys: PublicKey[], code: string): Promise<RecoveryUnlockResult> {
368
+ const generation = lockGeneration(mailboxUid);
369
+ const vault = await getKeyVault(mailboxUid);
370
+ const recoveryWraps = vault.masterKeyWraps.filter((w) => w.method === "recovery");
371
+ const aad = buildAad(mailboxUid, MASTER_KEY_AAD_PURPOSE);
372
+
373
+ let opened: { masterKey: Uint8Array; methodId?: string } | undefined;
374
+ for (const wrap of recoveryWraps) {
375
+ if (wrap.kdf !== RECOVERY_KDF_LABEL) {
376
+ continue;
377
+ }
378
+ let wrappingKey: Uint8Array | undefined;
379
+ try {
380
+ wrappingKey = await deriveFromRecoveryCode(code, fromBase64(wrap.salt));
381
+ opened = { masterKey: await openWithKey(wrappingKey, { ciphertext: wrap.ciphertext, nonce: wrap.nonce }, aad), methodId: wrap.methodId };
382
+ } catch {
383
+ // Wrong code for this wrap (AEAD failure), an empty code, or a corrupt wrap - all just "not this one".
384
+ } finally {
385
+ wrappingKey?.fill(0);
386
+ }
387
+ if (opened) {
388
+ break;
389
+ }
390
+ }
391
+ if (!opened) {
392
+ throw new Error(RECOVERY_CODE_REJECTED);
393
+ }
394
+
395
+ const result = await openSession(mailboxUid, mailboxKeys, vault, opened.masterKey, generation);
396
+ return {
397
+ ...result,
398
+ ...(opened.methodId !== undefined ? { recoveryMethodId: opened.methodId } : {}),
399
+ remainingRecoveryCodes: recoveryWraps.length - 1,
400
+ };
401
+ }
402
+
403
+ /**
404
+ * Shared tail of every unlock method once its wrap has yielded `masterKey`: opens and imports the active signing and
405
+ * encryption private keys and the retained encryption keys (`openRetainedEncryptionKeys()`), aborts with `KeysLockedError` if the mailbox was locked since `generation` was taken, then
406
+ * stores the result (tracked weakly in `issued`) and notifies. Zeroes `masterKey` on any failure.
407
+ */
408
+ async function openSession(
409
+ mailboxUid: string,
410
+ mailboxKeys: PublicKey[],
411
+ vault: KeyVault,
412
+ masterKey: Uint8Array,
413
+ generation: string,
414
+ ): Promise<UnlockResult> {
415
+ const unlocked: UnlockedKeys = { masterKey };
416
+ const unopenableKeys: string[] = [];
417
+
418
+ // The active private keys are imported *non-extractable*, same as the retained encryption keys below (round-5
419
+ // review; previously imported extractable, on the reasoning that an XSS able to call `exportKey()` on these
420
+ // handles could equally call `openWithKey()` with the in-memory master key - but that framing missed that
421
+ // `exportKey()` yields raw bytes an attacker can copy out and keep *after* the session ends (logout, master-key
422
+ // zeroing), whereas the master-key-access path only works while the session is live. Their one former consumer,
423
+ // `keyRotation.ts`'s `rewrapPrivateKeysUnderNewMasterKey()`, was removed (2026-09-15; it re-wrapped only the
424
+ // active keys, and web-client's rotation re-seals every vault wrap itself) - confirmed nothing in this package
425
+ // or in `web-client`/`electron-client` calls `exportKey()` on a session key at all, so nothing depends on this
426
+ // being extractable. The transient PKCS#8 plaintext buffers are zeroed as soon as WebCrypto has copied them
427
+ // into a `CryptoKey` either way.
428
+ try {
429
+ const signingPublicKey = findActivePublicKey(mailboxKeys, "sign");
430
+ const wrappedSigningKey = signingPublicKey && findWrappedPrivateKey(vault, signingPublicKey.fingerprint);
431
+ if (signingPublicKey && wrappedSigningKey) {
432
+ try {
433
+ const aad = buildAad(mailboxUid, SIGNING_PRIVATE_KEY_AAD_PURPOSE);
434
+ unlocked.signingPrivateKey = await openPrivateKey(masterKey, wrappedSigningKey, aad, { name: "ECDSA", namedCurve: "P-256" }, ["sign"]);
435
+ unlocked.signingCertDer = fromBase64(signingPublicKey.publicKey);
436
+ unlocked.signingFingerprint = signingPublicKey.fingerprint;
437
+ } catch {
438
+ unopenableKeys.push(signingPublicKey.fingerprint);
439
+ }
440
+ }
441
+
442
+ const encryptionPublicKey = findActivePublicKey(mailboxKeys, "encrypt");
443
+ const wrappedEncryptionKey = encryptionPublicKey && findWrappedPrivateKey(vault, encryptionPublicKey.fingerprint);
444
+ if (encryptionPublicKey && wrappedEncryptionKey) {
445
+ try {
446
+ const aad = buildAad(mailboxUid, ENCRYPTION_PRIVATE_KEY_AAD_PURPOSE);
447
+ unlocked.encryptionPrivateKey = await openPrivateKey(masterKey, wrappedEncryptionKey, aad, { name: "ECDH", namedCurve: "P-256" }, ["deriveBits"]);
448
+ } catch (err) {
449
+ throw new UnopenableEncryptionKeyError(encryptionPublicKey.fingerprint, err);
450
+ }
451
+ unlocked.encryptionCertDer = fromBase64(encryptionPublicKey.publicKey);
452
+ unlocked.encryptionFingerprint = encryptionPublicKey.fingerprint;
453
+ }
454
+
455
+ await openRetainedEncryptionKeys(mailboxUid, mailboxKeys, vault, unlocked, encryptionPublicKey?.fingerprint, unopenableKeys);
456
+ if (lockGeneration(mailboxUid) !== generation) {
457
+ // Locked (e.g. logout) while this unlock was awaiting - never restore keys after that.
458
+ throw new KeysLockedError();
459
+ }
460
+ } catch (err) {
461
+ // A failed unlock never reaches the session store, so nothing else would ever zero this.
462
+ destroyObject(unlocked);
463
+ throw err;
464
+ }
465
+
466
+ // A re-unlock replacing existing keys (e.g. `settings/encryption`'s post-rotation refresh) deliberately
467
+ // does NOT zero the previous entry's master key: an in-flight consumer that captured the old
468
+ // `UnlockedKeys` object (e.g. a local-index build pass) is still legitimately using it, and nothing
469
+ // "destroyed" the session - only `destroyUnlockedKeys()` does that, and it destroys every issued object still
470
+ // reachable. The store itself keeps the replaced object only weakly (see `issued`).
471
+ sessions.set(mailboxUid, unlocked);
472
+ const objects = issued.get(mailboxUid) ?? new Set<WeakHandle<UnlockedKeys>>();
473
+ // Drop handles whose object was already collected, so repeated re-unlocks don't grow the set.
474
+ for (const handle of objects) {
475
+ if (!handle.deref()) {
476
+ objects.delete(handle);
477
+ }
478
+ }
479
+ objects.add(weakHandle(unlocked));
480
+ issued.set(mailboxUid, objects);
481
+ notify({ mailboxUid, state: "unlocked" });
482
+ return { unopenableKeys };
483
+ }