v2.24.0 — Attachment storage
Postmaster can now keep the files an email carried, not just the fact that it
carried them.
Storing attachments
Attachment metadata — filename, type, size — is recorded whenever either
storage switch is on. The bytes are a separate opt-in, independent of
POSTMASTER_STORE_CONTENT, which is the point: an invoice PDF is often worth
keeping when the body that carried a magic-login link is not.
POSTMASTER_STORE_ATTACHMENTS=true
POSTMASTER_ATTACHMENTS_DISK=s3
With it on, Resend and Release replay a message with its attachments intact,
and the dashboard offers each one as a download.
Bytes are content-addressed by sha256, so the logo on every send costs one
file however many messages carry it, and a file is only removed once every
message referencing it has let it go.
Three limits keep the disk bounded — a per-file ceiling, a retention window,
and a total budget that evicts oldest-first. The metadata row always survives,
so the dashboard still reports what an email carried even after the bytes are
gone.
Downloads, and S3
Downloads always go through Postmaster's own gated endpoint, which authorizes
the request before handing anything out. From there a cloud disk gets a
redirect to a short-lived signed URL, so the bytes travel from the bucket
rather than through a PHP worker; a local disk streams through the app, where
a redirect would save nothing.
Postmaster sets no ACL on what it writes, so a bucket using S3's Bucket owner
enforced ownership setting — the default since 2023 — works untouched. The
readme documents the storage prefix, for scoping a lifecycle rule or a bucket
policy.
In the dashboard
- A paperclip in the message list marks mail that went out with something
attached. - The message detail page lists the files between the envelope details and the
body, where a mail client puts them. - Embedded images aren't listed — they're resolved back into the preview
instead, and the preview says so when one is no longer stored. - The message page now reads as one message rather than a stack of cards, the
body sizes itself to its content, and the overview chart names each bar's
count on hover.
Upgrading
Run php artisan migrate. Two migrations land: the new email_attachments
table, and a rename of the attachments column on email_messages to
legacy_attachment_names. The rename is data-preserving — that column held
filenames only, and the dashboard still reads them — and it frees the name for
the new relation.
Nothing else changes. With POSTMASTER_STORE_ATTACHMENTS left off, the only
new behaviour is that attachment metadata is recorded alongside stored content.