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-or-id> 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.
SELECTOR TRASHED AT NAME
id:15 2026-07-06T21:40:11Z return.pdf
id:88 2026-07-05T09:12:44Z old-drafts
Human output uses UTC second precision. --json retains the full authoritative
timestamps for automation.
Only trash roots are listed: trashing a directory produces one entry, and
docbank restore id:<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.pdf → return (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 physical number of raw or zstd bytes that GC can unlink immediately; it is not the decoded document size. 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.
Embedded maintenance¶
Embedded applications own the same lifecycle but schedule finite passes instead
of asking the daemon to drain a full operation. EmptyTrash limits one preview
or deletion to MaxRoots; zero selects the finite DefaultTrashEmptyMaxRoots.
GarbageCollect, Verify, and Repack accept a WorkBudget; zero
MaxObjects selects the finite DefaultMaintenanceMaxObjects, and explicit
values are capped by MaxMaintenanceObjects. A positive MaxBytes is a soft
limit that lets the current object finish. Pack retains its compatible soft
MaxBytes option and reports More when eligible loose backlog remains.
If a maintenance report returns More, schedule another pass with its non-empty
NextCursor. Reuse a cursor only with the operation that issued it; cursors are
opaque continuation positions, not stable snapshots. Repack cursors retain the
mapping, dead-pack, or sparse-pack phase even when that phase has no blob-hash
position, so continuation does not restart completed mapping work.
Embedded GC intentionally considers only bounded unreachable catalog rows; it does not scan loose directories for untracked files. Embedded Verify re-hashes only a bounded blob page and does not validate the whole metadata catalog. The daemon commands above preserve full orphan reconciliation and whole-catalog verification.
Physical maintenance never determines whether application data is live. Tree, trash, version-retention, and any future external-reference policy decide which logical references remain. GC only reclaims authority after those policies have made a blob unreachable, and Repack only reclaims pack space made dead by GC.
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.