Store attachments through the dedup pipeline instead of one object per upload

FixMediaService
Shipped
August 6, 2026 at 8:45 PM UTC
Author
Kamo
Commit
0eda67f

Attachments wrote their own "<imgId>/<fileName>" object on every upload, bypassing the content-addressed store entirely — the same logo-draft.png attached three times in one conversation occupied three objects. Uploads now hash into a shared ImgDat, so identical content is stored once no matter how often it is attached, and an attachment that matches a document already in the system costs no storage at all. The Img row stays per-attachment: it carries the conversation, uploader and revocation state, and it is still what access is checked against. Only the bytes are shared. Reads resolve the content-addressed key when the row has a dat and fall back to the legacy per-upload object when it does not, so attachments predating this keep working — including any written by an instance that has not rolled over yet. ChatAttachmentDedupBackfill folds those legacy attachments in on startup: it selects only rows with a null dat and sets that dat last, so it is idempotent, self-healing across the rollout, and a failure on one row leaves it on the still-working legacy path rather than failing startup. Legacy objects are left in place; nothing deletes from MinIO.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing