Masked email
An address given to a website tends to spread: it is sold, leaked in a breach, or added to marketing lists, and once it has spread it cannot be taken back. Masked addresses limit the damage. A masked address is a receiving address that delivers to an existing account without revealing the account’s real address.
A user can hand a different masked address to every website or service. When one of them starts sending unwanted mail or leaks the address, the user disables that address alone, and every other address keeps working. An application such as a password manager can create a masked address over JMAP when the user registers a new login, and record which site it was issued to.
Each masked address is a MaskedEmail object that belongs to one account, found in the WebUI under Account › Masked Addresses.
Masked addresses receive mail only. They cannot be used as a sending identity or as the SMTP envelope sender, so replies from the user carry the user’s own address.
Address format and delivery
Section titled “Address format and delivery”The server generates the address when the object is created, and stores it in the read-only email field. The address has the form <prefix>.<code>@<domain>, with three parts:
<prefix>is 16 random lowercase letters and digits, or the value given inemailPrefixat creation.<code>is generated by the server. It identifies the masked address and encodes its optional expiry time, set at creation withexpiresAt. Because the expiry is part of the address, it cannot be changed afterwards.<domain>is the domain given inemailDomain, or, without it, the domain of the account’s name.
When mail arrives for an address of this form, the SMTP server recognises it at RCPT TO, before any lookup in an external directory. It delivers the message to the owning account if all of the following hold:
- The masked address exists and
enableistrue. - Its expiry time, if any, has not passed.
- The account still has an address in the recipient’s domain.
Otherwise, the address is treated as an unknown recipient, and rejected like any address that does not exist on the server, as described in RCPT stage.
The message is delivered to the account as addressed. The masked address remains visible in the To header, so the user can see which address a message was sent to, and therefore which site gave it out.
Creating a masked address
Section titled “Creating a masked address”Users create masked addresses in the account manager of the WebUI. Integrators, such as password managers, create them over JMAP with x:MaskedEmail/set, under the urn:stalwart:jmap capability. The following fields describe the address, and are set by the client:
| Field | Purpose |
|---|---|
description | A note for the user about what the address is for. |
forDomain | The site the address was issued to, for example https://example.com. Password managers fill it in automatically. |
url | A link back to the integrator’s entry for the address, for example a custom URI that opens the password manager at that login. |
emailPrefix | Optional prefix of the generated address: 1 to 64 ASCII letters, digits or underscores, not starting with an underscore. It is stored in lower case. |
emailDomain | Optional domain of the generated address. It must be a domain in which the account has an address. |
expiresAt | Optional time after which the address stops receiving mail. |
emailPrefix, emailDomain and expiresAt are read at creation only, because they become part of the generated address. The server sets accountId, createdAt and, from the credentials used for the request, createdBy.
The following request, as a password manager might send it, creates a masked address for a newsletter subscription:
{ "methodCalls": [ [ "x:MaskedEmail/set", { "create": { "new1": { "description": "Newsletter signup", "forDomain": "https://example.com", "emailPrefix": "newsletter" } } }, "c1" ] ], "using": ["urn:ietf:params:jmap:core", "urn:stalwart:jmap"]}The response returns the generated address in email, for example newsletter.<code>@example.org. The same object is created from the command line with this command:
stalwart-cli create MaskedEmail --field description='Newsletter signup' --field forDomain=https://example.comCreation fails when the requested address cannot be built, or when the account has no room for another one:
| Error | Description | Cause |
|---|---|---|
forbidden on emailDomain | The specified domain is not valid for this account. | The domain does not exist, or the account has no address in it. |
forbidden on emailDomain | No valid domain is available for this account. | emailDomain is omitted and the account name has no domain. |
invalidProperties on emailPrefix | The prefix does not meet the rules above. | |
overQuota | You have exceeded your quota of <n> masked addresses. | The account has reached its limit. |
Limits
Section titled “Limits”The number of masked addresses an account may hold is set by maxMaskedAddresses on the Email singleton, 5 by default. A maxMaskedAddresses entry in the account’s quotas overrides the server-wide value for that account, as described in Quotas.
Disabling an address does not free its place. Disabled masked addresses count towards the limit until they are destroyed.
Disabling, deleting and expiry
Section titled “Disabling, deleting and expiry”An address can be retired in three ways, which differ in whether it can come back. Setting enable to false makes the address unknown at once, so mail sent to it is rejected, while the object, with its description and site, is kept. Setting it back to true restores delivery. Destroying the object removes the address permanently. An expired address is rejected in the same way as a disabled one.
Masked addresses do not outlive their account. When the account is deleted, its masked addresses are destroyed with it.
The following commands disable a masked address and delete another one:
stalwart-cli update MaskedEmail <id> --field enable=falsestalwart-cli delete MaskedEmail --ids <id>To review what an account holds, x:MaskedEmail/query with an accountId filter lists its masked addresses. The MaskedEmail reference lists the remaining commands.
Permissions
Section titled “Permissions”Each operation requires its own permission: sysMaskedEmailGet, sysMaskedEmailQuery, sysMaskedEmailCreate, sysMaskedEmailUpdate and sysMaskedEmailDestroy. The built-in User role holds all five, so every user can manage the masked addresses of their own account without help. The System Administrator role holds them as well, and tenant administrators hold them through the User role. Permissions describes how roles are assigned.