@rapidmx/web-client 0.21.0 → 0.23.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 (897) hide show
  1. package/README.md +48 -14
  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 +3 -3
  5. package/apps/admin/data-requests/index.tsx +8 -8
  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 +6 -6
  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 +7 -7
  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 +6 -6
  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 +5 -5
  20. package/apps/admin/quarantine/index.tsx +5 -5
  21. package/apps/admin/retention-policy/index.tsx +3 -3
  22. package/apps/admin/signing-certificates/index.tsx +5 -5
  23. package/apps/admin/transport-rules/[uid].tsx +5 -5
  24. package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
  25. package/apps/admin/transport-rules/index.tsx +4 -4
  26. package/apps/admin/transport-rules/new/index.tsx +5 -5
  27. package/apps/escrow/_layout.tsx +1 -1
  28. package/apps/escrow/audit-log/index.tsx +4 -4
  29. package/apps/escrow/index.tsx +4 -4
  30. package/apps/escrow/matters/[uid].tsx +10 -10
  31. package/apps/escrow/matters/new/index.tsx +6 -6
  32. package/apps/shared/appearance/AppearanceForm.tsx +3 -3
  33. package/apps/shared/appearance/AppearanceHead.tsx +1 -1
  34. package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
  35. package/apps/shared/appearance/appearanceCache.ts +1 -1
  36. package/apps/shared/appearance/appearanceContext.ts +1 -1
  37. package/apps/shared/appearance/theme.ts +1 -1
  38. package/apps/shared/auth/adminAccess.ts +1 -1
  39. package/apps/shared/calendar/bookingPlugin.ts +1 -1
  40. package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
  41. package/apps/shared/calendar/calendarReminders.ts +1 -1
  42. package/apps/shared/calendar/useCalendarReminders.ts +1 -1
  43. package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
  44. package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
  45. package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
  46. package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
  47. package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
  48. package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
  49. package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
  50. package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
  51. package/apps/shared/components/admin/diagnostics/format.ts +1 -1
  52. package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
  53. package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
  54. package/apps/shared/components/admin/elevation.ts +1 -1
  55. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
  56. package/apps/shared/components/admin/layout/AdminShell.tsx +8 -8
  57. package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +6 -6
  58. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +10 -8
  59. package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +4 -4
  60. package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
  61. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
  62. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
  63. package/apps/shared/components/admin/settings/BrandingForm.tsx +5 -5
  64. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
  65. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +4 -4
  66. package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
  67. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
  68. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +4 -4
  69. package/apps/shared/components/admin/settings/PluginsManager.tsx +5 -5
  70. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +5 -5
  71. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +7 -7
  72. package/apps/shared/components/admin/setup/SetupWizard.tsx +28 -24
  73. package/apps/shared/components/admin/signOut.ts +1 -1
  74. package/apps/shared/components/admin/usePagedList.tsx +2 -2
  75. package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
  76. package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
  77. package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
  78. package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
  79. package/apps/shared/components/calendar/EventDetails.tsx +16 -14
  80. package/apps/shared/components/calendar/EventEditor.tsx +54 -39
  81. package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
  82. package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
  83. package/apps/shared/components/calendar/EventModal.tsx +5 -5
  84. package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
  85. package/apps/shared/components/calendar/EventShell.tsx +1 -1
  86. package/apps/shared/components/calendar/FindATime.tsx +6 -4
  87. package/apps/shared/components/calendar/GuestInput.tsx +2 -2
  88. package/apps/shared/components/calendar/MonthView.tsx +2 -2
  89. package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
  90. package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
  91. package/apps/shared/components/calendar/RecurrenceEditor.tsx +2 -2
  92. package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
  93. package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
  94. package/apps/shared/components/calendar/SplitDayView.tsx +1 -1
  95. package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
  96. package/apps/shared/components/calendar/TimeGridView.tsx +5 -3
  97. package/apps/shared/components/calendar/allDay.ts +2 -2
  98. package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
  99. package/apps/shared/components/calendar/eventForm.ts +3 -3
  100. package/apps/shared/components/calendar/eventFormat.ts +3 -3
  101. package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
  102. package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
  103. package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
  104. package/apps/shared/components/contacts/ContactForm.tsx +27 -17
  105. package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
  106. package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
  107. package/apps/shared/components/contacts/contactKeys.ts +3 -3
  108. package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
  109. package/apps/shared/components/escrow/layout/EscrowShell.tsx +6 -6
  110. package/apps/shared/components/forms/StringListField.tsx +1 -1
  111. package/apps/shared/components/layout/AppShell.tsx +12 -10
  112. package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
  113. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +9 -9
  114. package/apps/shared/components/layout/MailboxProvisioning.tsx +8 -6
  115. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
  116. package/apps/shared/components/layout/ThemeSwitch.tsx +1 -1
  117. package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
  118. package/apps/shared/components/layout/UserMenu.tsx +6 -4
  119. package/apps/shared/components/mail/ConversationList.tsx +25 -10
  120. package/apps/shared/components/mail/ConversationThreadPane.tsx +33 -17
  121. package/apps/shared/components/mail/InviteCard.tsx +11 -9
  122. package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
  123. package/apps/shared/components/mail/MailAddress.tsx +1 -1
  124. package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
  125. package/apps/shared/components/mail/MailSelectionBar.tsx +2 -2
  126. package/apps/shared/components/mail/MenuButton.tsx +1 -1
  127. package/apps/shared/components/mail/MessageDetailPane.tsx +69 -48
  128. package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
  129. package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
  130. package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
  131. package/apps/shared/components/mail/compose/ComposeContext.tsx +14 -3
  132. package/apps/shared/components/mail/compose/ComposeWindow.tsx +34 -30
  133. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
  134. package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
  135. package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
  136. package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
  137. package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
  138. package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
  139. package/apps/shared/components/mail/compose/quotedBody.ts +5 -5
  140. package/apps/shared/components/mail/compose/recipients.ts +1 -1
  141. package/apps/shared/components/mail/invite/InviteRowChip.tsx +10 -14
  142. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +11 -9
  143. package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
  144. package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
  145. package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
  146. package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
  147. package/apps/shared/components/mail/labelMenu.tsx +9 -7
  148. package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
  149. package/apps/shared/components/mail/listPreferences.ts +2 -2
  150. package/apps/shared/components/mail/pinnedSigners.ts +45 -19
  151. package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
  152. package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
  153. package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
  154. package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +3 -3
  155. package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
  156. package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
  157. package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
  158. package/apps/shared/components/mail/reading/conversationPrint.ts +76 -0
  159. package/apps/shared/components/mail/reading/messageExport.ts +1 -1
  160. package/apps/shared/components/mail/reading/printMessage.ts +46 -9
  161. package/apps/shared/components/mail/reading/useMessageActions.ts +28 -21
  162. package/apps/shared/components/mail/unreadStyle.tsx +1 -1
  163. package/apps/shared/components/mail/verificationSeals.ts +16 -12
  164. package/apps/shared/components/mail/writableMailboxes.ts +14 -7
  165. package/apps/shared/components/rules/RuleBuilder.tsx +1 -1
  166. package/apps/shared/components/settings/SigningCertificateCard.tsx +4 -4
  167. package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
  168. package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
  169. package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
  170. package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
  171. package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
  172. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
  173. package/apps/shared/keyboard/ShortcutsDialog.tsx +1 -1
  174. package/apps/shared/keyboard/keymap.ts +1 -0
  175. package/apps/shared/mail/decryptedMessages.ts +116 -0
  176. package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
  177. package/apps/shared/mail/folderCounts.ts +8 -4
  178. package/apps/shared/mail/folderOfType.ts +5 -3
  179. package/apps/shared/mail/folderTree.ts +1 -1
  180. package/apps/shared/mail/listSnapshots.ts +2 -2
  181. package/apps/shared/mail/messageReadState.ts +6 -5
  182. package/apps/shared/mail/newMailNotifications.ts +2 -2
  183. package/apps/shared/mail/outbox/composeBridge.ts +2 -2
  184. package/apps/shared/mail/outbox/outboxState.ts +1 -1
  185. package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
  186. package/apps/shared/mail/outbox/sendDecision.ts +2 -2
  187. package/apps/shared/mail/outbox/sendJob.ts +40 -35
  188. package/apps/shared/mail/outbox/sendOutcomes.ts +20 -15
  189. package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
  190. package/apps/shared/mail/permanentDelete.ts +8 -7
  191. package/apps/shared/mail/primaryMailbox.ts +1 -1
  192. package/apps/shared/mail/reportNotices.ts +1 -1
  193. package/apps/shared/mail/senderBlocking.ts +36 -20
  194. package/apps/shared/mail/useMailConnection.ts +12 -10
  195. package/apps/shared/mail/useMailLiveUpdates.ts +3 -3
  196. package/apps/shared/mail/useMailboxUpdateAccess.ts +35 -11
  197. package/apps/shared/mail/useMarkMessageRead.ts +14 -7
  198. package/apps/shared/mail/useNewMailNotifications.ts +1 -1
  199. package/apps/shared/mail/usePermanentDelete.tsx +5 -3
  200. package/apps/shared/navigation/pageTitle.ts +1 -1
  201. package/apps/shared/notifications/NotificationCenter.tsx +1 -1
  202. package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
  203. package/apps/shared/notifications/apiErrors.ts +2 -2
  204. package/apps/shared/notifications/pushStatus.ts +1 -1
  205. package/apps/shared/notifications/systemErrors.ts +1 -1
  206. package/apps/shared/search/LocalIndexLifecycle.tsx +18 -12
  207. package/apps/shared/search/crossMailboxSearch.ts +3 -3
  208. package/apps/shared/search/localIndexBuilder.ts +24 -12
  209. package/apps/shared/search/localIndexKey.ts +1 -1
  210. package/apps/shared/search/localIndexRpcClient.ts +150 -30
  211. package/apps/shared/search/localIndexTransport.ts +84 -0
  212. package/apps/shared/search/localIndexWorker.ts +1 -1
  213. package/apps/shared/search/searchTier2.ts +3 -3
  214. package/apps/shared/signing/enrollmentTracker.ts +3 -3
  215. package/apps/shared/signing/enrollmentView.ts +1 -1
  216. package/apps/shared/signing/signingInfo.ts +1 -1
  217. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +6 -4
  218. package/apps/shared/styles/app.css +14 -23
  219. package/apps/www/_layout.tsx +2 -2
  220. package/apps/www/calendar/index.tsx +20 -18
  221. package/apps/www/contacts/[uid].tsx +8 -6
  222. package/apps/www/contacts/index.tsx +19 -17
  223. package/apps/www/index.tsx +68 -48
  224. package/apps/www/messages/[uid].tsx +11 -9
  225. package/apps/www/settings/auto-reply/index.tsx +6 -4
  226. package/apps/www/settings/blocked-senders/index.tsx +9 -7
  227. package/apps/www/settings/encryption/index.tsx +38 -32
  228. package/apps/www/settings/filters/[uid].tsx +19 -14
  229. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
  230. package/apps/www/settings/filters/index.tsx +4 -4
  231. package/apps/www/settings/filters/new/index.tsx +10 -8
  232. package/apps/www/settings/labels/index.tsx +12 -10
  233. package/apps/www/settings/privacy/index.tsx +18 -14
  234. package/apps/www/settings/profile/index.tsx +16 -11
  235. package/apps/www/settings/read-receipts/index.tsx +17 -12
  236. package/apps/www/settings/sharing/index.tsx +6 -6
  237. package/apps/www/settings/signatures/[uid].tsx +5 -5
  238. package/apps/www/settings/signatures/index.tsx +4 -4
  239. package/apps/www/settings/signatures/new/index.tsx +4 -4
  240. package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
  241. package/apps/www/tasks/index.tsx +28 -23
  242. package/dist/apps/admin/_layout.d.ts +1 -1
  243. package/dist/apps/admin/_layout.js +1 -1
  244. package/dist/apps/admin/audit-log/index.d.ts +1 -1
  245. package/dist/apps/admin/audit-log/index.js +4 -4
  246. package/dist/apps/admin/branding/index.js +3 -3
  247. package/dist/apps/admin/data-requests/index.js +8 -8
  248. package/dist/apps/admin/distribution-lists/[uid].js +3 -3
  249. package/dist/apps/admin/distribution-lists/index.js +4 -4
  250. package/dist/apps/admin/distribution-lists/new/index.js +5 -5
  251. package/dist/apps/admin/domains/[uid].js +6 -6
  252. package/dist/apps/admin/domains/index.js +4 -4
  253. package/dist/apps/admin/domains/new/index.js +5 -5
  254. package/dist/apps/admin/encryption-policy/index.js +1 -1
  255. package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
  256. package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
  257. package/dist/apps/admin/escrow-scopes/index.js +4 -4
  258. package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
  259. package/dist/apps/admin/index.js +6 -6
  260. package/dist/apps/admin/ingest-queue/index.js +4 -4
  261. package/dist/apps/admin/mailbox-policy/index.js +1 -1
  262. package/dist/apps/admin/mailboxes/[uid].js +5 -5
  263. package/dist/apps/admin/quarantine/index.js +5 -5
  264. package/dist/apps/admin/retention-policy/index.js +3 -3
  265. package/dist/apps/admin/signing-certificates/index.js +5 -5
  266. package/dist/apps/admin/transport-rules/[uid].js +5 -5
  267. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
  268. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
  269. package/dist/apps/admin/transport-rules/index.js +4 -4
  270. package/dist/apps/admin/transport-rules/new/index.js +5 -5
  271. package/dist/apps/escrow/_layout.d.ts +1 -1
  272. package/dist/apps/escrow/audit-log/index.d.ts +1 -1
  273. package/dist/apps/escrow/audit-log/index.js +4 -4
  274. package/dist/apps/escrow/index.js +4 -4
  275. package/dist/apps/escrow/matters/[uid].js +10 -10
  276. package/dist/apps/escrow/matters/new/index.js +6 -6
  277. package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
  278. package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
  279. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
  280. package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
  281. package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
  282. package/dist/apps/shared/appearance/appearanceCache.js +1 -1
  283. package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
  284. package/dist/apps/shared/appearance/theme.d.ts +1 -1
  285. package/dist/apps/shared/auth/adminAccess.js +1 -1
  286. package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
  287. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
  288. package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
  289. package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
  290. package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
  291. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
  292. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
  293. package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
  294. package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
  295. package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
  296. package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
  297. package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
  298. package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
  299. package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
  300. package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
  301. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
  302. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
  303. package/dist/apps/shared/components/admin/elevation.js +1 -1
  304. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
  305. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  306. package/dist/apps/shared/components/admin/layout/AdminShell.js +7 -7
  307. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
  308. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
  309. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
  310. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
  311. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
  312. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
  313. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
  314. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
  315. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
  316. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
  317. package/dist/apps/shared/components/admin/settings/BrandingForm.js +5 -5
  318. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
  319. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
  320. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
  321. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
  322. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
  323. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
  324. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
  325. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
  326. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
  327. package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
  328. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
  329. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
  330. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
  331. package/dist/apps/shared/components/admin/setup/SetupWizard.d.ts +4 -4
  332. package/dist/apps/shared/components/admin/setup/SetupWizard.js +27 -23
  333. package/dist/apps/shared/components/admin/signOut.js +1 -1
  334. package/dist/apps/shared/components/admin/usePagedList.js +2 -2
  335. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
  336. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
  337. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
  338. package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
  339. package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
  340. package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
  341. package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
  342. package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
  343. package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
  344. package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
  345. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
  346. package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
  347. package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
  348. package/dist/apps/shared/components/calendar/EventModal.js +3 -3
  349. package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
  350. package/dist/apps/shared/components/calendar/EventShell.js +1 -1
  351. package/dist/apps/shared/components/calendar/FindATime.js +6 -4
  352. package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
  353. package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
  354. package/dist/apps/shared/components/calendar/MonthView.js +1 -1
  355. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
  356. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
  357. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
  358. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
  359. package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
  360. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
  361. package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
  362. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
  363. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
  364. package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
  365. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
  366. package/dist/apps/shared/components/calendar/TimeGridView.js +2 -2
  367. package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
  368. package/dist/apps/shared/components/calendar/allDay.js +1 -1
  369. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
  370. package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
  371. package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
  372. package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
  373. package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
  374. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
  375. package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
  376. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  377. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
  378. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
  379. package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
  380. package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
  381. package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
  382. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
  383. package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
  384. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
  385. package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
  386. package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
  387. package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
  388. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  389. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
  390. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +6 -6
  391. package/dist/apps/shared/components/forms/StringListField.js +1 -1
  392. package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
  393. package/dist/apps/shared/components/layout/AppShell.js +11 -9
  394. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
  395. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
  396. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
  397. package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
  398. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
  399. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
  400. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
  401. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
  402. package/dist/apps/shared/components/layout/UserMenu.js +6 -4
  403. package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
  404. package/dist/apps/shared/components/mail/ConversationList.js +23 -8
  405. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
  406. package/dist/apps/shared/components/mail/ConversationThreadPane.js +19 -13
  407. package/dist/apps/shared/components/mail/InviteCard.js +10 -8
  408. package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
  409. package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
  410. package/dist/apps/shared/components/mail/MailAddress.js +1 -1
  411. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
  412. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
  413. package/dist/apps/shared/components/mail/MenuButton.js +1 -1
  414. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
  415. package/dist/apps/shared/components/mail/MessageDetailPane.js +61 -46
  416. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
  417. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
  418. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
  419. package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
  420. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
  421. package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
  422. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +34 -30
  423. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
  424. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
  425. package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
  426. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
  427. package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
  428. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
  429. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
  430. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
  431. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
  432. package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
  433. package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
  434. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +3 -3
  435. package/dist/apps/shared/components/mail/invite/InviteRowChip.js +7 -9
  436. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +2 -2
  437. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +11 -9
  438. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
  439. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
  440. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
  441. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
  442. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
  443. package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
  444. package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
  445. package/dist/apps/shared/components/mail/labelMenu.js +9 -7
  446. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
  447. package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
  448. package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
  449. package/dist/apps/shared/components/mail/listPreferences.js +1 -1
  450. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
  451. package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
  452. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
  453. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
  454. package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
  455. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
  456. package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
  457. package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
  458. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
  459. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
  460. package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
  461. package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
  462. package/dist/apps/shared/components/mail/reading/conversationPrint.d.ts +15 -0
  463. package/dist/apps/shared/components/mail/reading/conversationPrint.js +69 -0
  464. package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
  465. package/dist/apps/shared/components/mail/reading/printMessage.d.ts +9 -3
  466. package/dist/apps/shared/components/mail/reading/printMessage.js +33 -6
  467. package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
  468. package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
  469. package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
  470. package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
  471. package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
  472. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
  473. package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
  474. package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
  475. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
  476. package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
  477. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  478. package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
  479. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
  480. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
  481. package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
  482. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
  483. package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
  484. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
  485. package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
  486. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  487. package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
  488. package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
  489. package/dist/apps/shared/keyboard/keymap.d.ts +6 -0
  490. package/dist/apps/shared/keyboard/keymap.js +1 -0
  491. package/dist/apps/shared/mail/decryptedMessages.d.ts +27 -0
  492. package/dist/apps/shared/mail/decryptedMessages.js +97 -0
  493. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
  494. package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
  495. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  496. package/dist/apps/shared/mail/folderCounts.js +6 -4
  497. package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
  498. package/dist/apps/shared/mail/folderOfType.js +4 -4
  499. package/dist/apps/shared/mail/folderTree.d.ts +1 -1
  500. package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
  501. package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
  502. package/dist/apps/shared/mail/messageReadState.js +5 -5
  503. package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
  504. package/dist/apps/shared/mail/newMailNotifications.js +1 -1
  505. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
  506. package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
  507. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
  508. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
  509. package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
  510. package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
  511. package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
  512. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
  513. package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
  514. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
  515. package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
  516. package/dist/apps/shared/mail/permanentDelete.js +8 -8
  517. package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
  518. package/dist/apps/shared/mail/primaryMailbox.js +1 -1
  519. package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
  520. package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
  521. package/dist/apps/shared/mail/senderBlocking.js +19 -16
  522. package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
  523. package/dist/apps/shared/mail/useMailConnection.js +12 -10
  524. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
  525. package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
  526. package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
  527. package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
  528. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
  529. package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
  530. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
  531. package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
  532. package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
  533. package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
  534. package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
  535. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
  536. package/dist/apps/shared/notifications/apiErrors.js +2 -2
  537. package/dist/apps/shared/notifications/pushStatus.js +1 -1
  538. package/dist/apps/shared/notifications/systemErrors.js +1 -1
  539. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
  540. package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
  541. package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
  542. package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
  543. package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
  544. package/dist/apps/shared/search/localIndexBuilder.js +15 -12
  545. package/dist/apps/shared/search/localIndexKey.js +1 -1
  546. package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
  547. package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
  548. package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
  549. package/dist/apps/shared/search/localIndexTransport.js +1 -0
  550. package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
  551. package/dist/apps/shared/search/searchTier2.d.ts +3 -3
  552. package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
  553. package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
  554. package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
  555. package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
  556. package/dist/apps/shared/signing/signingInfo.js +1 -1
  557. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
  558. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
  559. package/dist/apps/shared/styles/app.css +14 -23
  560. package/dist/apps/www/_layout.d.ts +1 -1
  561. package/dist/apps/www/_layout.js +1 -1
  562. package/dist/apps/www/calendar/index.d.ts +1 -1
  563. package/dist/apps/www/calendar/index.js +20 -18
  564. package/dist/apps/www/contacts/[uid].d.ts +1 -1
  565. package/dist/apps/www/contacts/[uid].js +8 -6
  566. package/dist/apps/www/contacts/index.d.ts +1 -1
  567. package/dist/apps/www/contacts/index.js +19 -17
  568. package/dist/apps/www/index.d.ts +1 -1
  569. package/dist/apps/www/index.js +64 -50
  570. package/dist/apps/www/messages/[uid].d.ts +1 -1
  571. package/dist/apps/www/messages/[uid].js +11 -9
  572. package/dist/apps/www/settings/appearance/index.d.ts +1 -1
  573. package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
  574. package/dist/apps/www/settings/auto-reply/index.js +6 -4
  575. package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
  576. package/dist/apps/www/settings/blocked-senders/index.js +6 -4
  577. package/dist/apps/www/settings/encryption/index.d.ts +1 -1
  578. package/dist/apps/www/settings/encryption/index.js +31 -29
  579. package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
  580. package/dist/apps/www/settings/filters/[uid].js +11 -9
  581. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
  582. package/dist/apps/www/settings/filters/index.d.ts +1 -1
  583. package/dist/apps/www/settings/filters/index.js +4 -4
  584. package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
  585. package/dist/apps/www/settings/filters/new/index.js +10 -8
  586. package/dist/apps/www/settings/labels/index.d.ts +1 -1
  587. package/dist/apps/www/settings/labels/index.js +12 -10
  588. package/dist/apps/www/settings/privacy/index.d.ts +1 -1
  589. package/dist/apps/www/settings/privacy/index.js +18 -14
  590. package/dist/apps/www/settings/profile/index.d.ts +1 -1
  591. package/dist/apps/www/settings/profile/index.js +7 -5
  592. package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
  593. package/dist/apps/www/settings/read-receipts/index.js +5 -3
  594. package/dist/apps/www/settings/sharing/index.d.ts +1 -1
  595. package/dist/apps/www/settings/sharing/index.js +6 -6
  596. package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
  597. package/dist/apps/www/settings/signatures/[uid].js +5 -5
  598. package/dist/apps/www/settings/signatures/index.d.ts +1 -1
  599. package/dist/apps/www/settings/signatures/index.js +4 -4
  600. package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
  601. package/dist/apps/www/settings/signatures/new/index.js +4 -4
  602. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
  603. package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
  604. package/dist/apps/www/tasks/index.d.ts +1 -1
  605. package/dist/apps/www/tasks/index.js +17 -15
  606. package/dist/lib/admin/auditLogApi.d.ts +32 -0
  607. package/dist/lib/admin/auditLogApi.js +27 -0
  608. package/dist/lib/admin/distributionListsApi.d.ts +47 -0
  609. package/dist/lib/admin/distributionListsApi.js +34 -0
  610. package/dist/lib/admin/domainsApi.d.ts +75 -0
  611. package/dist/lib/admin/domainsApi.js +44 -0
  612. package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
  613. package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
  614. package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
  615. package/dist/lib/admin/escrowAuditLogApi.js +31 -0
  616. package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
  617. package/dist/lib/admin/escrowScopesApi.js +50 -0
  618. package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
  619. package/dist/lib/admin/leftoverMailboxApi.js +67 -0
  620. package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
  621. package/dist/lib/admin/mailboxPolicyApi.js +21 -0
  622. package/dist/lib/admin/matterExportApi.d.ts +40 -0
  623. package/dist/lib/admin/matterExportApi.js +39 -0
  624. package/dist/lib/admin/matterSearchApi.d.ts +20 -0
  625. package/dist/lib/admin/matterSearchApi.js +27 -0
  626. package/dist/lib/admin/mattersApi.d.ts +56 -0
  627. package/dist/lib/admin/mattersApi.js +40 -0
  628. package/dist/lib/admin/pluginsApi.d.ts +265 -0
  629. package/dist/lib/admin/pluginsApi.js +102 -0
  630. package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
  631. package/dist/lib/admin/retentionPolicyApi.js +29 -0
  632. package/dist/lib/admin/setupApi.d.ts +23 -0
  633. package/dist/lib/admin/setupApi.js +26 -0
  634. package/dist/lib/admin/transportRulesApi.d.ts +66 -0
  635. package/dist/lib/admin/transportRulesApi.js +43 -0
  636. package/dist/lib/appearance/preferencesApi.d.ts +128 -0
  637. package/dist/lib/appearance/preferencesApi.js +257 -0
  638. package/dist/lib/auth/profileApi.d.ts +33 -0
  639. package/dist/lib/auth/profileApi.js +52 -0
  640. package/dist/lib/auth/session.d.ts +70 -0
  641. package/dist/lib/auth/session.js +210 -0
  642. package/dist/lib/branding/brandingApi.d.ts +47 -0
  643. package/dist/lib/branding/brandingApi.js +61 -0
  644. package/dist/lib/branding/useBranding.d.ts +23 -0
  645. package/dist/lib/branding/useBranding.js +65 -0
  646. package/dist/lib/calendar/calendarApi.d.ts +230 -0
  647. package/dist/lib/calendar/calendarApi.js +106 -0
  648. package/dist/lib/calendar/calendarColors.d.ts +18 -0
  649. package/dist/lib/calendar/calendarColors.js +39 -0
  650. package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
  651. package/dist/lib/calendar/calendarDragIds.js +92 -0
  652. package/dist/lib/calendar/calendarMutations.d.ts +68 -0
  653. package/dist/lib/calendar/calendarMutations.js +190 -0
  654. package/dist/lib/calendar/eventDescription.d.ts +43 -0
  655. package/dist/lib/calendar/eventDescription.js +195 -0
  656. package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
  657. package/dist/lib/calendar/freeBusyApi.js +119 -0
  658. package/dist/lib/calendar/inviteApi.d.ts +121 -0
  659. package/dist/lib/calendar/inviteApi.js +54 -0
  660. package/dist/lib/calendar/recurrence.d.ts +60 -0
  661. package/dist/lib/calendar/recurrence.js +225 -0
  662. package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
  663. package/dist/lib/components/avatar/ContactAvatar.js +26 -0
  664. package/dist/lib/components/buttons/Button.d.ts +9 -0
  665. package/dist/lib/components/buttons/Button.js +14 -0
  666. package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
  667. package/dist/lib/components/buttons/CopyButton.js +14 -0
  668. package/dist/lib/components/feedback/Alert.d.ts +4 -0
  669. package/dist/lib/components/feedback/Alert.js +4 -0
  670. package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
  671. package/dist/lib/components/feedback/Skeleton.js +14 -0
  672. package/dist/lib/components/forms/FormField.d.ts +8 -0
  673. package/dist/lib/components/forms/FormField.js +5 -0
  674. package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
  675. package/dist/lib/components/navigation/BottomTabBar.js +32 -0
  676. package/dist/lib/components/overlays/Drawer.d.ts +20 -0
  677. package/dist/lib/components/overlays/Drawer.js +27 -0
  678. package/dist/lib/components/overlays/Modal.d.ts +17 -0
  679. package/dist/lib/components/overlays/Modal.js +25 -0
  680. package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
  681. package/dist/lib/components/overlays/PopoverPortal.js +53 -0
  682. package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
  683. package/dist/lib/components/overlays/overlayStack.js +143 -0
  684. package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
  685. package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
  686. package/dist/lib/contacts/contactsApi.d.ts +174 -0
  687. package/dist/lib/contacts/contactsApi.js +139 -0
  688. package/dist/lib/contacts/vcard.d.ts +23 -0
  689. package/dist/lib/contacts/vcard.js +179 -0
  690. package/dist/lib/crypto/composeSecurity.d.ts +67 -0
  691. package/dist/lib/crypto/composeSecurity.js +93 -0
  692. package/dist/lib/crypto/encoding.d.ts +9 -0
  693. package/dist/lib/crypto/encoding.js +32 -0
  694. package/dist/lib/crypto/escrowKeys.d.ts +25 -0
  695. package/dist/lib/crypto/escrowKeys.js +59 -0
  696. package/dist/lib/crypto/idleTimeout.d.ts +16 -0
  697. package/dist/lib/crypto/idleTimeout.js +47 -0
  698. package/dist/lib/crypto/keySession.d.ts +159 -0
  699. package/dist/lib/crypto/keySession.js +367 -0
  700. package/dist/lib/crypto/keys.d.ts +50 -0
  701. package/dist/lib/crypto/keys.js +89 -0
  702. package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
  703. package/dist/lib/crypto/keyvaultApi.js +350 -0
  704. package/dist/lib/crypto/masterKey.d.ts +59 -0
  705. package/dist/lib/crypto/masterKey.js +104 -0
  706. package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
  707. package/dist/lib/crypto/masterKeyWraps.js +228 -0
  708. package/dist/lib/crypto/messageSecurity.d.ts +248 -0
  709. package/dist/lib/crypto/messageSecurity.js +367 -0
  710. package/dist/lib/crypto/mime.d.ts +143 -0
  711. package/dist/lib/crypto/mime.js +504 -0
  712. package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
  713. package/dist/lib/crypto/passkeyUnlock.js +79 -0
  714. package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
  715. package/dist/lib/crypto/passwordUnlock.js +73 -0
  716. package/dist/lib/crypto/recoveryCode.d.ts +28 -0
  717. package/dist/lib/crypto/recoveryCode.js +86 -0
  718. package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
  719. package/dist/lib/crypto/signingProviderApi.js +60 -0
  720. package/dist/lib/crypto/smime.d.ts +128 -0
  721. package/dist/lib/crypto/smime.js +402 -0
  722. package/dist/lib/crypto/smimeMessage.d.ts +132 -0
  723. package/dist/lib/crypto/smimeMessage.js +341 -0
  724. package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
  725. package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
  726. package/dist/lib/crypto/verificationSeal.d.ts +56 -0
  727. package/dist/lib/crypto/verificationSeal.js +220 -0
  728. package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
  729. package/dist/lib/mail/compose/composeQuoting.js +204 -0
  730. package/dist/lib/mail/compose/emojiData.d.ts +9 -0
  731. package/dist/lib/mail/compose/emojiData.js +18 -0
  732. package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
  733. package/dist/lib/mail/compose/giphyApi.js +20 -0
  734. package/dist/lib/mail/conversationsApi.d.ts +80 -0
  735. package/dist/lib/mail/conversationsApi.js +44 -0
  736. package/dist/lib/mail/dataExportApi.d.ts +50 -0
  737. package/dist/lib/mail/dataExportApi.js +40 -0
  738. package/dist/lib/mail/directoryApi.d.ts +49 -0
  739. package/dist/lib/mail/directoryApi.js +101 -0
  740. package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
  741. package/dist/lib/mail/erasureRequestApi.js +43 -0
  742. package/dist/lib/mail/flaggedMessages.d.ts +22 -0
  743. package/dist/lib/mail/flaggedMessages.js +73 -0
  744. package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
  745. package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
  746. package/dist/lib/mail/labelsApi.d.ts +47 -0
  747. package/dist/lib/mail/labelsApi.js +38 -0
  748. package/dist/lib/mail/mailAddress.d.ts +27 -0
  749. package/dist/lib/mail/mailAddress.js +54 -0
  750. package/dist/lib/mail/mailApi.d.ts +850 -0
  751. package/dist/lib/mail/mailApi.js +602 -0
  752. package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
  753. package/dist/lib/mail/mailDetailHooks.js +53 -0
  754. package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
  755. package/dist/lib/mail/mailFilterRulesApi.js +45 -0
  756. package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
  757. package/dist/lib/mail/mailSignaturesApi.js +42 -0
  758. package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
  759. package/dist/lib/mail/mailboxAccessApi.js +56 -0
  760. package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
  761. package/dist/lib/mail/mailboxImportApi.js +51 -0
  762. package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
  763. package/dist/lib/mail/messageBodySanitizer.js +118 -0
  764. package/dist/lib/mail/pushClient.d.ts +106 -0
  765. package/dist/lib/mail/pushClient.js +317 -0
  766. package/dist/lib/mail/sendEvents.d.ts +33 -0
  767. package/dist/lib/mail/sendEvents.js +44 -0
  768. package/dist/lib/mail/sendFailure.d.ts +16 -0
  769. package/dist/lib/mail/sendFailure.js +86 -0
  770. package/dist/lib/mail/senderListsApi.d.ts +69 -0
  771. package/dist/lib/mail/senderListsApi.js +126 -0
  772. package/dist/lib/search/queryGrammar.d.ts +51 -0
  773. package/dist/lib/search/queryGrammar.js +111 -0
  774. package/dist/lib/search/searchApi.d.ts +100 -0
  775. package/dist/lib/search/searchApi.js +107 -0
  776. package/dist/lib/search/searchScoring.d.ts +34 -0
  777. package/dist/lib/search/searchScoring.js +44 -0
  778. package/dist/lib/search/searchTier3.d.ts +64 -0
  779. package/dist/lib/search/searchTier3.js +335 -0
  780. package/dist/lib/tasks/tasksApi.d.ts +85 -0
  781. package/dist/lib/tasks/tasksApi.js +55 -0
  782. package/dist/lib/util/api.d.ts +160 -0
  783. package/dist/lib/util/api.js +251 -0
  784. package/dist/lib/util/apiClientContext.d.ts +7 -0
  785. package/dist/lib/util/apiClientContext.js +32 -0
  786. package/dist/lib/util/apiQuery.d.ts +32 -0
  787. package/dist/lib/util/apiQuery.js +42 -0
  788. package/dist/lib/util/clipboard.d.ts +7 -0
  789. package/dist/lib/util/clipboard.js +56 -0
  790. package/dist/lib/util/dateInput.d.ts +6 -0
  791. package/dist/lib/util/dateInput.js +14 -0
  792. package/dist/lib/util/timeZone.d.ts +12 -0
  793. package/dist/lib/util/timeZone.js +32 -0
  794. package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
  795. package/dist/lib/util/useCopyToClipboard.js +34 -0
  796. package/dist/lib/util/useIsMobile.d.ts +10 -0
  797. package/dist/lib/util/useIsMobile.js +29 -0
  798. package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
  799. package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
  800. package/lib/admin/auditLogApi.ts +48 -0
  801. package/lib/admin/distributionListsApi.ts +77 -0
  802. package/lib/admin/domainsApi.ts +113 -0
  803. package/lib/admin/escrowAccessRequestsApi.ts +110 -0
  804. package/lib/admin/escrowAuditLogApi.ts +101 -0
  805. package/lib/admin/escrowScopesApi.ts +112 -0
  806. package/lib/admin/leftoverMailboxApi.ts +120 -0
  807. package/lib/admin/mailboxPolicyApi.ts +38 -0
  808. package/lib/admin/matterExportApi.ts +62 -0
  809. package/lib/admin/matterSearchApi.ts +35 -0
  810. package/lib/admin/mattersApi.ts +86 -0
  811. package/lib/admin/pluginsApi.ts +339 -0
  812. package/lib/admin/retentionPolicyApi.ts +46 -0
  813. package/lib/admin/setupApi.ts +40 -0
  814. package/lib/admin/transportRulesApi.ts +106 -0
  815. package/lib/appearance/preferencesApi.ts +335 -0
  816. package/lib/auth/profileApi.ts +77 -0
  817. package/lib/auth/session.ts +238 -0
  818. package/lib/branding/brandingApi.ts +97 -0
  819. package/lib/branding/useBranding.ts +81 -0
  820. package/lib/calendar/calendarApi.ts +309 -0
  821. package/lib/calendar/calendarColors.ts +45 -0
  822. package/lib/calendar/calendarDragIds.ts +112 -0
  823. package/lib/calendar/calendarMutations.ts +236 -0
  824. package/lib/calendar/eventDescription.ts +211 -0
  825. package/lib/calendar/freeBusyApi.ts +197 -0
  826. package/lib/calendar/inviteApi.ts +154 -0
  827. package/lib/calendar/recurrence.ts +285 -0
  828. package/lib/components/avatar/ContactAvatar.tsx +47 -0
  829. package/lib/components/buttons/Button.tsx +40 -0
  830. package/lib/components/buttons/CopyButton.tsx +45 -0
  831. package/lib/components/feedback/Alert.tsx +13 -0
  832. package/lib/components/feedback/Skeleton.tsx +38 -0
  833. package/lib/components/forms/FormField.tsx +23 -0
  834. package/lib/components/navigation/BottomTabBar.tsx +66 -0
  835. package/lib/components/overlays/Drawer.tsx +72 -0
  836. package/lib/components/overlays/Modal.tsx +64 -0
  837. package/lib/components/overlays/PopoverPortal.tsx +74 -0
  838. package/lib/components/overlays/overlayStack.ts +165 -0
  839. package/lib/components/pickers/MiniDatePicker.tsx +90 -0
  840. package/lib/contacts/contactsApi.ts +289 -0
  841. package/lib/contacts/vcard.ts +197 -0
  842. package/lib/crypto/composeSecurity.ts +140 -0
  843. package/lib/crypto/encoding.ts +36 -0
  844. package/lib/crypto/escrowKeys.ts +74 -0
  845. package/lib/crypto/idleTimeout.ts +49 -0
  846. package/lib/crypto/keySession.ts +483 -0
  847. package/lib/crypto/keys.ts +105 -0
  848. package/lib/crypto/keyvaultApi.ts +674 -0
  849. package/lib/crypto/masterKey.ts +134 -0
  850. package/lib/crypto/masterKeyWraps.ts +269 -0
  851. package/lib/crypto/messageSecurity.ts +600 -0
  852. package/lib/crypto/mime.ts +568 -0
  853. package/lib/crypto/passkeyUnlock.ts +103 -0
  854. package/lib/crypto/passwordUnlock.ts +98 -0
  855. package/lib/crypto/recoveryCode.ts +93 -0
  856. package/lib/crypto/signingProviderApi.ts +135 -0
  857. package/lib/crypto/smime.ts +459 -0
  858. package/lib/crypto/smimeMessage.ts +491 -0
  859. package/lib/crypto/useIdleKeyTimeout.ts +176 -0
  860. package/lib/crypto/verificationSeal.ts +288 -0
  861. package/lib/mail/compose/composeQuoting.ts +243 -0
  862. package/lib/mail/compose/emojiData.ts +34 -0
  863. package/lib/mail/compose/giphyApi.ts +31 -0
  864. package/lib/mail/conversationsApi.ts +123 -0
  865. package/lib/mail/dataExportApi.ts +73 -0
  866. package/lib/mail/directoryApi.ts +161 -0
  867. package/lib/mail/erasureRequestApi.ts +71 -0
  868. package/lib/mail/flaggedMessages.ts +80 -0
  869. package/lib/mail/focusedInboxOverridesApi.ts +64 -0
  870. package/lib/mail/labelsApi.ts +71 -0
  871. package/lib/mail/mailAddress.ts +76 -0
  872. package/lib/mail/mailApi.ts +1286 -0
  873. package/lib/mail/mailDetailHooks.ts +57 -0
  874. package/lib/mail/mailFilterRulesApi.ts +116 -0
  875. package/lib/mail/mailSignaturesApi.ts +84 -0
  876. package/lib/mail/mailboxAccessApi.ts +106 -0
  877. package/lib/mail/mailboxImportApi.ts +87 -0
  878. package/lib/mail/messageBodySanitizer.ts +129 -0
  879. package/lib/mail/pushClient.ts +375 -0
  880. package/lib/mail/sendEvents.ts +72 -0
  881. package/lib/mail/sendFailure.ts +98 -0
  882. package/lib/mail/senderListsApi.ts +161 -0
  883. package/lib/search/queryGrammar.ts +179 -0
  884. package/lib/search/searchApi.ts +199 -0
  885. package/lib/search/searchScoring.ts +46 -0
  886. package/lib/search/searchTier3.ts +406 -0
  887. package/lib/tasks/tasksApi.ts +128 -0
  888. package/lib/util/api.ts +318 -0
  889. package/lib/util/apiClientContext.ts +35 -0
  890. package/lib/util/apiQuery.ts +61 -0
  891. package/lib/util/clipboard.ts +55 -0
  892. package/lib/util/dateInput.ts +15 -0
  893. package/lib/util/timeZone.ts +33 -0
  894. package/lib/util/useCopyToClipboard.ts +48 -0
  895. package/lib/util/useIsMobile.ts +34 -0
  896. package/lib/videoconf/videoMeetingsApi.ts +144 -0
  897. package/package.json +10 -4
@@ -0,0 +1,350 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * Typed wrappers over `@rapidmx/restapi`'s E2E encryption endpoints (`BaseKeyVaultRoute`,
7
+ * `BaseKeyLookupRoute`, `BaseEncryptionPolicyRoute` — mounted in `server` at `mail/mailboxes` and
8
+ * `system/encryption-policy`, see `src/{mongo,sql}/routes/{KeyVaultRoute,KeyLookupRoute,
9
+ * EncryptionPolicyRoute}.ts`). These calls carry only wrapped/ciphertext key material and public
10
+ * certificates — the server never sees an unwrapped private key or master key; see `crypto/masterKey.ts`
11
+ * and `crypto/keys.ts` for the client-side cryptography that produces the values passed here.
12
+ */
13
+ import { ApiRequestError, withClient } from "../util/api.js";
14
+ /** `client`, given by every function below that calls the network, is an explicit `ApiClient` from `createApiClient()`
15
+ * (e.g. one account of a multi-account app) to call instead of the default global `apiFetch()` - see `withClient()`'s
16
+ * own doc comment in `util/api.ts`. Omitted (the default), every function here behaves exactly as before. */
17
+ export function getKeyVault(mailboxUid, client) {
18
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault`);
19
+ }
20
+ /** The most recently issued, currently-valid (non-revoked, non-expired) published key of the given use
21
+ * type — the one that should actually be used to sign/encrypt going forward. A mailbox or contact may
22
+ * have several of the same `useType` on file after a rotation; older ones are kept for decrypting old
23
+ * mail, never removed, per the spec's own key-lifecycle rules. Every revoked key is skipped, whatever its
24
+ * `revocationReason` - a superseded key may still verify old mail (`isTrustedForVerification()`) but is never used for
25
+ * new mail. Shared by `crypto/keySession.ts` (the
26
+ * mailbox's own keys) and `crypto/composeSecurity.ts` (a recipient's discovered keys). */
27
+ export function findActivePublicKey(keys, useType) {
28
+ const now = Date.now();
29
+ return keys
30
+ .filter((k) => k.useType === useType && !k.revokedAt && k.notAfter > now)
31
+ .sort((a, b) => b.notBefore - a.notBefore)[0];
32
+ }
33
+ /**
34
+ * Whether `key` may still be trusted to *verify* existing signatures - never whether to sign or encrypt with it (that is
35
+ * `findActivePublicKey()`, which skips every revoked key). Expired keys are trusted: mail signed while a key was valid
36
+ * stays verifiable after it expires. A key revoked with `revocationReason: "superseded"` (a routine rotation) is trusted
37
+ * for the same reason. A key revoked as `"compromised"`, or revoked with no reason (legacy data, treated as
38
+ * compromised), is never trusted, because a message's claimed signing time can't show it was signed before the
39
+ * compromise.
40
+ */
41
+ export function isTrustedForVerification(key) {
42
+ return !key.revokedAt || key.revocationReason === "superseded";
43
+ }
44
+ /**
45
+ * The fingerprints (lowercased, de-duplicated) of every signing key in `keys` and `previousKeys` that is still trusted
46
+ * for verification (`isTrustedForVerification()`) - the trusted pins to pass to `messageSecurity.ts`'s
47
+ * `evaluateMessageSecurity()` for a `Contact` (its TOFU-pinned `keys` plus its `previousKeys`, which only key
48
+ * discovery, "trust this signer" and conflict resolution write) or a `Mailbox` (its own `keys`, so the user's own mail
49
+ * signed before their rotation still verifies).
50
+ *
51
+ * Key rotation continuity: a `PreviousKey` was pinned before it was replaced - by the user accepting a conflict
52
+ * (`replacement: "user"`) or by restapi's same-issuer renewal of an expired or revoked key (`"automatic"`, which moves
53
+ * the superseded key here with `revocationReason: "superseded"`) - so it is trusted for verification exactly like a
54
+ * current pin, and mail it signed before the rotation still verifies. Both replacement kinds are trusted alike, and the
55
+ * revocation rule is the same for current and previous keys: expired and superseded keys are trusted, compromised and
56
+ * reasonless revoked keys are not.
57
+ */
58
+ export function signingKeyFingerprints(keys, previousKeys) {
59
+ const fingerprints = [...(keys ?? []), ...(previousKeys ?? [])]
60
+ .filter((key) => key.useType === "sign" && isTrustedForVerification(key))
61
+ .map((key) => key.fingerprint.toLowerCase());
62
+ return [...new Set(fingerprints)];
63
+ }
64
+ /**
65
+ * Thrown by `enrollKey()` when `masterKeyWraps` were supplied (a first-time vault setup) but the mailbox's vault
66
+ * already has master-key wraps - e.g. another device or tab finished setting it up first. restapi answers `409`;
67
+ * this subclass (still an `ApiRequestError` with `status` 409) is only used once a re-read of the vault confirms it
68
+ * really has wraps, so an unrelated `409` (a lost optimistic-lock race) isn't mistaken for it. A caller should
69
+ * unlock the existing vault instead of enrolling a new master key.
70
+ */
71
+ export class VaultAlreadyInitializedError extends ApiRequestError {
72
+ constructor(message, code) {
73
+ super(message, 409, code);
74
+ this.name = "VaultAlreadyInitializedError";
75
+ }
76
+ }
77
+ /** Enrolls a new signing or encryption key. See `EnrollKeyInput`'s own doc comments for which fields
78
+ * matter for which `useType`. Rejects with `VaultAlreadyInitializedError` when `masterKeyWraps` were supplied
79
+ * but the vault is already set up (see that class). */
80
+ export async function enrollKey(mailboxUid, input, client) {
81
+ try {
82
+ return await withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/keys`, {
83
+ method: "POST",
84
+ body: JSON.stringify(input),
85
+ });
86
+ }
87
+ catch (err) {
88
+ if (err instanceof ApiRequestError && err.status === 409 && input.masterKeyWraps?.length) {
89
+ const vault = await getKeyVault(mailboxUid, client).catch(() => undefined);
90
+ if (vault && vault.masterKeyWraps.length > 0) {
91
+ throw new VaultAlreadyInitializedError(err.message, err.code);
92
+ }
93
+ }
94
+ throw err;
95
+ }
96
+ }
97
+ const SIGN_ENROLLMENT_STAGES = [
98
+ "submitted",
99
+ "awaiting-challenge",
100
+ "challenge-answered",
101
+ "validating",
102
+ "issuing",
103
+ "issued",
104
+ "failed",
105
+ ];
106
+ const ENROLLMENT_STATUSES = new Set(["pending", "issued", "failed"]);
107
+ const STEP_STATES = new Set(["done", "active", "pending", "failed"]);
108
+ const ENROLLMENT_TEXT_FIELDS = [
109
+ "certificate",
110
+ "error",
111
+ "requestedAt",
112
+ "updatedAt",
113
+ "lastCheckedAt",
114
+ "nextCheckAt",
115
+ "errorCode",
116
+ "note",
117
+ "issuedAt",
118
+ "installedAt",
119
+ "notAfter",
120
+ "serialNumber",
121
+ "issuer",
122
+ "subject",
123
+ ];
124
+ function isRecord(value) {
125
+ return typeof value === "object" && value !== null && !Array.isArray(value);
126
+ }
127
+ /**
128
+ * Reads an enrollment status from the network: keeps `status` (an unknown value counts as still `pending`, the safe reading) and
129
+ * whichever optional fields have the right type, drops the rest - an older server's answer comes through as it always did, and a
130
+ * newer one's malformed field cannot break the page. `progress` is clamped to 0-100; a step needs an id, a label and a known state.
131
+ */
132
+ export function normalizeEnrollmentResult(raw) {
133
+ const source = isRecord(raw) ? raw : {};
134
+ const result = {
135
+ status: ENROLLMENT_STATUSES.has(source.status) ? source.status : "pending",
136
+ };
137
+ for (const field of ENROLLMENT_TEXT_FIELDS) {
138
+ if (typeof source[field] === "string") {
139
+ result[field] = source[field];
140
+ }
141
+ }
142
+ if (source.provider === "manual" || source.provider === "rfc8823") {
143
+ result.provider = source.provider;
144
+ }
145
+ if (typeof source.stage === "string" && SIGN_ENROLLMENT_STAGES.includes(source.stage)) {
146
+ result.stage = source.stage;
147
+ }
148
+ if (Array.isArray(source.stages)) {
149
+ result.stages = source.stages
150
+ .filter((step) => isRecord(step) && typeof step.id === "string" && typeof step.label === "string" && STEP_STATES.has(step.state))
151
+ .map((step) => ({
152
+ id: step.id,
153
+ label: step.label,
154
+ state: step.state,
155
+ ...(typeof step.at === "string" ? { at: step.at } : {}),
156
+ }));
157
+ }
158
+ if (typeof source.progress === "number" && Number.isFinite(source.progress)) {
159
+ result.progress = Math.min(100, Math.max(0, source.progress));
160
+ }
161
+ if (typeof source.retryable === "boolean") {
162
+ result.retryable = source.retryable;
163
+ }
164
+ return result;
165
+ }
166
+ /** Starts an automated (RFC 8823 email-reply-00 ACME) public-CA signing-certificate enrollment —
167
+ * only meaningful when the deployment has `mail:pki:signing_enrollment:backend` set to `"rfc8823"`
168
+ * (a `"manual"`-backend deployment's `SigningCertificateEnrollment` throws instead). Genuinely
169
+ * asynchronous — the CA issues the certificate via a real email round-trip, likely minutes away, not
170
+ * synchronous the way `enrollKey()`'s encryption-key path is — poll `checkSignEnrollmentStatus()`
171
+ * rather than expecting an immediate result. `wrappedKey` is submitted upfront (this server never sees
172
+ * an unwrapped private key) so the eventual install needs no further client action at all. */
173
+ export function startSignEnrollment(mailboxUid, input, client) {
174
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/keys/sign-enrollment`, {
175
+ method: "POST",
176
+ body: JSON.stringify(input),
177
+ });
178
+ }
179
+ /** Reports the current status of a previously started automated enrollment — see
180
+ * `startSignEnrollment()`. Answers from what the server already knows; `checkSignEnrollmentNow()` asks the CA. */
181
+ export async function checkSignEnrollmentStatus(mailboxUid, enrollmentId, client) {
182
+ return normalizeEnrollmentResult(await withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/keys/sign-enrollment/${encodeURIComponent(enrollmentId)}`));
183
+ }
184
+ /** The mailbox's current (most recent) signing-certificate enrollment, or `null` when it has never had one - so a second device or
185
+ * a fresh page learns of one started elsewhere. Needs no stored enrollment id. */
186
+ export async function getCurrentSignEnrollment(mailboxUid, client) {
187
+ let raw;
188
+ try {
189
+ raw = await withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/keys/sign-enrollment`);
190
+ }
191
+ catch (err) {
192
+ if (err instanceof ApiRequestError && err.status === 404) {
193
+ return null;
194
+ }
195
+ throw err;
196
+ }
197
+ const enrollmentId = isRecord(raw) && typeof raw.enrollmentId === "string" ? raw.enrollmentId : undefined;
198
+ return enrollmentId ? { ...normalizeEnrollmentResult(raw), enrollmentId } : null;
199
+ }
200
+ /** How long a "check now" is refused for after a `429`, when the server doesn't say (it answers `Retry-After`, which `apiFetch()` does not surface). */
201
+ export const CHECK_NOW_DEFAULT_RETRY_SECONDS = 10;
202
+ /** How many seconds to wait before asking again after `checkSignEnrollmentNow()` was refused with `429`: the body's `retryAfter` (seconds)
203
+ * when the server put one there, else `CHECK_NOW_DEFAULT_RETRY_SECONDS`. `undefined` for any other error. */
204
+ export function checkNowRetryAfterSeconds(err) {
205
+ if (!(err instanceof ApiRequestError) || err.status !== 429) {
206
+ return undefined;
207
+ }
208
+ const retryAfter = isRecord(err.details) ? err.details.retryAfter : undefined;
209
+ return typeof retryAfter === "number" && Number.isFinite(retryAfter) && retryAfter > 0
210
+ ? Math.ceil(retryAfter)
211
+ : CHECK_NOW_DEFAULT_RETRY_SECONDS;
212
+ }
213
+ /** Forces an immediate re-check of an enrollment with the CA (instead of waiting for the server's own schedule) and returns its
214
+ * status. The server refuses with `429` when asked again within about ten seconds - see `checkNowRetryAfterSeconds()`. */
215
+ export async function checkSignEnrollmentNow(mailboxUid, enrollmentId, client) {
216
+ return normalizeEnrollmentResult(await withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/keys/sign-enrollment/${encodeURIComponent(enrollmentId)}/check`, { method: "POST" }));
217
+ }
218
+ /** Cancels a pending automated enrollment of this mailbox, so no key is installed from it afterwards, and returns its
219
+ * resulting status. Owner-only. `rekey()` is refused (409) while an enrollment holding a wrapped key is in flight, so
220
+ * this is how an owner with an enrollment stuck at the CA gets to rotate their keys. */
221
+ export function cancelSignEnrollment(mailboxUid, enrollmentId, client) {
222
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/keys/sign-enrollment/${encodeURIComponent(enrollmentId)}`, { method: "DELETE" });
223
+ }
224
+ /** Fetches `{escrowScopeId, publicKey}` for the `EscrowScope` this mailbox is currently assigned to
225
+ * (`Mailbox.escrowScopeId`, an admin-only assignment - see `EscrowInfo`'s own doc comment). 404s if the
226
+ * mailbox has no escrow scope assigned, the scope no longer exists, or the caller can't access this
227
+ * mailbox. Used by `crypto/masterKeyWraps.ts`'s `buildEscrowWrap()` to get the certificate MK is wrapped
228
+ * against. */
229
+ export function getEscrowInfo(mailboxUid, client) {
230
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/escrow-info`);
231
+ }
232
+ /** Adds a wrapped copy of the master key for a new unlock method (e.g. registering a new passkey),
233
+ * independent of key enrollment. Requires an already-initialized vault. `expectedMasterKeyGeneration`, when given, is
234
+ * sent alongside the wrap (see `ExpectedMasterKeyGeneration`): a `409` then means the master key was rotated since. */
235
+ export function addMasterKeyWrap(mailboxUid, wrap, expectedMasterKeyGeneration, client) {
236
+ const body = expectedMasterKeyGeneration === undefined ? wrap : { ...wrap, expectedMasterKeyGeneration };
237
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/wraps`, {
238
+ method: "POST",
239
+ body: JSON.stringify(body),
240
+ });
241
+ }
242
+ /** Removes a wrapped copy of the master key for one unlock method. `methodId` is required whenever more
243
+ * than one wrap could share the same `method` (e.g. multiple passkeys). This alone does NOT revoke
244
+ * access for anyone who already captured the wrapped blob — see `rekey()`. */
245
+ export function removeMasterKeyWrap(mailboxUid, method, methodId, client) {
246
+ const query = methodId ? `?methodId=${encodeURIComponent(methodId)}` : "";
247
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/wraps/${encodeURIComponent(method)}${query}`, {
248
+ method: "DELETE",
249
+ });
250
+ }
251
+ /** Full, atomic replacement of the mailbox's key-vault contents — the only real revocation mechanism
252
+ * for a captured wrap. Restricted server-side to the mailbox's actual owner. Refused (409) while a signing enrollment
253
+ * holding a wrapped key is in flight (see `cancelSignEnrollment()`), or when an escrowed mailbox's request carries no
254
+ * replacement escrow wrap. */
255
+ export function rekey(mailboxUid, input, client) {
256
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keyvault/rekey`, {
257
+ method: "PUT",
258
+ body: JSON.stringify(input),
259
+ });
260
+ }
261
+ /** Server-side Discovery: the server itself performs the `_rapidmx` DNS lookup and remote key-endpoint
262
+ * fetch (browsers can't do DNS TXT lookups, and a direct cross-origin fetch would hit CORS), persisting
263
+ * the result onto a `Contact` in the caller's own address book. MUST be called lazily at compose time,
264
+ * never on message receipt (see `specs/end-to-end_encryption.md`'s "Discovery is Server-Side"). */
265
+ export function lookupKeys(mailboxUid, addr, client) {
266
+ const query = new URLSearchParams({ addr });
267
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keys/lookup?${query.toString()}`);
268
+ }
269
+ /**
270
+ * Thrown by `trustSigner()` when restapi answers `409`: a different signing key is already pinned for that address, so
271
+ * trusting this one would silently replace it. Still an `ApiRequestError` (`status` 409). A UI should show this as a
272
+ * key conflict for the user to resolve deliberately, not retry.
273
+ */
274
+ export class SignerKeyConflictError extends ApiRequestError {
275
+ constructor(message, code) {
276
+ super(message, 409, code);
277
+ this.name = "SignerKeyConflictError";
278
+ }
279
+ }
280
+ /**
281
+ * "Trust this signer": pins `certificate` as `address`'s signing key on the caller's contact for that address
282
+ * (`POST /mail/mailboxes/:id/keys/trust`), after which `evaluateMessageSecurity()` given that contact's pins reports
283
+ * the sender's signed mail as verified. Resolves with the contact's resulting key state, the same shape as
284
+ * `lookupKeys()`. Rejects with `SignerKeyConflictError` on `409` (a different signing key is already pinned), and a
285
+ * plain `ApiRequestError` for `400` (an invalid certificate, or one that doesn't name `address`) and `403`/`404`.
286
+ */
287
+ export async function trustSigner(mailboxUid, input, client) {
288
+ try {
289
+ return await withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keys/trust`, {
290
+ method: "POST",
291
+ body: JSON.stringify({ address: input.address, certificate: input.certificate }),
292
+ });
293
+ }
294
+ catch (err) {
295
+ if (err instanceof ApiRequestError && err.status === 409) {
296
+ throw new SignerKeyConflictError(err.message, err.code);
297
+ }
298
+ throw err;
299
+ }
300
+ }
301
+ /**
302
+ * Thrown by `resolveKeyConflict()` when restapi answers `409`: the contact's pinned key is no longer
303
+ * `expectedPinnedFingerprint` (another device or an automatic renewal changed it meanwhile). Still an `ApiRequestError`
304
+ * (`status` 409). A UI should reload the key state and ask again rather than retry.
305
+ */
306
+ export class PinnedKeyChangedError extends ApiRequestError {
307
+ constructor(message, code) {
308
+ super(message, 409, code);
309
+ this.name = "PinnedKeyChangedError";
310
+ }
311
+ }
312
+ /**
313
+ * Resolves a contact's key conflict (`POST /mail/mailboxes/:id/keys/resolve`) - see `ResolveKeyConflictInput` for what
314
+ * each action does. Resolves with the contact's resulting key state. Rejects with `PinnedKeyChangedError` on `409`, and
315
+ * a plain `ApiRequestError` for `400` (an invalid body or certificate), `403` (no rights) and `404` (no such contact, no
316
+ * pinned key, or no recorded conflict when one is needed).
317
+ */
318
+ export async function resolveKeyConflict(mailboxUid, input, client) {
319
+ const body = {
320
+ address: input.address,
321
+ useType: input.useType,
322
+ action: input.action,
323
+ expectedPinnedFingerprint: input.expectedPinnedFingerprint,
324
+ ...(input.certificate !== undefined ? { certificate: input.certificate } : {}),
325
+ };
326
+ try {
327
+ return await withClient(client, `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/keys/resolve`, {
328
+ method: "POST",
329
+ body: JSON.stringify(body),
330
+ });
331
+ }
332
+ catch (err) {
333
+ if (err instanceof ApiRequestError && err.status === 409) {
334
+ throw new PinnedKeyChangedError(err.message, err.code);
335
+ }
336
+ throw err;
337
+ }
338
+ }
339
+ /** The system-wide encryption policy (readable by any authenticated user, used to decide what encryption
340
+ * controls a compose UI should offer). */
341
+ export function getEncryptionPolicy(client) {
342
+ return withClient(client, `/system/encryption-policy`);
343
+ }
344
+ /** Admin-only (`RequiresTrustedRole`) — updates the system-wide encryption policy. */
345
+ export function updateEncryptionPolicy(patch, client) {
346
+ return withClient(client, `/system/encryption-policy`, {
347
+ method: "PUT",
348
+ body: JSON.stringify(patch),
349
+ });
350
+ }
@@ -0,0 +1,59 @@
1
+ /** MK is always 32 random bytes (AES-256), per the spec. */
2
+ export declare const MASTER_KEY_LENGTH_BYTES = 32;
3
+ /** The one AEAD construction used everywhere in this scheme, per the spec's "MUST use an AEAD
4
+ * construction (AES-256-GCM or XChaCha20-Poly1305)" — this codebase uses AES-256-GCM specifically,
5
+ * since it's natively available via WebCrypto with no additional library on any target platform
6
+ * (browser, Electron renderer). */
7
+ export declare const AEAD_ALGORITHM = "AES-256-GCM";
8
+ /** Generates a fresh master key. Never persisted directly — only ever used to derive an AES-GCM
9
+ * `CryptoKey` in-process (see `sealWithKey`/`openWithKey`) or wrapped via `wrapMasterKey.ts`-style
10
+ * helpers before being sent anywhere. */
11
+ export declare function generateMasterKey(): Uint8Array;
12
+ /**
13
+ * Thrown when key material handed to this module has already been destroyed - `keySession.ts`'s
14
+ * `destroyUnlockedKeys()` zeroes a master key in place, so a caller still holding a stale `UnlockedKeys`
15
+ * object (captured before an idle lock or logout) would otherwise keep sealing/opening under an all-zero
16
+ * AES key *without any error*: AES-GCM accepts any 32 bytes, so a zeroed key "works" and silently produces
17
+ * ciphertext anyone can open. Callers should catch this and re-read `getUnlockedKeys()` (prompting for the
18
+ * unlock credential again when that returns `undefined`) rather than retrying with the same object.
19
+ */
20
+ export declare class KeysLockedError extends Error {
21
+ constructor(message?: string);
22
+ }
23
+ /**
24
+ * Throws `KeysLockedError` for key material that can't be a live key: empty, or every byte zero (what a
25
+ * destroyed master key looks like). A genuine 32-byte random key or KDF output is all-zero with
26
+ * probability 2^-256, so this never rejects real key material.
27
+ */
28
+ export declare function assertKeyMaterialUsable(rawKey: Uint8Array): void;
29
+ /** The wire shape stored/sent for any AEAD-sealed value — matches `WrappedPrivateKey`/`MasterKeyWrap`'s
30
+ * own `ciphertext`/`nonce` fields in `keyvaultApi.ts`. */
31
+ export interface Sealed {
32
+ ciphertext: string;
33
+ nonce: string;
34
+ }
35
+ /**
36
+ * Binds a wrap to the account and purpose it was created for, per the spec: "The user ID and the wrap
37
+ * purpose MUST be bound as additional authenticated data, preventing a blob from being replayed against
38
+ * another account." Passed as AES-GCM's `additionalData` — authenticated but never encrypted, so
39
+ * `openWithKey()` fails closed (throws) if either value doesn't match what `sealWithKey()` was given.
40
+ */
41
+ export declare function buildAad(mailboxUid: string, purpose: string): Uint8Array;
42
+ /** AEAD-encrypts `plaintext` under `rawKey` (32 bytes — MK when sealing a private key, or a
43
+ * KDF-derived wrapping key when sealing MK itself). A fresh random nonce is generated per call — GCM
44
+ * requires a unique nonce per key, never reused. Throws `KeysLockedError` for a destroyed (all-zero) key,
45
+ * and also for an all-zero `plaintext` of master-key length - that is a destroyed master key being
46
+ * re-wrapped (e.g. by `masterKeyWraps.ts`), which would otherwise persist a wrap of a useless key. */
47
+ export declare function sealWithKey(rawKey: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): Promise<Sealed>;
48
+ /** Inverse of `sealWithKey()`. Throws (WebCrypto's own `OperationError`) if `rawKey`/`aad` don't match
49
+ * what the value was sealed under, or if the ciphertext was tampered with — GCM's authentication tag
50
+ * covers both the ciphertext and the AAD. Throws `KeysLockedError` for a destroyed (all-zero) key. */
51
+ export declare function openWithKey(rawKey: Uint8Array, sealed: Sealed, aad: Uint8Array): Promise<Uint8Array>;
52
+ /**
53
+ * HKDF-SHA256, used throughout this scheme to turn raw input keying material (an Argon2id output, a
54
+ * WebAuthn PRF secret, a recovery code) into an independent key for a specific purpose — never the raw
55
+ * material directly. `info` binds the derived key to what it's for (e.g. `"wrap"` vs. an auth proof),
56
+ * so the same input material never accidentally produces the same output for two different purposes.
57
+ * Throws `KeysLockedError` for empty or all-zero `ikm` (destroyed/zeroed input material).
58
+ */
59
+ export declare function hkdfDerive(ikm: Uint8Array, salt: Uint8Array, info: string, lengthBytes?: number): Promise<Uint8Array>;
@@ -0,0 +1,104 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * The master-key (MK) wrapping scheme from `specs/end-to-end_encryption.md`'s "Master Key Wrapping":
7
+ * a random 32-byte MK is generated once, private keys are AEAD-encrypted under MK directly, and MK
8
+ * itself is wrapped once per unlock method (password/passkey/recovery — see the sibling `*Unlock.ts`
9
+ * modules) using a key derived via HKDF. MK never leaves this module unwrapped except in memory, and
10
+ * this module never talks to the network — see `keyvaultApi.ts` for what gets sent to the server (only
11
+ * ciphertext, nonces, salts and KDF parameters, all opaque to it).
12
+ */
13
+ import { fromBase64, toBase64 } from "./encoding.js";
14
+ /** MK is always 32 random bytes (AES-256), per the spec. */
15
+ export const MASTER_KEY_LENGTH_BYTES = 32;
16
+ /** The one AEAD construction used everywhere in this scheme, per the spec's "MUST use an AEAD
17
+ * construction (AES-256-GCM or XChaCha20-Poly1305)" — this codebase uses AES-256-GCM specifically,
18
+ * since it's natively available via WebCrypto with no additional library on any target platform
19
+ * (browser, Electron renderer). */
20
+ export const AEAD_ALGORITHM = "AES-256-GCM";
21
+ const GCM_NONCE_LENGTH_BYTES = 12;
22
+ /** Generates a fresh master key. Never persisted directly — only ever used to derive an AES-GCM
23
+ * `CryptoKey` in-process (see `sealWithKey`/`openWithKey`) or wrapped via `wrapMasterKey.ts`-style
24
+ * helpers before being sent anywhere. */
25
+ export function generateMasterKey() {
26
+ return crypto.getRandomValues(new Uint8Array(MASTER_KEY_LENGTH_BYTES));
27
+ }
28
+ /**
29
+ * Thrown when key material handed to this module has already been destroyed - `keySession.ts`'s
30
+ * `destroyUnlockedKeys()` zeroes a master key in place, so a caller still holding a stale `UnlockedKeys`
31
+ * object (captured before an idle lock or logout) would otherwise keep sealing/opening under an all-zero
32
+ * AES key *without any error*: AES-GCM accepts any 32 bytes, so a zeroed key "works" and silently produces
33
+ * ciphertext anyone can open. Callers should catch this and re-read `getUnlockedKeys()` (prompting for the
34
+ * unlock credential again when that returns `undefined`) rather than retrying with the same object.
35
+ */
36
+ export class KeysLockedError extends Error {
37
+ constructor(message = "The encryption keys for this mailbox are locked. Unlock them again to continue.") {
38
+ super(message);
39
+ this.name = "KeysLockedError";
40
+ }
41
+ }
42
+ /**
43
+ * Throws `KeysLockedError` for key material that can't be a live key: empty, or every byte zero (what a
44
+ * destroyed master key looks like). A genuine 32-byte random key or KDF output is all-zero with
45
+ * probability 2^-256, so this never rejects real key material.
46
+ */
47
+ export function assertKeyMaterialUsable(rawKey) {
48
+ if (rawKey.length === 0 || rawKey.every((byte) => byte === 0)) {
49
+ throw new KeysLockedError();
50
+ }
51
+ }
52
+ async function importAeadKey(rawKey) {
53
+ assertKeyMaterialUsable(rawKey);
54
+ return crypto.subtle.importKey("raw", rawKey, "AES-GCM", false, ["encrypt", "decrypt"]);
55
+ }
56
+ /**
57
+ * Binds a wrap to the account and purpose it was created for, per the spec: "The user ID and the wrap
58
+ * purpose MUST be bound as additional authenticated data, preventing a blob from being replayed against
59
+ * another account." Passed as AES-GCM's `additionalData` — authenticated but never encrypted, so
60
+ * `openWithKey()` fails closed (throws) if either value doesn't match what `sealWithKey()` was given.
61
+ */
62
+ export function buildAad(mailboxUid, purpose) {
63
+ return new TextEncoder().encode(`${mailboxUid}:${purpose}`);
64
+ }
65
+ /** AEAD-encrypts `plaintext` under `rawKey` (32 bytes — MK when sealing a private key, or a
66
+ * KDF-derived wrapping key when sealing MK itself). A fresh random nonce is generated per call — GCM
67
+ * requires a unique nonce per key, never reused. Throws `KeysLockedError` for a destroyed (all-zero) key,
68
+ * and also for an all-zero `plaintext` of master-key length - that is a destroyed master key being
69
+ * re-wrapped (e.g. by `masterKeyWraps.ts`), which would otherwise persist a wrap of a useless key. */
70
+ export async function sealWithKey(rawKey, plaintext, aad) {
71
+ const key = await importAeadKey(rawKey);
72
+ if (plaintext.length === MASTER_KEY_LENGTH_BYTES) {
73
+ assertKeyMaterialUsable(plaintext);
74
+ }
75
+ const nonce = crypto.getRandomValues(new Uint8Array(GCM_NONCE_LENGTH_BYTES));
76
+ const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce, additionalData: aad }, key, plaintext);
77
+ return { ciphertext: toBase64(new Uint8Array(ciphertext)), nonce: toBase64(nonce) };
78
+ }
79
+ /** Inverse of `sealWithKey()`. Throws (WebCrypto's own `OperationError`) if `rawKey`/`aad` don't match
80
+ * what the value was sealed under, or if the ciphertext was tampered with — GCM's authentication tag
81
+ * covers both the ciphertext and the AAD. Throws `KeysLockedError` for a destroyed (all-zero) key. */
82
+ export async function openWithKey(rawKey, sealed, aad) {
83
+ const key = await importAeadKey(rawKey);
84
+ const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: fromBase64(sealed.nonce), additionalData: aad }, key, fromBase64(sealed.ciphertext));
85
+ return new Uint8Array(plaintext);
86
+ }
87
+ /**
88
+ * HKDF-SHA256, used throughout this scheme to turn raw input keying material (an Argon2id output, a
89
+ * WebAuthn PRF secret, a recovery code) into an independent key for a specific purpose — never the raw
90
+ * material directly. `info` binds the derived key to what it's for (e.g. `"wrap"` vs. an auth proof),
91
+ * so the same input material never accidentally produces the same output for two different purposes.
92
+ * Throws `KeysLockedError` for empty or all-zero `ikm` (destroyed/zeroed input material).
93
+ */
94
+ export async function hkdfDerive(ikm, salt, info, lengthBytes = 32) {
95
+ assertKeyMaterialUsable(ikm);
96
+ const baseKey = await crypto.subtle.importKey("raw", ikm, "HKDF", false, ["deriveBits"]);
97
+ const bits = await crypto.subtle.deriveBits({
98
+ name: "HKDF",
99
+ hash: "SHA-256",
100
+ salt: salt,
101
+ info: new TextEncoder().encode(info),
102
+ }, baseKey, lengthBytes * 8);
103
+ return new Uint8Array(bits);
104
+ }
@@ -0,0 +1,108 @@
1
+ import { type UnlockedKeys } from "./keySession.js";
2
+ import { type KeyVault, type MasterKeyWrap } from "./keyvaultApi.js";
3
+ import { Argon2idParams } from "./passwordUnlock.js";
4
+ import { RECOVERY_KDF_LABEL } from "./recoveryCode.js";
5
+ export { RECOVERY_KDF_LABEL };
6
+ export declare const WRAP_SCHEME_VERSION = 1;
7
+ export declare const RECOVERY_CODE_COUNT = 8;
8
+ /** Wraps `mk` under `password` (fresh Argon2id salt every time, even when replacing an existing
9
+ * password wrap). `params` defaults to this module's own recommended parameters - overridable only so
10
+ * tests can substitute cheaper ones; production callers should omit it and let the default (which can
11
+ * be raised over time, per `passwordUnlock.ts`'s own doc comment on why each wrap stores the exact
12
+ * parameters it was created with) apply. */
13
+ export declare function buildPasswordWrap(mailboxUid: string, mk: Uint8Array, password: string, params?: Argon2idParams): Promise<MasterKeyWrap>;
14
+ /** Generates `count` fresh recovery codes and wraps `mk` under each, returning both the wraps (to
15
+ * upload) and the plaintext codes (to show the user exactly once - callers must never persist these
16
+ * themselves). Each wrap's `methodId` is a sequence label, deliberately not derived from the code
17
+ * itself - that would let anyone who saw a wrap's `methodId` narrow down which physical code it
18
+ * corresponds to. */
19
+ export declare function buildRecoveryWraps(mailboxUid: string, mk: Uint8Array, count?: number): Promise<{
20
+ wraps: MasterKeyWrap[];
21
+ codes: string[];
22
+ }>;
23
+ /** `nonce`/`salt` labels for an escrow wrap - see `buildEscrowWrap()`'s own doc comment for why these
24
+ * are fixed placeholders rather than freshly generated values, unlike every other wrap method.
25
+ * Deliberately NOT valid base64 (`fromBase64("n/a")` decodes to 2 arbitrary bytes rather than throwing,
26
+ * since only a length of `4n+1` makes `atob()` reject its input) - any future generic
27
+ * `fromBase64(wrap.salt)`/`fromBase64(wrap.nonce)` refactor across every wrap method MUST special-case
28
+ * (or simply skip) `method === "escrow"` first, or it will silently "succeed" with garbage bytes instead
29
+ * of failing loudly. Nothing in this codebase does that generically today (`keySession.ts` only ever
30
+ * reads `.salt`/`.nonce` after already filtering to `method === "password"` or `method === "recovery"`), but it's exactly the kind
31
+ * of innocent-looking refactor that would reintroduce this as a real bug. */
32
+ export declare const ESCROW_KDF_LABEL = "cms-enveloped-data";
33
+ /**
34
+ * Wraps `mk` for the `escrow` unlock method: encrypts it as a CMS `EnvelopedData` structure to the
35
+ * escrow scope's own X.509 public-key certificate (`keyvaultApi.ts`'s `getEscrowInfo()` return value) -
36
+ * the exact same "encrypt to a recipient's certificate" operation `smime.ts`'s `encryptForRecipients()`
37
+ * already implements for message bodies, just applied to MK instead. Matches
38
+ * `specs/end-to-end_encryption.md`'s `wrap_escrow = AEAD(escrow scope public key, MK)` pseudocode.
39
+ *
40
+ * Unlike `buildPasswordWrap()`/`buildRecoveryWraps()`, there is no separately-generated salt or nonce
41
+ * here - CMS `EnvelopedData` already embeds everything a holder's own offline tooling needs to unwrap
42
+ * (the content-encryption algorithm, its IV, and the ECDH key-agreement material) inside the ciphertext
43
+ * itself. `nonce`/`salt` are still populated with a fixed, documented placeholder rather than left empty
44
+ * because restapi's own `validateMasterKeyWrap()` requires every `MasterKeyWrap` field to be a non-empty
45
+ * string regardless of method - they carry no cryptographic meaning for this method and a holder's
46
+ * unwrap tooling must never read them.
47
+ *
48
+ * This client never has (and this function never touches) the scope's *private* key - only a holder's
49
+ * own external tooling can ever unwrap the result. `escrowScopeId` is required so `resolveAllowEscrow()`
50
+ * (server-side) can confirm it matches the mailbox's actually-assigned scope before persisting.
51
+ */
52
+ export declare function buildEscrowWrap(mk: Uint8Array, escrowScopeId: string, scopePublicKeyCertDer: Uint8Array): Promise<MasterKeyWrap>;
53
+ /**
54
+ * Removes the recovery wrap a recovery code just opened (`unlockWithRecoveryCode()`'s `recoveryMethodId`) - recovery
55
+ * codes are single-use. A thin, intention-revealing wrapper over `removeMasterKeyWrap(mailboxUid, "recovery",
56
+ * methodId)`; `methodId` is required, so a missing id can never make the server pick "the" recovery wrap itself.
57
+ *
58
+ * restapi (`BaseKeyVaultRoute.removeMasterKeyWrap()`) refuses with `409` when this wrap is the vault's last non-escrow
59
+ * wrap (the owner's last own unlock method) - e.g. the last recovery code of a vault whose password wrap is gone. A
60
+ * caller replacing a forgotten password should therefore call `replacePasswordWrap()` *before* this, never after:
61
+ * while the code's wrap still exists it is the other unlock method that makes replacing the password safe. A `404`
62
+ * means the wrap is already gone (e.g. consumed from another tab) and is safe to treat as done.
63
+ */
64
+ export declare function consumeRecoveryCode(mailboxUid: string, methodId: string): Promise<KeyVault>;
65
+ /** Why `replacePasswordWrap()` failed - see that function. */
66
+ export type PasswordWrapReplaceFailure = "master_key_rotated" | "multiple_password_wraps" | "no_other_unlock_method" | "add_failed";
67
+ /**
68
+ * Thrown by `replacePasswordWrap()`. For every `reason` except `"add_failed"` nothing was written. `"add_failed"` means
69
+ * the old password wrap was removed but the new one couldn't be added: `restored` says whether the old wrap was put
70
+ * back (the vault is then as it was), and `cause` holds the add's own error (e.g. an `ApiRequestError` `409` for a
71
+ * master key rotated in the meantime). Either way the mailbox still has the other unlock method checked beforehand.
72
+ */
73
+ export declare class PasswordWrapReplaceError extends Error {
74
+ readonly reason: PasswordWrapReplaceFailure;
75
+ readonly restored?: boolean;
76
+ readonly cause?: unknown;
77
+ constructor(reason: PasswordWrapReplaceFailure, message: string, options?: {
78
+ restored?: boolean;
79
+ cause?: unknown;
80
+ });
81
+ }
82
+ /**
83
+ * Replaces the mailbox's password wrap with one for `newPassword`, wrapping the session's master key
84
+ * (`unlocked.masterKey`, e.g. after `unlockWithRecoveryCode()` for a forgotten password). Resolves with the vault after
85
+ * the new wrap was added.
86
+ *
87
+ * Why remove-then-add: `unlockWithPassword()` only ever tries the first password wrap, and password wraps carry no
88
+ * `methodId`, so restapi can't remove one of two (a `DELETE .../wraps/password` without `methodId` matching two wraps is
89
+ * a `400`). Adding first would leave an unremovable, unused second wrap. The safest order is therefore:
90
+ *
91
+ * 1. Build the new wrap (Argon2id) - fails, e.g. with `KeysLockedError` for destroyed keys, before any request.
92
+ * 2. Read the vault and refuse, writing nothing, with `PasswordWrapReplaceError` when: `expectedMasterKeyGeneration` was
93
+ * given and isn't the vault's (`"master_key_rotated"` - the remove endpoint doesn't check it, so it is checked here);
94
+ * the vault holds more than one password wrap (`"multiple_password_wraps"`); or removing the password wrap would
95
+ * leave no other own unlock wrap, i.e. no non-escrow, non-password wrap (`"no_other_unlock_method"` - restapi's
96
+ * remove endpoint refuses that with `409` too, since it counts only non-escrow wraps as the owner's own; checking
97
+ * here first gives a typed reason without a write attempt).
98
+ * 3. Remove the old password wrap (skipped when there is none: the new wrap is simply added). A failure here is
99
+ * rethrown as-is; nothing changed.
100
+ * 4. Add the new wrap, sending `expectedMasterKeyGeneration` when given (restapi answers `409` if the master key was
101
+ * rotated since). If this fails, the removed wrap is re-added (with the generation read in step 2) and
102
+ * `PasswordWrapReplaceError` `"add_failed"` is thrown with `restored`. If even that fails the mailbox has no password
103
+ * wrap, but step 2 guaranteed another own unlock method remains - the user can unlock with it and retry.
104
+ *
105
+ * Because step 2 needs another own unlock method, a caller replacing a password after a recovery-code unlock must call
106
+ * this *before* `consumeRecoveryCode()`: the not-yet-consumed code's wrap is that other method.
107
+ */
108
+ export declare function replacePasswordWrap(mailboxUid: string, unlocked: Pick<UnlockedKeys, "masterKey" | "destroyed">, newPassword: string, expectedMasterKeyGeneration?: number, params?: Argon2idParams): Promise<KeyVault>;