Selecting a filing system
Taxonomy import/export/merge, bundle import/verify, native export, refile and rescan accept--system CODE. Omission targets original system 1, except a taxonomy file’s
explicit system selects its own destination. A conflicting --system is an error.
These local commands are trusted administrator operations, not token-scoped APIs.
See JD for first-import introduction and immutable system/address identity.
suchi serve
Runs the HTTP server. Blocks until SIGINT/SIGTERM.
- Applies embedded migrations up to the current schema version.
- Creates a neutral System / Inbox baseline until a filing tree is selected.
- Starts the durable-outbox dispatcher.
- Logs the effective egress surface at INFO.
- Listens on
LISTEN_ADDR(default:8000).
suchi healthcheck
Probes LISTEN_ADDR’s /readyz over loopback. Exit 0 if the server
answers 200, non-zero otherwise. Wired as the Docker HEALTHCHECK, so the
image does not depend on wget or curl for readiness checks.
suchi auth reset-two-factor --email EMAIL
Offline recovery for a local-password account that has lost both its
authenticator and recovery codes. Stop the Suchi server and every other archive
writer first:
DATA_DIR/.suchi.lock, finds the normalized email,
and removes its TOTP row, recovery-code digests, and browser sessions in one
transaction. It preserves the password, account, documents, permissions, API
tokens, and paired mobile apps. The reset is idempotent and writes retained
operator.user_two_factor_reset audit history with a system actor.
The command does not open the encrypted seed and does not need .decrypt-key.
Run it only on the intended stopped archive. A missing account or a live process
returns non-zero without changing account state.
suchi import [flags]
Point --from at your existing DMS’s export bundle. See
importer for the full walkthrough.
Flags
Category-resolution flags (
--flat, --map-jd, --auto-jd) are mutually
exclusive.suchi gc [flags]
Mark-and-sweep the CAS: walks every blob under $DATA_DIR/blobs/
and deletes anything not referenced by a document original, archive,
decrypted copy, thumbnail, or user avatar. Trashed document rows still count.
The blob’s on-disk mtime gates a grace window. It is a retention buffer, not
protection against concurrent writes: an upload can reuse an old blob before
committing its new database reference.
It also counts empty folders under $DATA_DIR/rendered/. When documents move,
because a title, category or folder layout changed, the server removes only the
old link and leaves its folders in place. GC removes folders that contain nothing,
deepest first, and never rendered/ itself. A file or link added to a folder keeps
that folder, because removal refuses a non-empty directory.
Dry-run by default. Nothing is deleted until you pass --apply.
Before applying, stop Suchi and every other archive writer, including CLI
imports. Keep them stopped until GC finishes. Do not apply GC while a backup
is copying the blob tree.
Flags
Trash and its automatic retention remove document rows and rendered files,
but retain CAS blobs for this offline collection. Run GC during a planned
maintenance window after taking a complete backup.
suchi taxonomy validate <file>
Validate exactly one .toml or .huml suchi-taxonomy/v1 file, offline, using
the same strict parser as imports. Success is silent (exit 0); file/validation
failure exits 1; usage failure exits 2. Reads are limited to 1 MiB. YAML and
unsupported extensions fail. This command runs before server configuration or
database initialization: no data directory, provider or running server is needed,
and even malformed server configuration does not prevent validation.
From a source checkout, validate a bundled unprefixed tree:
suchi taxonomy import <file> [flags]
Preview a suchi-taxonomy/v1 import against the local instance. Dry-run is the
default; the summary identifies the destination, original-archive preservation and
initial access for a newly created system, alongside category additions and
collisions. --apply performs the same preview and transactional freshness
recheck as the HTTP importer. Imports never create or change automations.
Flags
Only safe initial unconfigured setup can replace the bootstrap tree. Filed
documents (including recoverable Trash) and dependent user configuration prevent
replacement. Later imports merge: preserve local descriptions and require a
remap/skip for collisions.
Without
--existing-system-code, first prefixed Apply adopts the original archive
under the file’s code/name, retaining IDs and grants. Newly created systems initially
admit only instance admins. After introduction unprefixed imports cannot remove it.
Apply queues the generated filing-index refresh in the same transaction. If no
server is processing jobs, refresh remains pending until the server runs; command
success does not claim the physical 00.00 archive.huml has updated.
suchi taxonomy export [flags]
Export a validated current snapshot on stdout, with id = "archive" and content
version = 1. Only user structure is serialized; generated System/49 is implicit.
Automations are not exported; a full archive backup carries them. Legacy incompatible
reserved rows fail export without repairing or hiding database contents.
--format huml.
suchi taxonomy merge [flags]
Admin operation for post-import cleanup: after years of use (or a
big import), taxonomy tables accumulate near-duplicates
(“BESCOM” and “Bescom”, “Tax” and “TAX”). Merge picks one, moves
every reference from the other, deletes the source, and rewrites
any automations that referenced the source name.
Dry-run by default. Reports how many docs would move before you
commit.
Flags
Example:
suchi doctor
One-shot diagnostic report. Prints:
- Managed egress destinations for OIDC, enabled mailboxes, Microsoft OAuth, and the active LLM endpoint, with credentials removed
- Presence of optional pipeline binaries on
PATH; versions are not queried - Configured, installed, and missing Tesseract language packs
- Database schema version compared with the embedded target
- Imported taxonomy preset, version, and content hash per system when available
$DATA_DIRwritability, and a warning when accounts other than its owner and group can reach it- Operational indicators: last-backup age (WARN past 2×
BACKUP_INTERVAL), dead-job count, oldest running job (WARN >15m), effective upload cap, last boot’s orphaned-job reclaim count, and theaudit_eventsrow total against the retention window
--scrub-cas for the explicit full CAS hash
walk.
Flags
Missing optional binaries or OCR language packs print but do not fail — the
ingest pipeline degrades gracefully. An explicit CAS scrub exits non-zero on
missing or corrupt content. Other hard errors also return non-zero. Useful for
air-gap verification and support tickets.
When
DATA_DIR is unset and the /data default cannot be created, the
doctor falls back to the current user’s OS config directory (for example,
~/Library/Application Support/suchi on macOS). An explicitly configured
DATA_DIR is never replaced by the fallback.
--quarantine-corrupt only while the server and workers are stopped. The
report-only scrub streams files without modifying them; quarantine moves each
bad path out of the live CAS for later inspection or restoration.
suchi mcp [flags]
Start a Model Context Protocol server that adapts the REST
surface into MCP tools. See MCP guide for the client-side
setup + tool reference.
Flags
The binary also responds to
suchi-mcp when invoked via symlink — the
argv[0] dispatcher rewrites the command so command: "suchi-mcp" in a
Claude Desktop config stays clean.
Uses a scoped API token, not an admin token. Every MCP call is attributed to that
token’s owner and stays within its bound filing system; there is no MCP system flag.
suchi demo [flags]
Seed $DATA_DIR from the versioned manifest maintained by
suchi-demo.
Flags
Exactly one corpus source flag is required. Suchi does not carry a corpus URL,
content version, or checksum and never downloads a release implicitly.
manifest.schema_version is the only compatibility gate; manifest.version
identifies corpus content and may change independently.
Corpus input is capped at 256 MiB compressed, 1 GiB extracted, and 10,000
archive entries; checksum responses are capped at 4 KiB. See
Public demo mode for cache and trust boundaries.
The manifest owns fixtures, metadata, saved views, and symbolic automations.
Same-name views and automations are preserved on repeat runs, and existing
documents are reported rather than duplicated. See demo instance
for the runtime and deployment boundaries.
suchi refile [flags]
Rerun document_added automations and enqueue a render/move job for every live
document in the selected system after an automation or path-template change.
Full walkthrough at Refile.
Flags
Safe against a live server — uploads that arrive mid-sweep use the
normal postingest chain and land under the new tree automatically.
suchi rescan [flags]
Re-run the content-extraction pipeline (qpdf → OCR → content write →
automations → render → thumb → post-classify) against
originals for a selected set of docs. Sibling to refile; the
split is:
refile= re-rundocument_addedautomations and enqueue a render job. Cheap, changes filing metadata. Use after preset, path-template, or automation edits.rescan= re-run everything, including OCR against the original blob. Expensive but authoritative — this is the tool for “I swapped tesseract for ocrmypdf” or “I upgraded the LLM classifier model.”
Filters (compose with AND)
Pre-flight
How it works under the hood
Each matching doc gets a freshpost-ingest job in the durable
outbox. When suchi serve is running (or the next boot picks the
job up), those drain through the same chain a fresh upload runs.
Idempotent — re-running against the same doc bumps its
pipeline_version_* markers but doesn’t otherwise diverge. Batched
in groups of 200 so a crash mid-run leaves the outbox consistent.
Signature semantics — each pipeline step reads a version constant
and writes it onto the doc on success:
pipeline_version_contentbumps when the content-extraction chain changes (new format handler, byte-cap change, etc.).pipeline_version_ocrbumps when the OCR engine or its data changes.pipeline_version_llmbumps when the classifier’s prompt or validated result contract changes (constant lives in the plugin package). Operator endpoint, model, key, timeout, and threshold changes use an explicit selected rescan instead of stale detection.
suchi rescan --stale <kind> catches only affected docs — the
selective alternative to a bulldozer.
Examples
Discovery in Approvals
The CLI is the surgical path: you pick filters, run the command, and own the outcome. Approvals discovers older successful pipeline results only when the tagged release policy carries a positive recommendation for that pipeline. Its proposal offers Rescan all, Try 20 first when more than 20 documents match, and Dismiss. Both surfaces route throughcore/rescan.Enqueue.
Dismiss cleanly closes the run; the card does not come back until the committed
recommendation increases. If someone rescans out-of-band and the stale count
hits zero, the detector cancels the pending run on a later boot carrying that
recommendation.
A recommendation threshold of 0 creates no new proposal for that kind and
does not cancel an existing reminder. Automatic LLM proposals also exclude
stored version 0, which means classification never completed. Enabling a
classifier therefore affects new ingests without turning the existing archive
into an upgrade proposal. An explicit --stale llm command still includes
those documents. See Pipeline versions for the tagged
policy and full boundary.
suchi export --out FILE.zip
Portable takeout of live originals and selected metadata from one system.
Manifest version "3" identifies its code/name. The ZIP contains original
bytes, documents/<name>-<id>.json sidecars and selected-system taxonomy-table
dumps, not a database restore graph. Sidecar format stays 1, adding optional
title_owner, jd_system, jd_address, custom_fields, and
archive_serial_number. Document IDs can change on intake.
Stop the server before running this offline command. The archive lock rejects an
export while another Suchi process owns DATA_DIR.
--owner-id or --all. Output must be outside
DATA_DIR.
Suchi publishes a complete mode-0600 archive atomically, refuses an existing
destination unless --force is set, and removes temporary output after any
failure. Missing original blobs fail by default. --allow-incomplete is an
explicit recovery override, and the manifest records its skipped originals.
After extracting the zip, placing each original beside its sidecar in an
fs-watch staging directory restores title ownership, title, source date,
correspondents, tags, JD category, custom-field values, and available ASNs within
the configured target. An ASN already retained by a live, trashed, or historical
row rejects that document without a partial import. Follow the takeout
reingestion guide for target preparation, safe copy order
and verification steps. This is not a full metadata round trip: notes,
document-link field values, and extraction-managed invoice_* fields are omitted
(the last are read again from the original on intake), while exported
sensitivity, mime_type, and sha256 sidecar fields are currently
informational and ignored on ingest.
MIME and SHA-256 are recomputed from the bytes, and sensitivity uses the target
instance default.
An input jd_system must match the watcher’s configured target; it cannot route
or create a system. jd_address is an informational category address, can repeat
across documents, and is ignored for routing and identity. Takeout manifest
version 3 and sidecar format 1 are unchanged. To retain IDs, memberships, ACLs
and relationships exactly, use a whole-instance backup, not
native takeout or Paperless import.
Not included in this version:
- Avatars, thumbnails (derived — regenerate on re-import).
- Document versions history.
- Approvals + audit history.
- Share links + saved views.
- Notes, document-link field values, and extraction-managed
invoice_*values.
version in manifest.json
so importers can gate on it.