ADR-0125: VayuMail Per-Account Signatures — Stored Setting, Send-Time Insertion & Self-Service Editing
Status: Accepted
Date: 2026-07-08
Author: @johalputt
Relates to: ADR-0124 (VayuMail overhaul), the VayuMail engine (internal/vayuos/mail), the composer (cmd/vayupress/vayuos_mail.go)
Context
VayuMail had no notion of a sender signature: operators re-typed a sign-off on every message. This is a Phase 2 slice of the VayuMail enterprise redesign. Three questions had to be settled: where the signature lives, when it is applied, and who may edit it.
Decision
1. Storage — a per-account column, not a separate table
A signature TEXT NOT NULL DEFAULT '' column is added to vayumail_accounts via
the store's existing idempotent ALTER TABLE ADD COLUMN migration pattern (the
same used for role, totp_*, quota_bytes). The signature is a 1:1 attribute
of an account, so a column is simpler than a side table and needs no join. It is
bounded to 4 KiB. AccountStore gains SetSignature/SignatureFor, and
Account.Signature is returned by List.
2. Applied at send time, before the quoted history
The signature is not baked into the composer textarea or into saved drafts
(which would double it on send and couple the stored draft to a mutable
setting). Instead the send handler appends it server-side, using the RFC 3676
dash-dash-space delimiter, and — reusing splitQuoted from the reader — inserts it
after the freshly-written reply and before any quoted history rather than at
the very bottom. A per-message "Append signature" toggle (default on) lets the
sender opt out; the composer previews the signature and swaps it live when the
From account changes (each From option carries its data-sig).
3. Self-service editing within an admin-only surface
Account management stays admin-only, with one narrow exception: a mailbox holder may update the signature of their own address (and nothing else) via the account-update endpoint. The handler enforces this by requiring, for a non-admin caller, that the only field present is the signature and that the target email equals the caller's own mailbox. This keeps signatures self-service without widening account management.
Consequences
- Signatures never double (send-time only) and drafts stay setting-independent.
- No new table or route; the change is a column + one endpoint field + composer UI. Existing accounts default to no signature (no behaviour change).
- HTML-body signatures and multiple signatures per account are out of scope (plain-text only, one per account) and can extend this later.