feat: sync Masked Email addresses when the server offers the capability#280
Open
salmonumbrella wants to merge 1 commit into
Open
feat: sync Masked Email addresses when the server offers the capability#280salmonumbrella wants to merge 1 commit into
salmonumbrella wants to merge 1 commit into
Conversation
…ability Extends the JMAP identity sync to also mirror Fastmail's Masked Email addresses into the private sendable_addresses set, but only when a JMAP session actually advertises the vendor capability (https://www.fastmail.com/dev/maskedemail) on a usable primary account. Servers without it -- Stalwart, or any standard JMAP server -- are completely unaffected: the capability is detected, never required. Only 'enabled' masks are synced (pending/disabled/deleted are not currently usable to send as); the display name falls back to the mask's forDomain when no description is set. If the server stops advertising the capability, every previously-synced masked row for that account is removed on the next sync, while identity rows are untouched -- the two reconcile together in one transaction. A masked-email provider_id can collide with an identity's provider_id without conflict, since the table's uniqueness is scoped per kind. Fixed a latent bug this surfaced: replySender.js and senderAuthorization.js both filtered sendable_addresses on kind = 'identity', which would have silently hidden every masked row from reply resolution and send authorization. Both are now kind-agnostic -- kind only disambiguates how a row was sourced, never whether it's usable. No UI surfaces, no masked-email creation, no per-mask settings -- a read-only mirror of what already exists on the server, like the identity sync itself. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
salmonumbrella
marked this pull request as ready for review
July 16, 2026 10:53
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to the JMAP identity sync: when a session advertises Fastmail's vendor extension (
https://www.fastmail.com/dev/maskedemail) on a usable primary account, the sync also mirrors the account's enabled Masked Email addresses into the private sendable set. The capability is detected, never required — a server without it (Stalwart, any standard JMAP server) takes the identical code path with zero masked rows, and the vendor capability only enters the JMAPusingarray for the one call that needs it.Result: mail arriving at a Masked Email address → reply → the From is already the masked address, transiently. Masked addresses appear nowhere else — no settings list, no picker enumeration, no alias creation, and nothing is ever written back to the server.
Changes
jmapClient.js:sessionHasMaskedEmail()capability check +MaskedEmail/getbehind it, with the same response-shape validation and same-origin token pinning asIdentity/getidentitySync.js: enabled masks reconcile askind='masked'rows in the same transaction as identities; a server that stops advertising the capability has its masked rows wiped on the next sync while identity rows are untouchedreplySender.js/senderAuthorization.js: dropped an incorrectkind = 'identity'filter so the sendable-set lookup is kind-agnostic (kind only records how a row was sourced, never whether it's usable) — with a seam-proof test driving a real send from a masked-sourced row with no other changes to the send pathTesting
--max-warnings 0) — capability-absent path (never rejected, never requested), reconcile add/update/remove, capability-disappears wipe, disabled/pending/deleted masks excluded, malformedMaskedEmail/getrejected, tamperedapiUrlnever receives the token, end-to-end masked-row sendRuntime-tested against a live Fastmail account with Masked Email addresses on a real mailbox. Stacked on #278 and #279 (their diffs are included here so the branch stands alone); I'll rebase this down to just its own changes as they land.
Contributor License Agreement
By submitting this pull request I confirm that:
third-party material and confirmed it is compatible with the CLA).