Branding and templates: per-domain, tenant and server logos, /logo, operator calendar email templates and RSVP page (BT-1 to BT-26)

Logos resolve domain, then tenant, then server-wide, then the built-in, with
subdomains finding their domain. GET /logo serves a data-URL image, redirects
to a URL logo without fetching it, sandboxes SVG, and answers 404 when no
custom logo applies. Emails embed the first PNG, JPEG or GIF logo. Logo and
template writes are checked; stored templates are read at send time, always
escaped, and fall back to the built-in with a build warning when they don't
parse. The RSVP page is served byte for byte with a CSP and no-referrer. The
sign-in and RSVP pages load the logo through an image element. MT-22's
session logo follows the chain to the server-wide logo.
Acceptance tests 1 to 17; test 18 written as the ignored branding_compat.
This commit is contained in:
2026-09-18 22:27:19 -07:00
parent ecbdfd533b
commit 0bc6b03dcd
29 changed files with 1772 additions and 91 deletions
+2 -2
View File
@@ -264,8 +264,8 @@ Each requirement has an ID, and tests name the IDs they check.
**Decision** (2026-09-18) on the shape: in the JMAP session, the
principal's own account's `accountCapabilities` carry `urn:inbuxa:jmap`
(contract C-1) with `logo`: a string (the URL or data URL as stored) or
`null`. Until branding is built it follows this chain, steps 1 and 2 of
BT-1.
`null`. With branding built (2026-09-18) it follows BT-1 steps 1 to 3,
skipping unusable values (BT-4); `null` means the built-in logo, step 4.
- **MT-23.** The server never fetches a logo URL itself. ihasmail draws URL
logos through its image proxy, as it does today.