What is protected
Every delete that DBDock performs against object storage (S3, Cloudflare R2, Cloudinary, or local) passes through a guard before anything is removed. This covers automatic retention, WAL cleanup, and thedbdock delete / dbdock cleanup
commands, on both DBDock-managed storage and your own buckets.
Layer 1 — Delete guard
Before any object is deleted, the key is validated and checked against an allow-list of DBDock-owned prefixes:- Structural checks — empty keys, surrounding whitespace, leading slashes,
folder-style keys (
.../), path traversal (..), and wildcards are refused. - Prefix allow-list — keys must live under a DBDock prefix
(
backups/,wal/,dbdock_backups/, orbackup-). Anything outside the namespace DBDock created is never a deletion target. - Circuit breaker — a single run cannot delete more than
maxDeletesPerWindowobjects within a rolling window (default 1000 per minute), so a bug in a retention policy can’t cascade into wiping a bucket.
Layer 2 — Recycle bin
Deletes are soft by default. Instead of being removed immediately, an object is copied to a.trash/<timestamp>/ prefix (a fast server-side copy on S3/R2)
and only then removed from its original location. A small .trashmeta.json
sidecar records the original key, the time, and the reason.
Trashed objects are hard-purged automatically once they are older than
trashRetentionDays (default 14 days), and can be restored before then.
Layer 3 — Bucket versioning (recommended for R2/S3)
The recycle bin lives inside the same bucket. For protection against bucket-level mistakes (a bad lifecycle rule, a compromised key, an accidental bucket action), enable object versioning on the bucket itself so deletes become recoverable delete-markers.Enable versioning
In the Cloudflare dashboard open R2 → your bucket → Settings → Object
versioning and turn it on. On AWS S3, enable Bucket Versioning under
the bucket’s Properties.
Add a lifecycle rule
Add a lifecycle rule to expire non-current versions after a retention
window (for example 30 days) so old versions don’t accumulate cost.
Configuration
All of layer 1 and layer 2 are on by default. Tune them understorage.deletionSafety:
| Field | Default | Description |
|---|---|---|
enabled | true | Master switch for the guard and allow-list. |
recycleBin | true | Soft-delete to .trash/ instead of deleting immediately. |
trashRetentionDays | 14 | Days a trashed object is kept before it is purged. |
maxDeletesPerWindow | 1000 | Circuit-breaker limit per rolling minute. |
allowedPrefixes | DBDock roots | Override the protected prefix allow-list. |

