@rapidmx/web-client 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -14
- package/apps/admin/_layout.tsx +2 -2
- package/apps/admin/audit-log/index.tsx +4 -4
- package/apps/admin/branding/index.tsx +3 -3
- package/apps/admin/data-requests/index.tsx +8 -8
- package/apps/admin/distribution-lists/[uid].tsx +3 -3
- package/apps/admin/distribution-lists/index.tsx +4 -4
- package/apps/admin/distribution-lists/new/index.tsx +5 -5
- package/apps/admin/domains/[uid].tsx +6 -6
- package/apps/admin/domains/index.tsx +4 -4
- package/apps/admin/domains/new/index.tsx +5 -5
- package/apps/admin/encryption-policy/index.tsx +1 -1
- package/apps/admin/escrow-scopes/[uid].tsx +7 -7
- package/apps/admin/escrow-scopes/index.tsx +4 -4
- package/apps/admin/escrow-scopes/new/index.tsx +5 -5
- package/apps/admin/index.tsx +6 -6
- package/apps/admin/ingest-queue/index.tsx +4 -4
- package/apps/admin/mailbox-policy/index.tsx +1 -1
- package/apps/admin/mailboxes/[uid].tsx +5 -5
- package/apps/admin/quarantine/index.tsx +5 -5
- package/apps/admin/retention-policy/index.tsx +3 -3
- package/apps/admin/signing-certificates/index.tsx +5 -5
- package/apps/admin/transport-rules/[uid].tsx +5 -5
- package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
- package/apps/admin/transport-rules/index.tsx +4 -4
- package/apps/admin/transport-rules/new/index.tsx +5 -5
- package/apps/escrow/_layout.tsx +1 -1
- package/apps/escrow/audit-log/index.tsx +4 -4
- package/apps/escrow/index.tsx +4 -4
- package/apps/escrow/matters/[uid].tsx +10 -10
- package/apps/escrow/matters/new/index.tsx +6 -6
- package/apps/shared/appearance/AppearanceForm.tsx +3 -3
- package/apps/shared/appearance/AppearanceHead.tsx +1 -1
- package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
- package/apps/shared/appearance/appearanceCache.ts +1 -1
- package/apps/shared/appearance/appearanceContext.ts +1 -1
- package/apps/shared/appearance/theme.ts +1 -1
- package/apps/shared/auth/adminAccess.ts +1 -1
- package/apps/shared/calendar/bookingPlugin.ts +1 -1
- package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
- package/apps/shared/calendar/calendarReminders.ts +1 -1
- package/apps/shared/calendar/useCalendarReminders.ts +1 -1
- package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
- package/apps/shared/components/admin/diagnostics/format.ts +1 -1
- package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
- package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
- package/apps/shared/components/admin/elevation.ts +1 -1
- package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
- package/apps/shared/components/admin/layout/AdminShell.tsx +8 -8
- package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +6 -6
- package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +10 -8
- package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +4 -4
- package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
- package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
- package/apps/shared/components/admin/settings/BrandingForm.tsx +5 -5
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +4 -4
- package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
- package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +4 -4
- package/apps/shared/components/admin/settings/PluginsManager.tsx +5 -5
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +5 -5
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +7 -7
- package/apps/shared/components/admin/setup/SetupWizard.tsx +27 -23
- package/apps/shared/components/admin/signOut.ts +1 -1
- package/apps/shared/components/admin/usePagedList.tsx +2 -2
- package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
- package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
- package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
- package/apps/shared/components/calendar/EventDetails.tsx +16 -14
- package/apps/shared/components/calendar/EventEditor.tsx +54 -39
- package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
- package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
- package/apps/shared/components/calendar/EventModal.tsx +5 -5
- package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/EventShell.tsx +1 -1
- package/apps/shared/components/calendar/FindATime.tsx +6 -4
- package/apps/shared/components/calendar/GuestInput.tsx +2 -2
- package/apps/shared/components/calendar/MonthView.tsx +2 -2
- package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
- package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +2 -2
- package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
- package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
- package/apps/shared/components/calendar/SplitDayView.tsx +1 -1
- package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
- package/apps/shared/components/calendar/TimeGridView.tsx +2 -2
- package/apps/shared/components/calendar/allDay.ts +2 -2
- package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
- package/apps/shared/components/calendar/eventForm.ts +3 -3
- package/apps/shared/components/calendar/eventFormat.ts +3 -3
- package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
- package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
- package/apps/shared/components/contacts/ContactForm.tsx +27 -17
- package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
- package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
- package/apps/shared/components/contacts/contactKeys.ts +3 -3
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +6 -6
- package/apps/shared/components/forms/StringListField.tsx +1 -1
- package/apps/shared/components/layout/AppShell.tsx +12 -10
- package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +9 -9
- package/apps/shared/components/layout/MailboxProvisioning.tsx +8 -6
- package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
- package/apps/shared/components/layout/ThemeSwitch.tsx +1 -1
- package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
- package/apps/shared/components/layout/UserMenu.tsx +6 -4
- package/apps/shared/components/mail/ConversationList.tsx +7 -5
- package/apps/shared/components/mail/ConversationThreadPane.tsx +26 -15
- package/apps/shared/components/mail/InviteCard.tsx +11 -9
- package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
- package/apps/shared/components/mail/MailAddress.tsx +1 -1
- package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
- package/apps/shared/components/mail/MailSelectionBar.tsx +2 -2
- package/apps/shared/components/mail/MenuButton.tsx +1 -1
- package/apps/shared/components/mail/MessageDetailPane.tsx +32 -30
- package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
- package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
- package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
- package/apps/shared/components/mail/compose/ComposeContext.tsx +14 -3
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +34 -30
- package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
- package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
- package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
- package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
- package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
- package/apps/shared/components/mail/compose/quotedBody.ts +5 -5
- package/apps/shared/components/mail/compose/recipients.ts +1 -1
- package/apps/shared/components/mail/invite/InviteRowChip.tsx +2 -2
- package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
- package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
- package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
- package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
- package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
- package/apps/shared/components/mail/labelMenu.tsx +9 -7
- package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
- package/apps/shared/components/mail/listPreferences.ts +2 -2
- package/apps/shared/components/mail/pinnedSigners.ts +45 -19
- package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
- package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
- package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
- package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +3 -3
- package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
- package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
- package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
- package/apps/shared/components/mail/reading/messageExport.ts +1 -1
- package/apps/shared/components/mail/reading/printMessage.ts +2 -2
- package/apps/shared/components/mail/reading/useMessageActions.ts +28 -21
- package/apps/shared/components/mail/unreadStyle.tsx +1 -1
- package/apps/shared/components/mail/verificationSeals.ts +16 -12
- package/apps/shared/components/mail/writableMailboxes.ts +14 -7
- package/apps/shared/components/rules/RuleBuilder.tsx +1 -1
- package/apps/shared/components/settings/SigningCertificateCard.tsx +4 -4
- package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
- package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
- package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
- package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
- package/apps/shared/keyboard/ShortcutsDialog.tsx +1 -1
- package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
- package/apps/shared/mail/folderCounts.ts +8 -4
- package/apps/shared/mail/folderOfType.ts +5 -3
- package/apps/shared/mail/folderTree.ts +1 -1
- package/apps/shared/mail/listSnapshots.ts +2 -2
- package/apps/shared/mail/messageReadState.ts +6 -5
- package/apps/shared/mail/newMailNotifications.ts +2 -2
- package/apps/shared/mail/outbox/composeBridge.ts +2 -2
- package/apps/shared/mail/outbox/outboxState.ts +1 -1
- package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
- package/apps/shared/mail/outbox/sendDecision.ts +2 -2
- package/apps/shared/mail/outbox/sendJob.ts +40 -35
- package/apps/shared/mail/outbox/sendOutcomes.ts +20 -15
- package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
- package/apps/shared/mail/permanentDelete.ts +8 -7
- package/apps/shared/mail/primaryMailbox.ts +1 -1
- package/apps/shared/mail/reportNotices.ts +1 -1
- package/apps/shared/mail/senderBlocking.ts +36 -20
- package/apps/shared/mail/useMailConnection.ts +12 -10
- package/apps/shared/mail/useMailLiveUpdates.ts +3 -3
- package/apps/shared/mail/useMailboxUpdateAccess.ts +35 -11
- package/apps/shared/mail/useMarkMessageRead.ts +14 -7
- package/apps/shared/mail/useNewMailNotifications.ts +1 -1
- package/apps/shared/mail/usePermanentDelete.tsx +5 -3
- package/apps/shared/navigation/pageTitle.ts +1 -1
- package/apps/shared/notifications/NotificationCenter.tsx +1 -1
- package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
- package/apps/shared/notifications/apiErrors.ts +2 -2
- package/apps/shared/notifications/pushStatus.ts +1 -1
- package/apps/shared/notifications/systemErrors.ts +1 -1
- package/apps/shared/search/LocalIndexLifecycle.tsx +18 -12
- package/apps/shared/search/crossMailboxSearch.ts +3 -3
- package/apps/shared/search/localIndexBuilder.ts +24 -12
- package/apps/shared/search/localIndexKey.ts +1 -1
- package/apps/shared/search/localIndexRpcClient.ts +150 -30
- package/apps/shared/search/localIndexTransport.ts +84 -0
- package/apps/shared/search/localIndexWorker.ts +1 -1
- package/apps/shared/search/searchTier2.ts +3 -3
- package/apps/shared/signing/enrollmentTracker.ts +3 -3
- package/apps/shared/signing/enrollmentView.ts +1 -1
- package/apps/shared/signing/signingInfo.ts +1 -1
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +6 -4
- package/apps/shared/styles/app.css +14 -23
- package/apps/www/_layout.tsx +2 -2
- package/apps/www/calendar/index.tsx +20 -18
- package/apps/www/contacts/[uid].tsx +8 -6
- package/apps/www/contacts/index.tsx +19 -17
- package/apps/www/index.tsx +57 -48
- package/apps/www/messages/[uid].tsx +11 -9
- package/apps/www/settings/auto-reply/index.tsx +6 -4
- package/apps/www/settings/blocked-senders/index.tsx +9 -7
- package/apps/www/settings/encryption/index.tsx +38 -32
- package/apps/www/settings/filters/[uid].tsx +19 -14
- package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
- package/apps/www/settings/filters/index.tsx +4 -4
- package/apps/www/settings/filters/new/index.tsx +10 -8
- package/apps/www/settings/labels/index.tsx +12 -10
- package/apps/www/settings/privacy/index.tsx +18 -14
- package/apps/www/settings/profile/index.tsx +16 -11
- package/apps/www/settings/read-receipts/index.tsx +17 -12
- package/apps/www/settings/sharing/index.tsx +6 -6
- package/apps/www/settings/signatures/[uid].tsx +5 -5
- package/apps/www/settings/signatures/index.tsx +4 -4
- package/apps/www/settings/signatures/new/index.tsx +4 -4
- package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
- package/apps/www/tasks/index.tsx +28 -23
- package/dist/apps/admin/_layout.d.ts +1 -1
- package/dist/apps/admin/_layout.js +1 -1
- package/dist/apps/admin/audit-log/index.d.ts +1 -1
- package/dist/apps/admin/audit-log/index.js +4 -4
- package/dist/apps/admin/branding/index.js +3 -3
- package/dist/apps/admin/data-requests/index.js +8 -8
- package/dist/apps/admin/distribution-lists/[uid].js +3 -3
- package/dist/apps/admin/distribution-lists/index.js +4 -4
- package/dist/apps/admin/distribution-lists/new/index.js +5 -5
- package/dist/apps/admin/domains/[uid].js +6 -6
- package/dist/apps/admin/domains/index.js +4 -4
- package/dist/apps/admin/domains/new/index.js +5 -5
- package/dist/apps/admin/encryption-policy/index.js +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
- package/dist/apps/admin/escrow-scopes/index.js +4 -4
- package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
- package/dist/apps/admin/index.js +6 -6
- package/dist/apps/admin/ingest-queue/index.js +4 -4
- package/dist/apps/admin/mailbox-policy/index.js +1 -1
- package/dist/apps/admin/mailboxes/[uid].js +5 -5
- package/dist/apps/admin/quarantine/index.js +5 -5
- package/dist/apps/admin/retention-policy/index.js +3 -3
- package/dist/apps/admin/signing-certificates/index.js +5 -5
- package/dist/apps/admin/transport-rules/[uid].js +5 -5
- package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
- package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
- package/dist/apps/admin/transport-rules/index.js +4 -4
- package/dist/apps/admin/transport-rules/new/index.js +5 -5
- package/dist/apps/escrow/_layout.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.js +4 -4
- package/dist/apps/escrow/index.js +4 -4
- package/dist/apps/escrow/matters/[uid].js +10 -10
- package/dist/apps/escrow/matters/new/index.js +6 -6
- package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
- package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
- package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
- package/dist/apps/shared/appearance/appearanceCache.js +1 -1
- package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
- package/dist/apps/shared/appearance/theme.d.ts +1 -1
- package/dist/apps/shared/auth/adminAccess.js +1 -1
- package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
- package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
- package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
- package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
- package/dist/apps/shared/components/admin/elevation.js +1 -1
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +7 -7
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
- package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
- package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +5 -5
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
- package/dist/apps/shared/components/admin/signOut.js +1 -1
- package/dist/apps/shared/components/admin/usePagedList.js +2 -2
- package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
- package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
- package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
- package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
- package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
- package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
- package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
- package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
- package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventModal.js +3 -3
- package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/EventShell.js +1 -1
- package/dist/apps/shared/components/calendar/FindATime.js +6 -4
- package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
- package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/MonthView.js +1 -1
- package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
- package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
- package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
- package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
- package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
- package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
- package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
- package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
- package/dist/apps/shared/components/calendar/allDay.js +1 -1
- package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
- package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
- package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
- package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
- package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
- package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
- package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
- package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
- package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
- package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
- package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
- package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
- package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +6 -6
- package/dist/apps/shared/components/forms/StringListField.js +1 -1
- package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
- package/dist/apps/shared/components/layout/AppShell.js +11 -9
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
- package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
- package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
- package/dist/apps/shared/components/layout/UserMenu.js +6 -4
- package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
- package/dist/apps/shared/components/mail/ConversationList.js +6 -4
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
- package/dist/apps/shared/components/mail/InviteCard.js +10 -8
- package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
- package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
- package/dist/apps/shared/components/mail/MailAddress.js +1 -1
- package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MenuButton.js +1 -1
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
- package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
- package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
- package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
- package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +34 -30
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
- package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
- package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
- package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
- package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
- package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
- package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
- package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
- package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
- package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
- package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
- package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
- package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
- package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
- package/dist/apps/shared/components/mail/labelMenu.js +9 -7
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
- package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
- package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
- package/dist/apps/shared/components/mail/listPreferences.js +1 -1
- package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
- package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
- package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
- package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
- package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
- package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
- package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
- package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
- package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
- package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
- package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
- package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
- package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
- package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
- package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
- package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
- package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
- package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
- package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
- package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
- package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
- package/dist/apps/shared/mail/folderCounts.js +6 -4
- package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
- package/dist/apps/shared/mail/folderOfType.js +4 -4
- package/dist/apps/shared/mail/folderTree.d.ts +1 -1
- package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
- package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
- package/dist/apps/shared/mail/messageReadState.js +5 -5
- package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/newMailNotifications.js +1 -1
- package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
- package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
- package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
- package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
- package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
- package/dist/apps/shared/mail/permanentDelete.js +8 -8
- package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
- package/dist/apps/shared/mail/primaryMailbox.js +1 -1
- package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
- package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
- package/dist/apps/shared/mail/senderBlocking.js +19 -16
- package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
- package/dist/apps/shared/mail/useMailConnection.js +12 -10
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
- package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
- package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
- package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
- package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
- package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
- package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
- package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
- package/dist/apps/shared/notifications/apiErrors.js +2 -2
- package/dist/apps/shared/notifications/pushStatus.js +1 -1
- package/dist/apps/shared/notifications/systemErrors.js +1 -1
- package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
- package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
- package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
- package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
- package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
- package/dist/apps/shared/search/localIndexBuilder.js +15 -12
- package/dist/apps/shared/search/localIndexKey.js +1 -1
- package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
- package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
- package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
- package/dist/apps/shared/search/localIndexTransport.js +1 -0
- package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
- package/dist/apps/shared/search/searchTier2.d.ts +3 -3
- package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
- package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
- package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.js +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
- package/dist/apps/shared/styles/app.css +14 -23
- package/dist/apps/www/_layout.d.ts +1 -1
- package/dist/apps/www/_layout.js +1 -1
- package/dist/apps/www/calendar/index.d.ts +1 -1
- package/dist/apps/www/calendar/index.js +20 -18
- package/dist/apps/www/contacts/[uid].d.ts +1 -1
- package/dist/apps/www/contacts/[uid].js +8 -6
- package/dist/apps/www/contacts/index.d.ts +1 -1
- package/dist/apps/www/contacts/index.js +19 -17
- package/dist/apps/www/index.d.ts +1 -1
- package/dist/apps/www/index.js +54 -50
- package/dist/apps/www/messages/[uid].d.ts +1 -1
- package/dist/apps/www/messages/[uid].js +11 -9
- package/dist/apps/www/settings/appearance/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.js +6 -4
- package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
- package/dist/apps/www/settings/blocked-senders/index.js +6 -4
- package/dist/apps/www/settings/encryption/index.d.ts +1 -1
- package/dist/apps/www/settings/encryption/index.js +31 -29
- package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
- package/dist/apps/www/settings/filters/[uid].js +11 -9
- package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
- package/dist/apps/www/settings/filters/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/index.js +4 -4
- package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/new/index.js +10 -8
- package/dist/apps/www/settings/labels/index.d.ts +1 -1
- package/dist/apps/www/settings/labels/index.js +12 -10
- package/dist/apps/www/settings/privacy/index.d.ts +1 -1
- package/dist/apps/www/settings/privacy/index.js +18 -14
- package/dist/apps/www/settings/profile/index.d.ts +1 -1
- package/dist/apps/www/settings/profile/index.js +7 -5
- package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
- package/dist/apps/www/settings/read-receipts/index.js +5 -3
- package/dist/apps/www/settings/sharing/index.d.ts +1 -1
- package/dist/apps/www/settings/sharing/index.js +6 -6
- package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
- package/dist/apps/www/settings/signatures/[uid].js +5 -5
- package/dist/apps/www/settings/signatures/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/index.js +4 -4
- package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/new/index.js +4 -4
- package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
- package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
- package/dist/apps/www/tasks/index.d.ts +1 -1
- package/dist/apps/www/tasks/index.js +17 -15
- package/dist/lib/admin/auditLogApi.d.ts +32 -0
- package/dist/lib/admin/auditLogApi.js +27 -0
- package/dist/lib/admin/distributionListsApi.d.ts +47 -0
- package/dist/lib/admin/distributionListsApi.js +34 -0
- package/dist/lib/admin/domainsApi.d.ts +75 -0
- package/dist/lib/admin/domainsApi.js +44 -0
- package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
- package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
- package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
- package/dist/lib/admin/escrowAuditLogApi.js +31 -0
- package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
- package/dist/lib/admin/escrowScopesApi.js +50 -0
- package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
- package/dist/lib/admin/leftoverMailboxApi.js +67 -0
- package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
- package/dist/lib/admin/mailboxPolicyApi.js +21 -0
- package/dist/lib/admin/matterExportApi.d.ts +40 -0
- package/dist/lib/admin/matterExportApi.js +39 -0
- package/dist/lib/admin/matterSearchApi.d.ts +20 -0
- package/dist/lib/admin/matterSearchApi.js +27 -0
- package/dist/lib/admin/mattersApi.d.ts +56 -0
- package/dist/lib/admin/mattersApi.js +40 -0
- package/dist/lib/admin/pluginsApi.d.ts +265 -0
- package/dist/lib/admin/pluginsApi.js +102 -0
- package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
- package/dist/lib/admin/retentionPolicyApi.js +29 -0
- package/dist/lib/admin/setupApi.d.ts +23 -0
- package/dist/lib/admin/setupApi.js +26 -0
- package/dist/lib/admin/transportRulesApi.d.ts +66 -0
- package/dist/lib/admin/transportRulesApi.js +43 -0
- package/dist/lib/appearance/preferencesApi.d.ts +128 -0
- package/dist/lib/appearance/preferencesApi.js +257 -0
- package/dist/lib/auth/profileApi.d.ts +33 -0
- package/dist/lib/auth/profileApi.js +52 -0
- package/dist/lib/auth/session.d.ts +70 -0
- package/dist/lib/auth/session.js +210 -0
- package/dist/lib/branding/brandingApi.d.ts +47 -0
- package/dist/lib/branding/brandingApi.js +61 -0
- package/dist/lib/branding/useBranding.d.ts +23 -0
- package/dist/lib/branding/useBranding.js +65 -0
- package/dist/lib/calendar/calendarApi.d.ts +230 -0
- package/dist/lib/calendar/calendarApi.js +106 -0
- package/dist/lib/calendar/calendarColors.d.ts +18 -0
- package/dist/lib/calendar/calendarColors.js +39 -0
- package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
- package/dist/lib/calendar/calendarDragIds.js +92 -0
- package/dist/lib/calendar/calendarMutations.d.ts +68 -0
- package/dist/lib/calendar/calendarMutations.js +190 -0
- package/dist/lib/calendar/eventDescription.d.ts +43 -0
- package/dist/lib/calendar/eventDescription.js +195 -0
- package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
- package/dist/lib/calendar/freeBusyApi.js +119 -0
- package/dist/lib/calendar/inviteApi.d.ts +121 -0
- package/dist/lib/calendar/inviteApi.js +54 -0
- package/dist/lib/calendar/recurrence.d.ts +60 -0
- package/dist/lib/calendar/recurrence.js +225 -0
- package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
- package/dist/lib/components/avatar/ContactAvatar.js +26 -0
- package/dist/lib/components/buttons/Button.d.ts +9 -0
- package/dist/lib/components/buttons/Button.js +14 -0
- package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
- package/dist/lib/components/buttons/CopyButton.js +14 -0
- package/dist/lib/components/feedback/Alert.d.ts +4 -0
- package/dist/lib/components/feedback/Alert.js +4 -0
- package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
- package/dist/lib/components/feedback/Skeleton.js +14 -0
- package/dist/lib/components/forms/FormField.d.ts +8 -0
- package/dist/lib/components/forms/FormField.js +5 -0
- package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
- package/dist/lib/components/navigation/BottomTabBar.js +32 -0
- package/dist/lib/components/overlays/Drawer.d.ts +20 -0
- package/dist/lib/components/overlays/Drawer.js +27 -0
- package/dist/lib/components/overlays/Modal.d.ts +17 -0
- package/dist/lib/components/overlays/Modal.js +25 -0
- package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
- package/dist/lib/components/overlays/PopoverPortal.js +53 -0
- package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
- package/dist/lib/components/overlays/overlayStack.js +143 -0
- package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
- package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
- package/dist/lib/contacts/contactsApi.d.ts +174 -0
- package/dist/lib/contacts/contactsApi.js +139 -0
- package/dist/lib/contacts/vcard.d.ts +23 -0
- package/dist/lib/contacts/vcard.js +179 -0
- package/dist/lib/crypto/composeSecurity.d.ts +67 -0
- package/dist/lib/crypto/composeSecurity.js +93 -0
- package/dist/lib/crypto/encoding.d.ts +9 -0
- package/dist/lib/crypto/encoding.js +32 -0
- package/dist/lib/crypto/escrowKeys.d.ts +25 -0
- package/dist/lib/crypto/escrowKeys.js +59 -0
- package/dist/lib/crypto/idleTimeout.d.ts +16 -0
- package/dist/lib/crypto/idleTimeout.js +47 -0
- package/dist/lib/crypto/keySession.d.ts +159 -0
- package/dist/lib/crypto/keySession.js +367 -0
- package/dist/lib/crypto/keys.d.ts +50 -0
- package/dist/lib/crypto/keys.js +89 -0
- package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
- package/dist/lib/crypto/keyvaultApi.js +350 -0
- package/dist/lib/crypto/masterKey.d.ts +59 -0
- package/dist/lib/crypto/masterKey.js +104 -0
- package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
- package/dist/lib/crypto/masterKeyWraps.js +228 -0
- package/dist/lib/crypto/messageSecurity.d.ts +248 -0
- package/dist/lib/crypto/messageSecurity.js +367 -0
- package/dist/lib/crypto/mime.d.ts +143 -0
- package/dist/lib/crypto/mime.js +504 -0
- package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
- package/dist/lib/crypto/passkeyUnlock.js +79 -0
- package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
- package/dist/lib/crypto/passwordUnlock.js +73 -0
- package/dist/lib/crypto/recoveryCode.d.ts +28 -0
- package/dist/lib/crypto/recoveryCode.js +86 -0
- package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
- package/dist/lib/crypto/signingProviderApi.js +60 -0
- package/dist/lib/crypto/smime.d.ts +128 -0
- package/dist/lib/crypto/smime.js +402 -0
- package/dist/lib/crypto/smimeMessage.d.ts +132 -0
- package/dist/lib/crypto/smimeMessage.js +341 -0
- package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
- package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
- package/dist/lib/crypto/verificationSeal.d.ts +56 -0
- package/dist/lib/crypto/verificationSeal.js +220 -0
- package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
- package/dist/lib/mail/compose/composeQuoting.js +204 -0
- package/dist/lib/mail/compose/emojiData.d.ts +9 -0
- package/dist/lib/mail/compose/emojiData.js +18 -0
- package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
- package/dist/lib/mail/compose/giphyApi.js +20 -0
- package/dist/lib/mail/conversationsApi.d.ts +80 -0
- package/dist/lib/mail/conversationsApi.js +44 -0
- package/dist/lib/mail/dataExportApi.d.ts +50 -0
- package/dist/lib/mail/dataExportApi.js +40 -0
- package/dist/lib/mail/directoryApi.d.ts +49 -0
- package/dist/lib/mail/directoryApi.js +101 -0
- package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
- package/dist/lib/mail/erasureRequestApi.js +43 -0
- package/dist/lib/mail/flaggedMessages.d.ts +22 -0
- package/dist/lib/mail/flaggedMessages.js +73 -0
- package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
- package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
- package/dist/lib/mail/labelsApi.d.ts +47 -0
- package/dist/lib/mail/labelsApi.js +38 -0
- package/dist/lib/mail/mailAddress.d.ts +27 -0
- package/dist/lib/mail/mailAddress.js +54 -0
- package/dist/lib/mail/mailApi.d.ts +850 -0
- package/dist/lib/mail/mailApi.js +602 -0
- package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
- package/dist/lib/mail/mailDetailHooks.js +53 -0
- package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
- package/dist/lib/mail/mailFilterRulesApi.js +45 -0
- package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
- package/dist/lib/mail/mailSignaturesApi.js +42 -0
- package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
- package/dist/lib/mail/mailboxAccessApi.js +56 -0
- package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
- package/dist/lib/mail/mailboxImportApi.js +51 -0
- package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
- package/dist/lib/mail/messageBodySanitizer.js +118 -0
- package/dist/lib/mail/pushClient.d.ts +106 -0
- package/dist/lib/mail/pushClient.js +317 -0
- package/dist/lib/mail/sendEvents.d.ts +33 -0
- package/dist/lib/mail/sendEvents.js +44 -0
- package/dist/lib/mail/sendFailure.d.ts +16 -0
- package/dist/lib/mail/sendFailure.js +86 -0
- package/dist/lib/mail/senderListsApi.d.ts +69 -0
- package/dist/lib/mail/senderListsApi.js +126 -0
- package/dist/lib/search/queryGrammar.d.ts +51 -0
- package/dist/lib/search/queryGrammar.js +111 -0
- package/dist/lib/search/searchApi.d.ts +100 -0
- package/dist/lib/search/searchApi.js +107 -0
- package/dist/lib/search/searchScoring.d.ts +34 -0
- package/dist/lib/search/searchScoring.js +44 -0
- package/dist/lib/search/searchTier3.d.ts +64 -0
- package/dist/lib/search/searchTier3.js +335 -0
- package/dist/lib/tasks/tasksApi.d.ts +85 -0
- package/dist/lib/tasks/tasksApi.js +55 -0
- package/dist/lib/util/api.d.ts +160 -0
- package/dist/lib/util/api.js +251 -0
- package/dist/lib/util/apiClientContext.d.ts +7 -0
- package/dist/lib/util/apiClientContext.js +32 -0
- package/dist/lib/util/apiQuery.d.ts +32 -0
- package/dist/lib/util/apiQuery.js +42 -0
- package/dist/lib/util/clipboard.d.ts +7 -0
- package/dist/lib/util/clipboard.js +56 -0
- package/dist/lib/util/dateInput.d.ts +6 -0
- package/dist/lib/util/dateInput.js +14 -0
- package/dist/lib/util/timeZone.d.ts +12 -0
- package/dist/lib/util/timeZone.js +32 -0
- package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
- package/dist/lib/util/useCopyToClipboard.js +34 -0
- package/dist/lib/util/useIsMobile.d.ts +10 -0
- package/dist/lib/util/useIsMobile.js +29 -0
- package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
- package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
- package/lib/admin/auditLogApi.ts +48 -0
- package/lib/admin/distributionListsApi.ts +77 -0
- package/lib/admin/domainsApi.ts +113 -0
- package/lib/admin/escrowAccessRequestsApi.ts +110 -0
- package/lib/admin/escrowAuditLogApi.ts +101 -0
- package/lib/admin/escrowScopesApi.ts +112 -0
- package/lib/admin/leftoverMailboxApi.ts +120 -0
- package/lib/admin/mailboxPolicyApi.ts +38 -0
- package/lib/admin/matterExportApi.ts +62 -0
- package/lib/admin/matterSearchApi.ts +35 -0
- package/lib/admin/mattersApi.ts +86 -0
- package/lib/admin/pluginsApi.ts +339 -0
- package/lib/admin/retentionPolicyApi.ts +46 -0
- package/lib/admin/setupApi.ts +40 -0
- package/lib/admin/transportRulesApi.ts +106 -0
- package/lib/appearance/preferencesApi.ts +335 -0
- package/lib/auth/profileApi.ts +77 -0
- package/lib/auth/session.ts +238 -0
- package/lib/branding/brandingApi.ts +97 -0
- package/lib/branding/useBranding.ts +81 -0
- package/lib/calendar/calendarApi.ts +309 -0
- package/lib/calendar/calendarColors.ts +45 -0
- package/lib/calendar/calendarDragIds.ts +112 -0
- package/lib/calendar/calendarMutations.ts +236 -0
- package/lib/calendar/eventDescription.ts +211 -0
- package/lib/calendar/freeBusyApi.ts +197 -0
- package/lib/calendar/inviteApi.ts +154 -0
- package/lib/calendar/recurrence.ts +285 -0
- package/lib/components/avatar/ContactAvatar.tsx +47 -0
- package/lib/components/buttons/Button.tsx +40 -0
- package/lib/components/buttons/CopyButton.tsx +45 -0
- package/lib/components/feedback/Alert.tsx +13 -0
- package/lib/components/feedback/Skeleton.tsx +38 -0
- package/lib/components/forms/FormField.tsx +23 -0
- package/lib/components/navigation/BottomTabBar.tsx +66 -0
- package/lib/components/overlays/Drawer.tsx +72 -0
- package/lib/components/overlays/Modal.tsx +64 -0
- package/lib/components/overlays/PopoverPortal.tsx +74 -0
- package/lib/components/overlays/overlayStack.ts +165 -0
- package/lib/components/pickers/MiniDatePicker.tsx +90 -0
- package/lib/contacts/contactsApi.ts +289 -0
- package/lib/contacts/vcard.ts +197 -0
- package/lib/crypto/composeSecurity.ts +140 -0
- package/lib/crypto/encoding.ts +36 -0
- package/lib/crypto/escrowKeys.ts +74 -0
- package/lib/crypto/idleTimeout.ts +49 -0
- package/lib/crypto/keySession.ts +483 -0
- package/lib/crypto/keys.ts +105 -0
- package/lib/crypto/keyvaultApi.ts +674 -0
- package/lib/crypto/masterKey.ts +134 -0
- package/lib/crypto/masterKeyWraps.ts +269 -0
- package/lib/crypto/messageSecurity.ts +600 -0
- package/lib/crypto/mime.ts +568 -0
- package/lib/crypto/passkeyUnlock.ts +103 -0
- package/lib/crypto/passwordUnlock.ts +98 -0
- package/lib/crypto/recoveryCode.ts +93 -0
- package/lib/crypto/signingProviderApi.ts +135 -0
- package/lib/crypto/smime.ts +459 -0
- package/lib/crypto/smimeMessage.ts +491 -0
- package/lib/crypto/useIdleKeyTimeout.ts +176 -0
- package/lib/crypto/verificationSeal.ts +288 -0
- package/lib/mail/compose/composeQuoting.ts +243 -0
- package/lib/mail/compose/emojiData.ts +34 -0
- package/lib/mail/compose/giphyApi.ts +31 -0
- package/lib/mail/conversationsApi.ts +123 -0
- package/lib/mail/dataExportApi.ts +73 -0
- package/lib/mail/directoryApi.ts +161 -0
- package/lib/mail/erasureRequestApi.ts +71 -0
- package/lib/mail/flaggedMessages.ts +80 -0
- package/lib/mail/focusedInboxOverridesApi.ts +64 -0
- package/lib/mail/labelsApi.ts +71 -0
- package/lib/mail/mailAddress.ts +76 -0
- package/lib/mail/mailApi.ts +1286 -0
- package/lib/mail/mailDetailHooks.ts +57 -0
- package/lib/mail/mailFilterRulesApi.ts +116 -0
- package/lib/mail/mailSignaturesApi.ts +84 -0
- package/lib/mail/mailboxAccessApi.ts +106 -0
- package/lib/mail/mailboxImportApi.ts +87 -0
- package/lib/mail/messageBodySanitizer.ts +129 -0
- package/lib/mail/pushClient.ts +375 -0
- package/lib/mail/sendEvents.ts +72 -0
- package/lib/mail/sendFailure.ts +98 -0
- package/lib/mail/senderListsApi.ts +161 -0
- package/lib/search/queryGrammar.ts +179 -0
- package/lib/search/searchApi.ts +199 -0
- package/lib/search/searchScoring.ts +46 -0
- package/lib/search/searchTier3.ts +406 -0
- package/lib/tasks/tasksApi.ts +128 -0
- package/lib/util/api.ts +318 -0
- package/lib/util/apiClientContext.ts +35 -0
- package/lib/util/apiQuery.ts +61 -0
- package/lib/util/clipboard.ts +55 -0
- package/lib/util/dateInput.ts +15 -0
- package/lib/util/timeZone.ts +33 -0
- package/lib/util/useCopyToClipboard.ts +48 -0
- package/lib/util/useIsMobile.ts +34 -0
- package/lib/videoconf/videoMeetingsApi.ts +144 -0
- 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>;
|