Skip to content

Configuration🔗

rsigma engine daemon, rsigma engine eval, rsigma rule backtest, and rsigma rule coverage can be driven by a YAML config file in addition to CLI flags and environment variables. This page describes the schema, the discovery chain, and the precedence model that decides which value wins when more than one layer sets the same key.

The same machinery is exposed through the rsigma config group for scaffolding, validation, introspection, and reload.

Precedence🔗

From highest to lowest:

  1. CLI flags on the command line (e.g. --rules /tmp/r).
  2. Environment variables: uniform RSIGMA_<SECTION>__<KEY> (see Environment layer) plus the legacy clap-bound names listed in Environment Variables.
  3. Project config: ./rsigma.yaml (or ./rsigma.yml) in the current directory, then the nearest .rsigmarc found by walking up from the current directory.
  4. User config: $XDG_CONFIG_HOME/rsigma/config.yaml (defaulting to ~/.config/rsigma/config.yaml).
  5. System config: /etc/rsigma/config.yaml.
  6. Compiled defaults baked into the binary.

Higher layers override lower ones per leaf, not whole sections; a project .rsigmarc that only sets eval.rules does not erase the rest of the user config. This matches the clig.dev guidance and is what most modern CLIs implement.

Use rsigma config show to see the resolved value for every key with the layer that supplied it.

Discovery🔗

All layers that exist are loaded and merged. There is no first-match-wins fallback: a file at /etc/rsigma/config.yaml is always loaded if it exists, even when a project rsigma.yaml is also present.

Passing --config <PATH> replaces the discovery chain entirely: only that file is loaded (and a bad path is a hard error, so misspellings surface immediately).

The ~/.config/rsigma location is computed by honouring XDG_CONFIG_HOME explicitly rather than dirs::config_dir(), so on macOS the path stays under ~/.config/rsigma instead of ~/Library/Application Support/. The rsigma install workflow uses the same layout.

Format🔗

A minimal example, with every supported top-level section:

# yaml-language-server: $schema=https://timescale.github.io/rsigma/rsigma.schema.json
version: 1

global:
  log_format: text         # text | json (maps to --log-format)
  output_format: json      # json | ndjson | table | csv | tsv (maps to --output-format)
  color: auto              # auto | always | never (maps to --color)

daemon:
  rules: /etc/rsigma/rules
  pipelines: [ecs_windows]
  sources: [/etc/rsigma/sources]
  api:
    addr: "0.0.0.0:9090"
  input:
    source: stdin
    format: auto
    buffer_size: 10000
  output:
    sinks: [stdout]
    drain_timeout: 5
    # webhooks: [/etc/rsigma/webhooks/]   # template-driven HTTP sinks
  correlation:
    action: alert
    event_mode: none
    max_events: 10
    max_state_entries: 100000   # hard cap across all correlations and groups
    # max_group_entries: 10000  # per-group window-state cap; unset = unbounded
  state:
    save_interval: 30
  engine:
    bloom_prefilter: false
    match_detail: off
    observe_fields: false
    egress_policy: default
  tap:
    enabled: false          # opt-in: enable GET /api/v1/tap (or pass --enable-tap)
    buffer_events: 8192     # per-session buffer; a full buffer drops events (counted)
    max_sessions: 2         # concurrent capture sessions (a session over the cap gets 409)
    max_duration: 5m        # largest accepted ?duration (a longer one gets 400)
  tail:
    enabled: false          # opt-in: enable GET /api/v1/detections/stream (or --enable-tail)
    buffer_events: 8192     # per-session buffer; a full buffer drops detections (counted)
    max_sessions: 2         # concurrent tail sessions (a session over the cap gets 409)

eval:
  rules: ./rules
  pipelines: [sysmon]
  input_format: auto
  fail_on_detection: false

backtest:
  rules: ./rules
  corpus: [./ci/corpus]
  expectations: ./ci/expectations.yml
  # unexpected: warn   # fail | warn | ignore; unset lets the expectations-file default apply
  input_format: auto

coverage:
  # atomics: https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/Indexes/index.yaml
  # baseline: https://raw.githubusercontent.com/SigmaHQ/sigma/master/other/sigma_attack_nav_coverage.json
  # targets: ./threat-model-techniques.txt
  fail_on_gaps: false

Run rsigma config init to scaffold a full, commented version. The full machine-readable schema is emitted by rsigma config schema.

Sections🔗

Section Used by Notes
global every subcommand global.log_format, global.output_format, and global.color. See Output Formats for the format/color semantics.
daemon engine daemon Mirrors every non-secret daemon flag.
daemon.api.tls engine daemon Inert unless the binary is built with the daemon-tls feature; otherwise config validate warns.
daemon.nats engine daemon Non-secret NATS knobs (e.g. consumer_group). Secrets stay env-only. Inert unless built with daemon-nats.
daemon.tap engine daemon Live event-tap limits (enabled, buffer_events, max_sessions, max_duration). Disabled by default; enable with enabled: true or --enable-tap. The rest are config-file-only. See HTTP API: Live event tap.
daemon.tail engine daemon Live detection-tail limits (enabled, buffer_events, max_sessions). Disabled by default; enable with enabled: true or --enable-tail. The rest are config-file-only. See HTTP API: Live detection tail.
daemon.engine.cross_rule_ac engine daemon Inert unless built with daachorse-index.
eval engine eval Mirrors the eval flag surface.
backtest rule backtest rules, corpus, expectations, unexpected, pipelines, and the syslog input knobs. unexpected has no compiled default so the expectations-file default can apply.
coverage rule coverage atomics, baseline, targets, fail_on_gaps. rules is intentionally absent (it is a required, invocation-specific CLI argument).
mcp mcp serve mcp.http_addr (the --http bind address; unset means stdio), mcp.lint_config, and mcp.rules_dir. The auth token is secret and stays flag/env-only. Inert unless built with the mcp feature.

Secrets policy🔗

The schema deliberately does not carry any secret-bearing daemon settings:

  • NATS auth (creds, token, user, password, nkey)
  • TLS key password

Supply these via environment variables (or --flag for ad-hoc use). Putting them in a checked-in YAML file would silently widen exposure, so the loader has no way to accept them.

Environment layer🔗

Two parallel schemes are honoured:

  1. Uniform RSIGMA_<SECTION>__<KEY> (recommended). Nested keys use the __ separator; single underscores stay inside a key. Values are parsed as YAML scalars so types coerce naturally (ints, bools, lists). Examples:

    Env var Equivalent config
    RSIGMA_DAEMON__API__ADDR=127.0.0.1:9090 daemon.api.addr
    RSIGMA_DAEMON__INPUT__BUFFER_SIZE=20000 daemon.input.buffer_size
    RSIGMA_GLOBAL__LOG_FORMAT=json global.log_format
    RSIGMA_MCP__HTTP_ADDR=127.0.0.1:9100 mcp.http_addr
  2. Legacy clap-bound names with a single underscore (NATS_CREDS, RSIGMA_CONSUMER_GROUP, RSIGMA_TLS_KEY_PASSWORD). These continue to work at the flag layer and are listed in Environment Variables. Secrets are only readable this way.

The uniform scheme is detected by the __ separator, so it never collides with the legacy single-underscore names.

--dry-run and config show🔗

config show folds default + file + env and reports the winning layer for each leaf. To preview what a real command will use, including its flag layer, the daemon, eval, backtest, and coverage commands support --dry-run:

rsigma engine daemon --dry-run
# prints the effective daemon section as YAML, then exits 0

In config show output the layer is one of default, file, or env. In a command's --dry-run view, flag-supplied values appear on top of all three.

Format versioning🔗

Every config carries version: 1 at the top level. The loader currently accepts any value (the field is informational), but future migrations will gate on it; treat it as required when authoring new files.

See also🔗