Mailstrix › Syntax & synopsis

Syntax & synopsis

Complete reference for the two binaries — the strixd scanning backend and the strix-scan CGO-free client — plus every MAILSTRIX_* environment variable, the HTTP /scan API, the request/response headers and the JSON verdict shape. Anything set on the command line can also be set by environment variable; flags win over the environment, and both are clamped to safe ranges at startup.

strixd — the scanning backend

One binary, several subcommands. With no command it defaults to serve. Run strixd <command> -h for that command's own flags.

strixd <command> [flags]

  serve        run the HTTP backend (/scan, /metrics, /health) — the default
  scan         scan files/dirs/stdin against the rules in-process
  fetch-rules  download an updated compiled rule bundle into the cache
  check-rules  compile the rules, print the count, exit non-zero on failure
  extract      dump what the container extractor carves from a file (no scan)
  info         print build, libyara, and loaded-rules-bundle identity
  health       probe the local /health endpoint (container HEALTHCHECK)
  version      print the version

extract is the debugging window into the moat: it shows every layer Mailstrix carves out of a file — OLE/OOXML parts, decompressed VBA, archive members, carved PE/ELF — without running YARA, so you can see exactly what the matcher will be handed.

strixd serve — flags

Each flag mirrors an environment variable (shown in parentheses); the flag overrides it.

FlagEnvDefaultPurpose
-hostMAILSTRIX_HOSTall interfacesHTTP bind host. Bind to loopback/private only.
-portMAILSTRIX_PORT8079HTTP bind port.
-backend-timeoutMAILSTRIX_BACKEND_TIMEOUT1sWhole-request backend budget.
-scan-timeoutMAILSTRIX_SCAN_TIMEOUT8sPer-scan libyara timeout.
-max-concurrentMAILSTRIX_MAX_CONCURRENTCPU countMax simultaneous libyara scans (CPU gate).
-max-bodyMAILSTRIX_MAX_BODY8 MiBMax request body; oversized is rejected, never truncated.
-tokenMAILSTRIX_TOKEN[_FILE]noneShared secret for /scan. Prefer the _FILE form (keeps it out of the process list).
-rules-dirMAILSTRIX_RULES_DIRnoneDirectory of *.yar/*.yara to compile at start.
-rulesMAILSTRIX_RULESnonePrecompiled .yac bundle; wins over -rules-dir.
-cache-dirMAILSTRIX_CACHE_DIRnoneWritable dir for the live bundle; seeded from -seed-rules when empty/unreadable.
-seed-rulesMAILSTRIX_SEED_RULESnoneBaked read-only .yac used to (re)seed the cache.
-verboseMAILSTRIX_VERBOSEoffPer-request logging.
-log-stdoutMAILSTRIX_LOG_STDOUToffInfo/access logs to stdout; errors stay on stderr.

strix-scan — the CGO-free client

A tiny client with no libyara dependency, for the Sieve/LDA path and for boxes that can't link a C library. It reads a file argument (or stdin), POSTs it to a running strixd, and signals the verdict through its exit code0 = clean/no-match (and on a transport error when -fail-open), non-zero = match or hard error.

strix-scan [flags] [file|-]
FlagDefaultPurpose
-url— (required)Base URL of strixd, e.g. http://127.0.0.1:8079 (MAILSTRIX_URL).
-token-filenoneFile holding the shared secret (preferred — not in the process list). Falls back to MAILSTRIX_TOKEN.
-tokennoneShared secret inline. Optional; omit for a token-less strixd.
-filenamenoneAttachment name, sent as X-MAILSTRIX-Filename so name/extension-keyed rules fire.
-timeout10sHTTP request timeout.
-max-body8 MiBMax bytes read from input; oversized fails open without scanning a truncated prefix.
-fail-opentrueOn a transport/HTTP error treat the message as CLEAN (exit 0) so a scanner outage never blocks delivery. =false surfaces the error (exit 2).
-quietfalsePrint nothing on a match — rely on the exit code only.
-versionPrint version and exit.

Environment variables

Every MAILSTRIX_* knob, grouped. Booleans accept any non-empty value as true. Durations like the refresh/TTL/timeout values are seconds unless a serve flag accepts Go duration syntax (500ms, 2s). All values are clamped to safe ranges at startup, so a non-positive value cannot disable a guard.

Server & admission

VariableDefaultMeaning
MAILSTRIX_HOSTall ifacesHTTP bind host.
MAILSTRIX_PORT8079HTTP bind port.
MAILSTRIX_ICAP_ADDRdisabledWhen set, also start an ICAP listener on this address.
MAILSTRIX_MAX_BODY8388608 (8 MiB)Max request body in bytes.
MAILSTRIX_MAX_CONCURRENTCPU countCPU gate — simultaneous libyara scans.
MAILSTRIX_MAX_INFLIGHT0 → 2× concurrentAdmission gate — in-flight buffers held for the whole request.
MAILSTRIX_BACKEND_TIMEOUT1sWhole-request backend budget.
MAILSTRIX_SCAN_TIMEOUT8sPer-scan libyara timeout.
MAILSTRIX_PPROFoffEnable the net/http/pprof endpoints (debug only).

Effort

VariableDefaultMeaning
MAILSTRIX_EFFORTauto / maxExtraction depth: 1 = raw bytes + shallow, higher = deeper recursion, auto derives it from admission-gate pressure. Per-request override via the header.
MAILSTRIX_EFFORT_MAXbuild defaultCeiling the effort level (and the X-MAILSTRIX-Effort header) is clamped to.

Authentication

VariableDefaultMeaning
MAILSTRIX_TOKENnoneShared secret for /scan. Empty = token-less (open) backend.
MAILSTRIX_TOKEN_FILEnoneRead the secret from a file (preferred; not in the process list).
MAILSTRIX_TOKEN_NEXTnoneSecond valid token, for zero-downtime rotation — both accepted during the overlap.
MAILSTRIX_METRICS_AUTHoffRequire the token on /metrics too (otherwise metrics are open).

Rules & cache

VariableDefaultMeaning
MAILSTRIX_RULESnonePrecompiled .yac bundle (wins over RULES_DIR).
MAILSTRIX_RULES_DIRnoneDirectory of source rules to compile at start.
MAILSTRIX_RULES_URLnoneBundle URL for fetch-rules (the daily rules-current release).
MAILSTRIX_CACHE_DIRnoneWritable dir for the live bundle; self-heals from the seed.
MAILSTRIX_SEED_RULESnoneBaked read-only .yac used to (re)seed the cache.
MAILSTRIX_RULES_MAX_AGE0 (off)Mark the bundle stale after N seconds; stale → /ready reports 200 but logs a warning.
MAILSTRIX_BIGFILE_RULESnoneSeparate bundle applied only to inputs over the threshold.
MAILSTRIX_BIGFILE_THRESHOLD6291456 (6 MiB)Size above which the big-file bundle is used.
MAILSTRIX_RULE_ALLOWLISTnoneOnly these rules may produce a verdict (tags-only).
MAILSTRIX_RULE_DENYLISTnoneInline denylist of rule names to suppress.
MAILSTRIX_DENYLIST_FILEnoneFile of denylisted rule names; reloaded on SIGHUP.
MAILSTRIX_CANARYoffShadow mode — tag every match mailstrix_canary=1 instead of acting; for safe rollout.

Verdict cache

VariableDefaultMeaning
MAILSTRIX_CACHE_SIZE65536In-memory L1 verdict entries (LRU+TTL).
MAILSTRIX_CACHE_TTL3600sVerdict cache TTL.
MAILSTRIX_REDIS_URLnoneOptional L2 verdict cache, shared across replicas.
MAILSTRIX_REDIS_PREFIXdefaultKey prefix for the Redis L2 cache.

Threat-intel feeds (abuse.ch)

All feeds are optional and refresh in the background. Refresh values are seconds.

VariableDefaultMeaning
MAILSTRIX_URLHAUS_KEYnoneURLhaus auth key; enables malware-URL lookups on extracted links.
MAILSTRIX_URLHAUS_MAX_URLS64Max URLs checked per message.
MAILSTRIX_URLHAUS_REFRESH21600sFeed refresh interval.
MAILSTRIX_MBAZAAR_KEYnoneMalwareBazaar key; enables attachment SHA-256 lookups.
MAILSTRIX_MBAZAAR_FEEDnoneMalwareBazaar feed source/path.
MAILSTRIX_MBAZAAR_REFRESH86400sFeed refresh interval.
MAILSTRIX_THREATFOX_KEYnoneThreatFox key; IOC URL lookups.
MAILSTRIX_THREATFOX_MAX_URLS64Max URLs checked per message.
MAILSTRIX_THREATFOX_REFRESH21600sFeed refresh interval.
MAILSTRIX_FEODOoffEnable the Feodo Tracker botnet-C2 feed.
MAILSTRIX_FEODO_REFRESH21600sFeed refresh interval.

Logging

VariableDefaultMeaning
MAILSTRIX_VERBOSEoffPer-request logging.
MAILSTRIX_LOG_STDOUToffInfo/access logs to stdout; errors stay on stderr.

The strix-scan client reads MAILSTRIX_URL and MAILSTRIX_TOKEN from the environment too.

HTTP API

The serve backend exposes five routes. Only /scan (and optionally /metrics) require the token; the health probes are always open so orchestrators can probe without a secret.

MethodPathAuthPurpose
POST/scantokenScan the raw request body; returns the JSON verdict.
GET/metricsoptionalPrometheus exposition — scans, matches, errors, cache hits, feed stats.
GET/healthopenLiveness — the process is up.
GET/readyopenReadiness — rules loaded (warns, not fails, on a stale bundle).
GET/versionopenBuild, libyara and loaded-bundle identity.
# scan a file, token in a Bearer header
curl -sS -X POST http://127.0.0.1:8079/scan \
  -H "Authorization: Bearer $MAILSTRIX_TOKEN" \
  -H "X-MAILSTRIX-Filename: invoice.docm" \
  --data-binary @invoice.docm

Request headers

HeaderPurpose
Authorization: Bearer <token>Shared secret. The X-MAILSTRIX-Token header is accepted as an equivalent.
X-MAILSTRIX-TokenAlternative way to pass the shared secret.
X-MAILSTRIX-FilenameAttachment/message name, mapped to the YARA filename/extension externals so name-keyed THOR/Loki rules fire.
X-MAILSTRIX-EffortPer-request extraction depth, clamped to [1, EFFORT_MAX]. Overrides MAILSTRIX_EFFORT=auto.
X-MAILSTRIX-CachePer-request cache control (e.g. bypass the verdict cache for a re-scan).

JSON verdict

A 200 with a matches array. The array is empty, never null, on a clean message, so a caller can branch on length alone. Each match names the rule and the source ruleset namespace, plus any tags and metadata.

{
  "matches": [
    {
      "rule": "SUSP_VBA_Shell_Exec",
      "namespace": "sigbase-gen_office.yar",
      "tags": ["macro", "exec"],
      "meta": { "author": "Florian Roth", "score": "75" }
    }
  ]
}
FieldMeaning
ruleThe YARA rule name that fired.
namespaceSource ruleset file (which set fired, not just the rule). Empty for synthetic matches such as URLhaus.
tagsRule tags (omitted when none).
metaRule metadata key/value map (omitted when none).

A scan error, timeout or libyara panic returns an empty matches array — Mailstrix fails open by design, so the scanner can never become a mail-delivery outage. The error is still counted in /metrics.

Rules & bundles

Mailstrix doesn't author rules — it packages roughly 10,000 public rules from eight curated upstreams, precompiled to a .yac bundle and rebuilt daily. The full list and per-set licenses are on the front page FAQ.

  • Each rule file compiles into its own libyara namespace named after the file, so the verdict's namespace tells you which ruleset fired.
  • A writable CACHE_DIR holds the live bundle and self-heals from SEED_RULES on a fresh deploy; fetch-rules pulls the daily rules-current release into it.
  • Any upstream can be pinned or disabled at build time with a per-source build arg (e.g. DIDIER=0).
  • Allowlist/denylist narrow the active set without recompiling; the denylist reloads on SIGHUP with no downtime.

Deploy quickstarts

Docker

docker run -d --name mailstrix \
  -p 127.0.0.1:8079:8079 \
  -e MAILSTRIX_TOKEN_FILE=/run/secrets/mailstrix \
  eilandert/mailstrix:latest

strix-scan from a Sieve/LDA pipe

strix-scan -url http://127.0.0.1:8079 \
  -token-file /etc/mailstrix/token \
  -filename "$ATTACHMENT_NAME" message.eml
# exit 0 = clean, non-zero = match

The five integration paths (rspamd, SpamAssassin, Sieve, ICAP, standalone) each have a ready-made config under contrib/; a Debian package, a Helm chart and full deployment docs live in the GitHub repository.