Skip to main content
Set PRE_CONSUME_SCRIPT=/path/to/your-script.sh and suchi runs it on every ingested document before any format-specific processing kicks in (qpdf, docsplit, blank removal, OCR, ZUGFeRD, …). The script can:
  • Rewrite bytes — decrypt with a proprietary tool, unpack a wrapper format, run a custom deskewer, whatever.
  • Emit metadata — attach tags or set custom fields via a JSON envelope on stdout.
  • Do nothing — a script that fails or produces no output is a no-op; ingest continues with the original bytes.
The CAS original is never handed to the script and never touched by it — you can’t accidentally destroy the upload from within a pre-consume hook.

Contract

The script is invoked as:
Environment (nothing else — the sandbox strips the parent’s env): Exit codes:
  • 0 — success. $SUCHI_OUTPUT is read (only if non-empty). Stdout is parsed as JSON.
  • non-zero — log at Warn, treat as “no changes”. Ingest keeps flowing; a failing pre-consume never aborts a doc.
Stdout JSON envelope (optional):
Unknown tags are auto-created. Unknown custom-field names are logged and skipped — create the field first via SQL / the admin surface. Limits (all configurable via env, defaults shown):
  • Timeout: 5 minutes (sandbox.Opts.Timeout)
  • Stdout ceiling: 1 MiB (parsed as JSON envelope)
  • SUCHI_OUTPUT ceiling: 200 MiB

Example: try candidate passwords from a file

A pre-consume-decrypt pattern for the case where suchi’s native decrypt path isn’t enough (e.g. a non-PDF encrypted format we don’t handle yet):
chmod 700, chown to the suchi user, then:

Example: attach a tag by filename pattern

Security

  • Runs under the same UID as suchi. Filesystem access is whatever that UID has — set chown on secret files defensively.
  • Runs in core/sandbox: empty env (beyond the vars above), hard timeout, bounded output, process-group kill on timeout.
  • No network by default — the sandbox doesn’t drop egress but suchi’s principle-8 posture assumes your reverse proxy / firewall blocks per-container outbound. If you need outbound (webhook, remote KMS), route it via a sidecar service the script curls to on localhost.

Why not just use plugins?

The plugin surface (plugin-api/) is Go interfaces + Go builds — great for suchi maintainers, painful for operators. PRE_CONSUME_SCRIPT is the classic shell-script escape hatch: “here’s a script, run it.” Meets operators where they are.