Skip to content

Trash, GC, Repack & Verify

docbank rm is always a soft deletion. There is no rm --hard, and neither GC nor repack runs automatically. Permanent deletion and physical reclamation are separate operator decisions, so the window for regret is as wide as you want it to be:

flowchart LR
    A[live node] -- "docbank rm" --> B[trash]
    B -- "docbank restore" --> A
    B -- "docbank trash empty --run" --> C[tree metadata gone; blob may be unreachable]
    C -- "docbank gc --run" --> D[blob authority removed]
    D -- "loose storage: same GC run" --> E[loose file removed]
    D -- "packed storage" --> F[dead immutable pack range]
    F -- "docbank storage repack" --> G[sparse source pack retired]

Stage 1: Trash (rm, restore, trash list)

docbank rm <path> marks the node — and its whole subtree for directories — as trashed. The tree entry disappears from ls, tree, and search; the name becomes reusable; the bytes are untouched. Running GC after rm does nothing to that content because trash remains a live, restorable reference.

docbank trash list
docbank trash list --json
ID   TRASHED AT                     NAME
15   2026-07-06T21:40:11.0021Z      return.pdf
88   2026-07-05T09:12:44.8810Z      old-drafts

Only trash roots are listed: trashing a directory produces one entry, and docbank restore <id> brings the entire subtree back to its original location. If a live node has since taken the name, the restored node is suffixed (return.pdfreturn (2).pdf); if the original parent was itself permanently deleted, the node is restored under /.

Trashing a subtree stamps every node with the same trash time, so a nested directory trashed before its parent keeps its own independent trash entry — restoring the parent doesn't resurrect things you trashed separately.

trash list --json returns the roots under items. For maintenance automation, trash empty --json returns candidate_roots, deleted, and run; it remains a dry run unless --run is present.

Stage 2: Empty the trash

docbank trash empty                        # dry run: everything
docbank trash empty --older-than 30d       # dry run: items trashed ≥30 days ago
docbank trash empty --older-than 30d --run # permanently delete those items

The command is a dry run unless --run is present. An executed run permanently deletes the selected tree entries. The document bytes are still on disk and may still be referenced by another node or version. Only content with no remaining reference becomes a GC candidate.

Stage 3: Garbage collection (gc)

Unreachable blob authority is removed only by explicit GC. A blob is reachable — and therefore never collected — while any of these reference it:

  • a live node,
  • a trashed node (trash is always restorable in full), or
  • a retained prior version of an edited document. Explicit, preview-first version pruning can release that reference without deleting the current file.
docbank gc          # dry run: candidate count and reclaimable bytes
docbank gc --run    # remove unreachable authority and loose files

For loose blobs, the reported reclaimable count is the number of bytes that GC can unlink immediately. A packed blob becomes logically dead when GC removes its catalog authority, but its stored bytes remain in the immutable pack until repack compacts that container; GC reports those bytes separately as pending repack rather than claiming they were reclaimed.

gc --run runs behind the daemon's maintenance gate, so a concurrent import can never dedup against a blob that's being deleted (see Ownership & Concurrency). Files are removed before their rows: a crash in between leaves rows-without-files, which the next gc --run reconciles and verify flags in the meantime. Orphan blobs from interrupted ingests are reclaimed the same way.

Stage 4: Repack packed storage (storage repack)

GC cannot remove one range from an immutable pack file. After gc --run, dead packed payload appears in storage status as dead_packed_bytes. An explicit docbank storage repack rewrites eligible sparse packs with their live blobs and retires the old source packs. Empty packs are retired directly.

Repack is not part of rm, trash empty, or gc, and there is currently no background maintenance scheduler. This is intentional: repacking may rewrite unrelated live blobs that share the same pack, so its timing and selection thresholds remain an independent storage-policy decision.

Verify

docbank verify

Validates logical metadata and audit history, then re-hashes every stored blob against its recorded SHA-256. It reports metadata failures or missing, corrupt, and unreadable problem blobs, exiting non-zero if anything is wrong. Corruption is something you detect on your schedule, not something you discover the day you need the document. Run it after moving the vault between disks, before deleting original sources, and periodically from cron.

Next: protect what remains with Backup & Restore, and see Integrity & Trust for what verify defends against.