By: Michael Lynch <git@mtlynch.io>
Make media publication recoverable across crashes Uploads and Tinybeans previously wrote several media variants before inserting their database rows. A partial write, later conversion failure, failed insert, or process crash could leave incomplete local files and orphaned S3 objects, while importer comments and reactions committed independently of the parent item. Litestream treats files as immutable published artifacts and writes local data through a sibling temporary file, fsync, close, atomic rename, and parent-directory fsync in reference/litestream/file/replica_client.go and reference/litestream/db.go. It also removes crash leftovers during startup and advances logical state only after durable storage succeeds. Apply those principles to media without pretending SQLite and object storage share a transaction. Persist a media_publication_intents row before writing variants, keep pending work invisible to gallery queries, and finalize the media row, comments, reactions, and intent deletion in one SQLite transaction. Local writes now use Litestream's crash-consistent rename protocol. S3 uses immutable final keys with conditional creation and idempotent deletion. Both application startup and the Tinybeans importer recover pending intents before accepting new work, while failed writes clean every derived filename and retain the intent when cleanup itself fails. The central implementation issue was the absence of an atomic rename or cross-system transaction for S3. Copying a temporary object would still be multi-step and add permissions and I/O, so the SQLite intent is the visibility barrier and cleanup is retryable. A final transaction error can also have an ambiguous commit outcome, so recovery relies on the durable presence of the intent instead of immediately deleting possibly published media. Tests use in-memory SQLite and fake storage to cover rollback and recovery without filesystem writes. No external blockers were encountered. Focused tests, the full Go checks, and every flake target passed before the amendment.