Skip to content

feat: add admin-enforced org and domain email signatures - #894

Open
halcycon wants to merge 1 commit into
bulwarkmail:mainfrom
halcycon:feature/org-enforced-signature
Open

feat: add admin-enforced org and domain email signatures#894
halcycon wants to merge 1 commit into
bulwarkmail:mainfrom
halcycon:feature/org-enforced-signature

Conversation

@halcycon

Copy link
Copy Markdown

Summary

Adds admin-enforced email signatures that can be applied instance-wide or per sender email domain, without stuffing large HTML into JMAP Identity.htmlSignature (which many servers, including Stalwart, limit to ~2047 bytes).

Templates are stored in admin policy, resolved at compose time from the selected identity’s address, and injected through Bulwark’s existing signature embed / send path.

Policy model

New fields on SettingsPolicy:

  • orgSignature
    • enabled
    • scope: instance | per_domain
    • mergeMode:
      • replace — org/domain signature only (ignore user signature)
      • append — user signature, then org/domain signature
      • fallback — org/domain signature only when the user has none
    • instance / perDomain HTML + plain-text templates
    • reply signature position + optional lock for users
    • -- separator preference + optional lock for users
  • domainBrandNames: sender email domain → brand display name (for {{brand.name}})

Template placeholders

Supported in org/domain templates:

  • {{display.name}} / {{name}} / {{display_name}}
  • {{email}}
  • {{brand.name}} / {{brand_name}} (looked up from domainBrandNames by sender email domain)

Admin UI (Admin → Signatures)

  • Enable/disable, scope, and merge mode
  • Instance-wide or per-domain template editors
  • Brand name map (domain → brand)
  • Reply placement + lock; separator + lock
  • Sample identity preview (display name + email) so admins who are not regular mail users can preview org-only and effective merged output

User-facing behaviour

  • Composer: resolves the effective signature from the selected identity + policy; respects admin-locked reply position and separator
  • Identity editor: hides personal HTML/plain signature fields when merge mode is replace
  • Composing settings: locks signature position / separator controls when the admin locks them

Implementation notes

  • Resolution lives in lib/org-signatures.ts (normalized on policy load via config-manager)
  • Independent of the signature-inline-images work; this PR does not depend on persistent CID signature assets
  • Org HTML is injected at compose time (not written into JMAP Identity signature fields)

Test plan

  • Enable org signatures with instance scope + replace; compose as a user and confirm only the org template appears
  • Switch merge mode to append; confirm user signature then org signature
  • Switch to fallback; confirm org sig only when the identity has no personal signature
  • Switch scope to per_domain; add templates for two domains and confirm the selected From address picks the matching template
  • Set domainBrandNames and confirm {{brand.name}} resolves from the sender domain
  • Use Admin → Signatures sample identity preview for org-only and merged output
  • Lock reply signature position; confirm user Composing settings control is locked and composer follows admin position
  • With replace mode, open identity editor and confirm signature fields are hidden/locked
  • Confirm existing plain/HTML user signatures still work when org signatures are disabled
  • npx vitest run lib/__tests__/org-signatures.test.ts passes

Made with Cursor

Allow admins to enforce instance-wide or per-domain signature templates with replace/append/fallback merge modes, brand placeholders keyed by sender domain, and compose-time injection without storing large HTML in JMAP Identity fields.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant