# B2 bucket lifecycle — `uploads/` prefix The `pending_uploads` cleanup worker (cron `30 * * * *`, see `internal/worker/jobs/handler.go::HandleUploadCleanup`) reaps unclaimed upload sessions every hour, deleting both the row and the corresponding B2 object. This bucket-level lifecycle rule is a **backstop** — it catches B2 objects that survive the row deletion (e.g. worker crashed mid-loop, B2 delete errored, manual DB tampering). ## Rule Apply via the Backblaze web console: **Bucket → `honeyDueProd` → Lifecycle Settings → Custom** ```json [ { "fileNamePrefix": "uploads/", "daysFromUploadingToHiding": 7, "daysFromHidingToDeleting": 1 } ] ``` Effect: any object under the `uploads/` prefix is hidden 7 days after upload, then permanently deleted 1 day after that. Total maximum lifetime of an orphaned object: 8 days. This rule does NOT affect: - `images/`, `documents/`, `completions/` — legacy multipart-uploaded objects, which are managed by the existing `task_completion_image` / `document_image` / `document.file_url` references. ## Why a backstop, not the primary mechanism The application worker is the primary mechanism because: 1. It can delete the **DB row** alongside the B2 object — lifecycle alone would leave dangling `pending_uploads` rows. 2. It runs hourly vs. lifecycle's once-per-day evaluation — much tighter recovery window for the common case. 3. It produces logs / metrics for orphan rate observability. ## Verification After applying: ```bash b2 bucket get-info honeyDueProd | jq '.lifecycleRules' ``` Should show the rule above. If you don't have the B2 CLI: ```bash curl -u "$B2_KEY_ID:$B2_APP_KEY" https://api.backblazeb2.com/b2api/v3/b2_authorize_account # Then use the returned authorization_token + apiUrl to call b2_get_bucket ```