CLI Reference
Complete reference for all Tracemill CLI commands, flags, and environment variables.
Applies to: CLI. This page documents the command-line tool. Cloud runs are launched from Tracemill Cloud — see Cloud.
Commands
tracemill run
Run a scenario or job and stream generated events.
tracemill run <content-id-or-path>The run command accepts either a file path or a content ID (see Content ID Resolution below). It inspects the type: field in the YAML to determine whether the file is a scenario or a job.
Use the scenario or job subcommand to explicitly validate the content type:
tracemill run scenario <content-id-or-path>
tracemill run job <content-id-or-path>Flags:
| Flag | Type | Default | Description |
|---|---|---|---|
--dry-run | bool | false | Simulate execution without sending events; prints a summary instead |
--no-stats | bool | false | Disable the live statistics overlay |
--set | stringArray | Override a job state value (repeatable, key=value); only valid for jobs | |
--target-type | string | Target SIEM type for format adjustments (e.g. splunk, sentinel); auto-inferred from HEC when not set | |
--json-separators | string | compact | JSON separators for stdout, HEC, TCP and HTTP: compact or spaced (the Splunk add-on collector layout). Rejected with --s3-bucket |
Destination flags (mutually exclusive — specify at most one group):
| Flag | Type | Description |
|---|---|---|
--hec-url | string | Splunk HEC endpoint URL |
--hec-token | string | Splunk HEC authentication token (required with --hec-url) |
--hec-index | string | Splunk index to route events to |
--hec-source | string | Splunk source field to set on events |
--hec-sourcetype | string | Splunk sourcetype field to set on events |
--hec-ack | bool | Enable HEC indexer acknowledgment |
--hec-ack-poll-interval | duration | Interval between ack poll requests (default 2s) |
--hec-ack-timeout | duration | Max time to wait for ack confirmation (default 30s) |
--hec-insecure | bool | Skip TLS certificate verification for the HEC endpoint (use only for self-signed dev certs) |
--hec-event | string | How HEC embeds a JSON event: object (default) or string (Splunk indexes the rendered bytes as _raw verbatim) |
--s3-bucket | string | S3 bucket name |
--s3-region | string | AWS region for the S3 bucket |
--s3-prefix | string | S3 object key prefix |
--s3-format | string | S3 output format: jsonl (default) or cloudtrail |
--s3-org-id | string | AWS Organization ID for CloudTrail org-trail path layout |
--tcp-host | string | TCP destination host |
--tcp-port | int | TCP destination port (required with --tcp-host) |
--http-url | string | HTTP destination URL |
--http-method | string | HTTP method: POST (default) or PUT |
--http-insecure | bool | Skip TLS certificate verification for the HTTP endpoint (use only for self-signed dev certs) |
When no destination flags are set, events are written to stdout as JSONL (the default).
Event types are auto-discovered from event-types/ directories in the content hierarchy (project > user > library). An emit step referencing an unknown event type is a hard error.
--dry-run uses a manual clock and summary sink — no events are emitted. The summary includes event counts, type breakdown, and estimated duration.
--no-stats suppresses the live statistics overlay. The overlay is shown on stderr when using a remote destination (HEC, S3, TCP, HTTP), or when stdout is redirected to a file/pipe. It is never shown when events go to stdout in a terminal (mixing overlay with event output would be unreadable).
--target-type applies target-specific formatting adjustments. For example, Splunk's Universal Forwarder abbreviates the <System> XML element to <s> in WinEventLog events — --target-type splunk applies this alias so generated events match what Splunk indexes. When --hec-url is set and --target-type is not explicitly provided, the target type is auto-inferred as splunk. Pass --target-type "" to disable inference.
--json-separators and --hec-event set the JSON layout the destination writes. The default is compact JSON embedded as an object; use spaced when the data you are testing is indexed through a Splunk add-on collector, such as the Microsoft 365 add-on. See JSON layout.
--set key=value overrides a value in the job's state block at invocation time, and is repeatable. Each value is YAML-decoded so types match the YAML state block (8080 → int, true → bool, [a,b] → list, {k: v} → map), except that a key whose declared value is a quoted string keeps the text as written: --set account_id=000000000000 stays the twelve-digit string. A literal override must have the same type as the key's declared literal (an int satisfies a declared float); a mismatch is rejected before any workload runs. String values that look like ExprStr (gen.*, ref.*, pool.*) are preserved as strings and evaluated by the engine. String leaves nested inside an override map or list value follow the same ExprStr rules (matching declared job state), so --set actions='[{event_id: gen.uuid()}]' evaluates the nested gen.uuid() at job start. The override key must already be declared in the job's state block — unknown keys are rejected before any workload runs, as are duplicate keys. --set is only valid for jobs; it is rejected on scenario runs (scenario state is not externally addressable — use a job to parameterise).
Examples:
# Send events to Splunk via HEC (auto-infers --target-type splunk)
tracemill run job.yaml --hec-url https://splunk:8088 --hec-token my-token
# Send O365 events in the Splunk add-on layout
tracemill run jobs/splunk/o365/exchange/o365-email-new-inbox-rule-created \
--hec-url https://splunk:8088 --hec-token my-token --json-separators spaced
# Send events to a generic HTTP endpoint with explicit Splunk formatting
tracemill run job.yaml --http-url https://proxy:9090 --target-type splunk
# Send events to S3 in CloudTrail format
tracemill run job.yaml --s3-bucket ct-ingest --s3-region us-east-1 --s3-format cloudtrail
# Override job state values at invocation
tracemill run jobs/aws/detection-job --set region=us-west-2 --set port=9090
# Default: stdout
tracemill run job.yamltracemill validate
Parse and validate YAML content files without delivering any events. Checks for schema errors, undefined references, and unreachable pool configurations.
# Validate a single file
tracemill validate --scenario <path>
tracemill validate --job <path>
tracemill validate --event-type <path>
# Validate all content in a directory recursively
tracemill validate --dir <path>Exactly one flag is required; they are mutually exclusive.
Rendering never delivers events. It needs the event-type catalog (--library, TRACEMILL_LIBRARY, or the installed library); without one, only the files themselves are checked and a warning is printed.
Flags:
| Flag | Description |
|---|---|
--scenario <path> | Validate a scenario YAML file, then render it once and check every event against its event type's payload schema. Prints "OK" on success. |
--job <path> | Validate a job YAML file, including all referenced pools and scenarios, then render the job once (bindings, state, and pools included) and check every event against its payload schema. Prints "OK" on success. |
--event-type <path> | Validate an event-type YAML file. Prints "OK" on success. |
--dir <path> | Recursively validate all content files in a directory. Dispatches each YAML file by its type: field (scenario, job, event-type, pool) and renders every scenario and job as --scenario and --job do, in one process. Non-YAML files and files with unknown types are skipped. Prints per-file results and a summary. |
--dir is useful for validating an entire content library in one command:
tracemill validate --dir ./scenarios/
tracemill validate --dir ./event-types/tracemill list
List available resources.
list generators
List all built-in generators.
tracemill list generators
tracemill list generators --jsonlist event-types
List all event types discovered from the content hierarchy.
tracemill list event-types
tracemill list event-types --jsonEvent types are auto-discovered from event-types/ directories in each content layer (project > user > library). The output includes a SOURCE column showing which layer provided each type.
list pools
List all pools defined in a job file.
tracemill list pools --job <path>
tracemill list pools --job <path> --jsonThe --job flag is required.
All list subcommands accept --json to emit JSON instead of a table.
tracemill schema
Inspect the JSON schemas embedded in the binary.
schema list
Print the names of all available schemas.
tracemill schema listschema show
Print a schema to stdout.
tracemill schema show <name>
tracemill schema show --alltracemill author
Deterministic tools for authoring scenarios from sample telemetry: summarize a dataset, extract master samples, compare a rendered event against a master, and reduce a detection's SPL to its group-by fields. They work offline, except for dataset URLs and live SPL parsing.
Exit codes (all author subcommands):
| Code | Meaning |
|---|---|
0 | Success. A fidelity fail or warn verdict still exits 0 — read the verdict from the report. |
1 | Runtime failure: a missing input, a failed download, a dataset over a bound, no event matching --value, no group-by tuple, or a Splunk error |
2 | Invalid invocation: unknown subcommand, missing or invalid flags, wrong number of arguments |
author extract
Group the events of a dataset by a key.
tracemill author extract <dataset> --key <key> --mode summary|samples|extract [flags]<dataset> is a local path or an http(s) URL. Keys are dotted paths for JSON and NDJSON (a literal dotted key wins), slash paths for XML (System/EventID), and field names for key-value data. Text datasets have the single category all.
| Mode | Writes | Prints |
|---|---|---|
summary | nothing | {format, total, key, categories: [{key, count}]} |
samples | the first event of each category to --out DIR, one file per category | [{key, count, sample_path}] |
extract | the first event whose key equals --value to --out FILE | {sample_path}; exits 1 when no event matches |
Flags:
| Flag | Type | Default | Description |
|---|---|---|---|
--key | string | Field to group by (required). For a timestamp-led multi-line kv dataset (Windows classic event log text, admon), which is grouped by record, a body path such as Message.Process Information.New Process Name also works | |
--mode | string | summary, samples, or extract (required) | |
--format | string | auto | Dataset format: auto, xml, json, ndjson, kv, or text |
--value | string | Key value to extract (required in extract mode) | |
--out | string | Output directory (samples) or file (extract); required in both modes | |
--max-bytes | int | 2147483648 (2 GiB) | Maximum dataset size; the dataset is held in memory |
--max-events | int | 5000000 | Maximum number of events in the dataset |
--max-categories | int | 10000 | Maximum number of distinct categories (summary and samples; extract stops at the first match and does not count categories) |
--cache-dir | string | Directory for downloaded datasets |
--max-bytes is checked before the dataset is read: against the file size for a local path and the Content-Length header for a URL, and again while reading. A dataset over any bound fails with exit code 1.
A URL dataset is downloaded to <cache-dir>/<sha256><ext> and its path is printed to stderr. The default directory is $SCENARIO_AUTHORING_CACHE/attack_data when that variable is set, and .cache/scenario-authoring/attack_data (relative to the working directory) otherwise.
author fidelity
Compare a generated event (or burst) against a master sample.
tracemill author fidelity --master <file> --generated <file> [flags]Flags:
| Flag | Type | Default | Description |
|---|---|---|---|
--master | string | Master sample file (required) | |
--generated | string | Generated render file (required) | |
--load-bearing | string | Comma-separated load-bearing field tokens: exact paths, globs (field~a*|b*), or cardinality checks (dc(field)==1) | |
--format | string | auto | Input format: auto, xml, json, or kv. A kv master compares against a kv render only; a kv master against an XML or JSON render, or any text input, has no verdict and exits 1 |
--output | string | json | json prints the fidelity report; diff prints a canonicalized structural diff of the first generated event against the master |
The JSON report includes field coverage, missing and extra fields, value differences, load-bearing results, and a verdict of pass, warn, or fail. The verdict is data, not an error: the command exits 0 for every verdict.
author spl-groupby
Print the final group-by tuple of a detection's SPL.
tracemill author spl-groupby --parser-json <file|->
tracemill author spl-groupby '<spl>'Pass exactly one of:
--parser-json— a saved Splunk/services/search/parserJSON response, from a file or-for stdin (at most 16 MiB).<spl>— the SPL itself, parsed live through Splunk's/services/search/parserendpoint. As in the Splunk search bar, SPL that does not start withsearchor|(a generating command such as| tstats) is sent with an implicitsearchprefix.
Live mode reads its connection from the environment:
| Variable | Description |
|---|---|
SPLUNK_URL | Splunk management URL, e.g. https://splunk.example.com:8089 (required) |
SPLUNK_AUTH | user:password |
SPLUNK_PASSWORD | Used as admin:$SPLUNK_PASSWORD when SPLUNK_AUTH is unset |
SPLUNK_INSECURE | Set to 1 to skip TLS certificate verification. Use only for a self-signed development instance. |
Flags:
| Flag | Type | Default | Description |
|---|---|---|---|
--parser-json | string | Saved parser response file, or - for stdin | |
--output | string | text | text prints one field per line and writes warnings to stderr as warning: <text>; json prints {"fields": [...], "warnings": [...]} |
A count-only search or one without an aggregation has no group-by tuple and exits 1.
Examples:
# Summarize a dataset by event name
tracemill author extract dataset.json --key eventName --mode summary
# Write one sample per category
tracemill author extract dataset.json --key eventName --mode samples --out .cache/samples
# Download a dataset and extract the first EventCode 4625 event as a master
tracemill author extract https://example.com/data.log --key EventCode --mode extract --value 4625 --out master.log
# Fidelity report with load-bearing fields
tracemill author fidelity --master master.json --generated render.json --load-bearing 'eventName,dc(userIdentity.userName)==1'
# Human-readable structural diff
tracemill author fidelity --master master.xml --generated render.xml --output diff
# Group-by fields from a saved parser response
tracemill author spl-groupby --parser-json parser.json
# Group-by fields parsed live by Splunk
SPLUNK_URL=https://splunk:8089 SPLUNK_AUTH='admin:YOUR_PASSWORD' tracemill author spl-groupby 'index=main | stats count by user'tracemill version
Print version, commit hash, build date, and Go version.
tracemill versionGlobal Flags
These flags are available on all commands:
| Flag | Type | Default | Env Variable | Description |
|---|---|---|---|---|
--verbose | bool | false | TRACEMILL_VERBOSE | Enable debug logging |
--library <path> | string | ~/.tracemill/library | TRACEMILL_LIBRARY | Override the library content directory |
Content ID Resolution
The run command resolves its argument using a three-layer content search. This lets you reference scenarios and jobs by short identifiers instead of full file paths.
Search layers (checked in priority order):
| Priority | Layer | Location |
|---|---|---|
| 1 | Project | Current working directory |
| 2 | User | ~/.tracemill/content/ |
| 3 | Library | ~/.tracemill/library/ (or TRACEMILL_LIBRARY) |
The first layer containing <content-id>.yaml wins. Project-local content shadows library content with the same ID, so you can pin or override a scenario without modifying the shared library.
Examples:
# Content ID — searched across project → user → library
tracemill run aws/scenarios/cloudtrail/brute-force
# File path — used directly, no search
tracemill run ./examples/aws-brute-force.yaml
tracemill run /absolute/path/scenario.yaml
tracemill run ~/scenarios/test.yamlFile path detection: arguments starting with ./, ../, /, or ~ are treated as literal file paths and bypass the content resolver. Arguments ending in .yaml or .yml are also treated as file paths, unless they contain . or .. path segments (e.g. aws/../../etc/passwd.yaml), which are rejected as invalid content IDs instead.
Environment Variables
| Variable | Flag | Description |
|---|---|---|
TRACEMILL_VERBOSE | --verbose | Enable debug logging |
TRACEMILL_LIBRARY | --library | Override the default library directory |
Environment variables use the TRACEMILL_ prefix. They can be set instead of passing flags:
export TRACEMILL_LIBRARY=/path/to/custom/library
tracemill run my-scenario