R2 Lifecycle Rules: `brioso-jobs` Bucket

R2 Lifecycle Rules: brioso-jobs Bucket

Never add a lifecycle rule touching the litestream/ prefix. That
prefix holds Litestream’s DB WAL replication stream. A lifecycle rule that
expires or deletes objects under litestream/ will corrupt backups. This
is not a hypothetical — treat it as a hard boundary when editing rules in
the Cloudflare dashboard.

Why this doc exists

The brioso-jobs bucket is on Cloudflare R2’s 10GB free tier and was
observed at ~16GB. Rather than deleting paid/delivered content, we expire
only artifacts that are either rebuildable, orphaned, or explicitly
time-boxed. Paid or delivered content is never deleted by this system.

Dashboard lifecycle rules

These two rules are hand-configured via the Cloudflare dashboard
(Bucket → Settings → Object lifecycle rules). There is no
Terraform/IaC coverage for this bucket, so if it is ever recreated, these
rules must be re-applied by hand from this doc.

Rule 1 — kits/ prefix, expire after 7 days

Kit ZIPs are rebuildable from their source tracks on demand, so they don’t
need to survive indefinitely. Objects under the kits/ prefix expire 7 days
after upload.

Rule 2 — logos/ prefix, expire after 30 days

Logo uploads that are never attached to a finished kit are orphaned data
with no other reference pointing at them. Objects under the logos/ prefix
expire 30 days after upload.

What is NOT a dashboard rule

Two more retention categories exist but are not representable as
Cloudflare dashboard lifecycle rules, because the dashboard feature has no
awareness of database state (job status, payment status):

  • failed-job — artifacts belonging to failed or cancelled jobs are
    swept after 14 days, excluding artifacts that are children of paid kits
    and any job with a paid or free-unlocked track (BRI-374: cancelJob is
    legal from any state, so a cancelled job can still hold delivered
    content — payment status outranks the sweep). After a whole-prefix sweep
    the groomer stamps the job row’s artifacts_purged_at tombstone, and the
    studio renders those generations as “no longer available” (terminal
    state, no player) instead of serving broken playback.
  • watermarked-master — a track’s watermarked master is swept
    immediately once the track is marked paid (no grace period). Free-tier
    free_unlocked tracks are never touched by this category, since their
    watermarked file is their only copy.

Both categories require checking job/track state in the database before
deleting, which the dashboard’s prefix+age model can’t express. They are
handled by a code-side groomer instead:
packages/dispatch/src/storageGroomer.ts.

Keeping this doc in sync

The thresholds above must match packages/storage/src/retention.ts’s
RETENTION_RULES array (the mode: "age" entries) by hand — there is no
automated sync between the dashboard config, this doc, and that file. If you
change a threshold in one place, update the other two in the same change.