Skip to main content
Configuration comes from TOML or HuML files with environment-variable overrides. See config.

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:
The command takes the normal 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.
Through 0.4.1 the default output was HuML. Scripts that expect HuML pass --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_DIR writability, 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 the audit_events row total against the retention window
It does not test remote connectivity, available disk space, CAS shard capacity, or operator-script egress. Use --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.
Run --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-run document_added automations 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.”
Every rescan is explicit + selectable + previewable. There is no “rescan all” default and no background auto-rescan — the operator picks the set every time.

Filters (compose with AND)

Pre-flight

How it works under the hood

Each matching doc gets a fresh post-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_content bumps when the content-extraction chain changes (new format handler, byte-cap change, etc.).
  • pipeline_version_ocr bumps when the OCR engine or its data changes.
  • pipeline_version_llm bumps 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.
Bumping the constant in code, rebuilding, and running 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 through core/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.
Without an owner flag, Suchi selects the only owner with live documents. An empty selected system falls back only when the instance has one enabled admin. Every other case requires --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.
Bumps to the export format land a new version in manifest.json so importers can gate on it.

suchi version

Prints the binary version, build revision, and the Go version it was compiled with.