Skip to content

cold-store-reaper: put_backup files DB dumps with no read-back verification — a corrupted Drive copy is ledgered as a good backup, unlike bundle upload which md5-verifies #3230

Closed

Description

@dezren39

Problem

pkgs/cold-store-reaper/cold_store.py — put_backup() (lines 993–1027) files a DB dump into Drive and records it as stored, but never verifies what Drive actually received. The bundle path in the same file does verify: upload() (lines 908–930) reads back files.get md5Checksum + size and refuses to mark the bundle verified on a mismatch. put_backup() has no equivalent step:

  1. file_id = self._resumable_upload(src, name, folder_id) (line 1019)
  2. a transfer_ledger row upload / backup-upload is written immediately (lines 1020–1024)
  3. the result dict is returned as success

That ledger row is load-bearing: _upload_accounting() (lines 497–541) computes the project-budget backup bytes from backup-upload rows, and backup_supabase() / backup_d1() / scripts/export-turso.sh all file through put_backup(). A backup whose stored bytes differ from the local dump (truncated/corrupted server-side assembly, wrong content) is counted as safely filed, and the corruption is only discovered at restore time — for a system whose entire purpose is disaster recovery of the estate's databases (d1, supabase, turso dumps, nightly via scripts/nightly-backup.sh).

Evidence (verified this run)

Laptop probe ~/git/fleet/probe-2026-10-31-laneB-backupverify/probe.py against the real cold_store.py (sha256 fba42e08027514abae2de1602e755854ebfd6a3b578a5eece34b6c0becd39b95, identical on the sandbox and laptop clones, main 357ec7fb):

  • _resumable_upload stubbed to return a file id while Drive holds different bytes, and cold_store.http_request rigged to raise if any verification request is attempted.
  • Result: put_backup("d1", dump.sql) returned success ({'db': 'd1', 'name': 'd1-20261031T212413Z-dump.sql', 'drive_file_id': 'file-corrupt-1', 'bytes': 2000}), zero http_request calls were made during the call, and a transfer_ledger row upload / 2000 / backup-upload was written for the full source size.

So there is provably no read-back of any kind on the backup path — not md5, not size, not even a files.get.

Why it matters

  • The bundle path's verification exists precisely because an unverified upload cannot be trusted; DB backups are at least as critical as bundle objects (they are the recovery source for the estate's databases, and the restore drill in scripts/restore-drill.sh exercises d1 backups).
  • Budget accounting treats the unverified row as stored bytes, so the failure is invisible in status as well.
  • The fix pattern already exists in the same file (upload() lines 908–930) and the test fake Drive already serves files.get with md5Checksum/size for the bundle tests.

Acceptance criteria

  • After _resumable_upload, put_backup() reads back the uploaded file's md5Checksum and size (Drive files.get, fields id,md5Checksum,size) and compares them to the local file's md5 (md5_file) and size.
  • On any mismatch, put_backup() raises ColdStoreError naming the mismatch and writes no backup-upload ledger row (mirroring upload()'s "bundle NOT marked verified" behaviour).
  • On match, behaviour is unchanged: same dated name, same ledger row, same result fields.
  • A regression test in pkgs/cold-store-reaper/tests/test_cold_store.py covers the mismatch case (fake Drive reporting a different md5/size → error, no ledger row) and the existing backup tests still pass (python3 -m unittest discover -s pkgs/cold-store-reaper/tests).

Filed by new-issue-creator-code-b, 2026-10-31. Distinct from #1427 (CI coverage for this package, closed today) — this is a runtime verification gap in put_backup itself.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    effort/sSmall: expected under 2 hours of work
    app/cold-store-reaperPackage/app: cold-store-reaper (pkgs/ or apps/ in this repo)
    on Oct 31, 2026
  2. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator-code-b
    • Session / ID: new-issue-creator-code-b cron run 2026-10-31 16:20 CDT
    • Found via: filed this run — read put_backup() (cold_store.py:993-1027) against the bundle upload() verification path (cold_store.py:908-930) and verified the gap with a laptop probe driving the real module (sha256-matched on both clones): _resumable_upload stubbed to a corrupt Drive copy, http_request rigged to raise on any verification attempt — put_backup still returned success, made zero verification calls, and wrote the full-size backup-upload ledger row.
    • Task: Filed this issue: put_backup files DB dumps (d1/supabase/turso, nightly) with no md5/size read-back, while bundle upload() in the same file verifies both and refuses to mark verified on mismatch.
    • Expectation: effort s; done when put_backup verifies md5+size after upload and writes no ledger row on mismatch, pinned by a regression test in pkgs/cold-store-reaper/tests/test_cold_store.py.
    • Links: Evidence body: hidden_files/issue-code-coldstore-putbackup-verify-body.md · Probe: laptop ~/git/fleet/probe-2026-10-31-laneB-backupverify/ · Distinct: ci(cold-store-reaper): no workflow runs the package's test suites — 2,700+ lines of tests execute only when a human remembers #1427 (CI coverage, closed)
  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 31, 2026
  4. 48 remaining items

  5. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  6. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — picked up
    Agent: repo-worker-code (OpenChamber scheduled lane)
    Session: ses_e5e63b9e2c2ffeNWcJTcGcVvwB · queue task 5674
    Task: Resume verification gate for the existing cold-store backup PR.
    Fresh state: #3230 OPEN; PR #4330 OPEN at f4351b8 and authored by dezren39. Required checks remain QUEUED; CodeRabbit skipped. No files or PR content changed.
    Next: park pending checks; revalidate PR/checks/review and ownership before any merge action.

  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — parked
    Agent: repo-worker-code (OpenChamber scheduled lane)
    Session: ses_e5e63b9e2c2ffeNWcJTcGcVvwB · queue task 5674
    Verified: #3230 OPEN; PR #4330 OPEN at f4351b8, authored by dezren39. Checks python: unittest suites and worker: local proof are QUEUED in run 41440146673; CodeRabbit skipped.
    Reason: CI pending; PR is Drew-owned and must not be modified or merged by this lane. No files or PR content changed.
    Resume: after checks complete, freshly revalidate PR/checks/review and ownership. Queue parked 20 minutes; work/assigned stays set.

  8. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · state: parked

  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up for ownership revalidation

  11. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked — Drew-owned PR remains open

  12. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up for ownership revalidation

  13. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked — Drew-owned PR remains open

  14. added a commit that references this issue on Nov 2, 2026
  15. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

  16. removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedQueue worker lifecycle state
    on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/cold-store-reaperPackage/app: cold-store-reaper (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions