Skip to content

Settings Reference

Open Settings with the ⚙ Settings button in the toolbar.


Essential vs All

Settings opens on Essential, which shows only the controls a feature is dead without — the AI models, enrichment and exposure API keys, and integration URLs and credentials — across six tabs. All shows everything: 16 tabs and every tuning knob. The choice is remembered per browser.

The rule, for anyone adding a field:

A control is Essential if the feature behind it does nothing until you type something in. Anything with a working default lives under All.

So credentials and endpoint URLs are Essential; timeouts, retry counts, throttle delays, output caps, CA-bundle paths (_CA) and prompt-file overrides are not. Tabs that manage content rather than configuration — IOC Whitelist, NSRL, Importers, KEV, Report Templates, Dashboard Views — sit under All too: they are empty and working out of the box.

Skip TLS verify (DFIR_<NAME>_INSECURE) is the one deliberate exception, on every integration that has it — MISP, YETI, OpenCTI, DFIR-IRIS, Timesketch, Notion, ClickUp and the notification webhooks — together with the global Allow insecure TLS to external hosts (DFIR_TLS_ALLOW_INSECURE_EXTERNAL) a non-loopback host additionally needs. A self-hosted instance behind a self-signed certificate is dead without one of them, and the error it fails with names no setting at all, so burying the flag under All left the connect flow failing with no visible knob to reach for. Prefer a _CA bundle where you have one: it keeps verification on.

Jira and ServiceNow have the same flag and stay out of Essential — their whole block is read-only in the dashboard by design, a security boundary edited in .env and applied by a restart.

Tools is All-only in full. Every external binary it wires up (Hayabusa, the Velociraptor CLI, Suricata, Snort, YARA) is blank-means-off, so nothing is broken by leaving them unconfigured — setting one up is a deliberate trip to All rather than something a new install must face.

In the markup an Essential control carries a data-essential attribute, so a newly added field stays out of Essential until someone opts it in. companion/tests/settings/settingsEssentialAll.test.ts pins the full Essential set; adding to it means editing that list.

Hiding a field never changes what is saved. Save posts only the keys whose values you actually changed, so a field you cannot see cannot blank a .env key, and switching modes mid-edit keeps whatever you have typed.

A value is saved exactly as you typed it, including #, quotes and leading or trailing spaces. Save puts quotes around the value in .env only when the value needs them. Test / reconnect then reads the value the same way the server reads it after a restart. Save refuses a value it cannot store without changing it: a value that contains both a single quote and a double quote, together with a #, a leading or trailing space, or a quote as its first character. The error names the field; change the value and save again. In versions before this fix, a value containing # was cut at the # after a restart. Test still passed. Enter any such secret again and save it.


The box beside the Settings title filters every field as you type. Tokens are ANDed and matched as substrings, against a field's label, its hint, and its env key: max events, DFIR_MAX_EVENTS and dfir-max-events all find the same control, so a key copied out of .env or the docs works as-is. Select options are searchable too — ollama finds the AI provider dropdown.

Search always spans All, whichever mode you are in. You search precisely because you cannot find something, so a search confined to Essential would report "no results" for a field that exists. Tabs Essential hides — Velociraptor, Tools, Dashboard Views and the rest — appear in the tab bar while a search is active, each badged with the number of fields it will show. Typing a tab's own name (kev, whitelist, nsrl) matches the tab and shows its pane unfiltered, which is how the tabs that manage content rather than configuration stay reachable.

The Essential / All toggle steps aside while a search is active, because search has suspended the Essential filter and the toggle would have no visible effect. Clear the box — empty it, use the ⨯, or press Escape in it — and the toggle returns with your choice unchanged. Searching never rewrites the remembered Essential/All preference, and closing Settings clears the box.


General

  • Case root location
  • Server port
  • Log level (debug / info / warn / error) — live toggle, no restart. DFIR_LOG_LEVEL (default info) sets the level at startup.
  • Open setup wizard link
  • Push ingest token management
  • Update check (opt-in dashboard banner for new GitHub releases)
  • Theme (dark / light)
  • Import severity — manage or clear the remembered minimum-severity import floor (see Importing Evidence)
  • Timeline row display — choose which sub-elements show on each forensic-timeline row (see Dashboard → Forensic Timeline)
  • Screenshot OCR search — enable/disable local Tesseract OCR indexing of captures
  • Evidence drop folder — enable/disable the per-case auto-import watcher, poll interval, and per-file size cap (see Importing Evidence)
  • Vim-style timeline navigation — toggle j/k/f/i/p/n/? keyboard shortcuts on the Forensic Timeline, default on (see Dashboard → Forensic Timeline)
  • DFIR_IMPORT_BULK_MIN_MB / DFIR_IMPORT_BATCH_ROWS (env vars) — the batched Velociraptor import. An export at or above DFIR_IMPORT_BULK_MIN_MB MB (default 8; 0 = always) is imported one batch of DFIR_IMPORT_BATCH_ROWS rows (default 5000) at a time — tagged, gated and written per batch, with one log line per batch — instead of expanded whole in memory. Under the threshold the import runs as before. See Importing → Large Velociraptor exports.
  • DFIR_MAX_IMPORT_FILE_MB (env var) — the whole-file ceiling for Import-from-path, default 256. A non-Plaso file over it is refused with a 413 that names this knob, before the file is read; Plaso super-timelines stream and are exempt. The drop folder has its own cap, DFIR_DROP_MAX_BYTES (default 200 MB), and since #921 that cap also bounds the SO-CRATES upload, which buffers the whole file.
  • DFIR_MAX_EVENTS (env var) — the per-import event ingestion cap, default 2000. Raise it for cases that need a full MFT/USN import; guarded against 0/negative/NaN silently reinstating the default.
  • DFIR_IMPORT_MEMORY_GUARD — on by default. An import's memory grows with the size of the whole case, so on a very large case one import can exhaust the machine and the server is killed. The guard refuses such an import first, with a message naming the case size and the memory it needs. The file stays saved in the case and nothing is changed; free memory or give the server more, then import it again. A Velociraptor hunt collect and an external hunt or flow import are guarded the same way: the rows are saved as case evidence first, and a refused hunt shows the message on its card — press Collect now to retry. Set it to off only when you know the machine has the memory.
  • Extra lab-setup folders (DFIR_LAB_SETUP_PATHS) — comma-separated folder fragments, added to the built-in VMware (\VMwareDnD\) and VirtualBox (\VirtualBox Dropped Files\) drag-and-drop folders. The built-in folders match only in the shape the hypervisor writes (\Temp\vmware-<user>\VMwareDnD\ and \Temp\VirtualBox Dropped Files\), so a bare folder of that name elsewhere is not capped. The match is case-insensitive and can be anywhere in a row's file path. A file copied into one of these folders is lab setup: the row is capped at Medium. Running the file is not capped. The change applies on the next import. Remove a folder and the next full re-scan gives its rows their grade back. See Advanced → Finding Severity Caps.
  • DFIR_ALLOWED_ORIGINS (env var) — comma-separated CORS allowlist of extra trusted browser origins beyond loopback, the extension, and any origin the companion itself served; every other origin gets a 403. Only needed when the dashboard is reached through a hostname (reverse proxy, hosted deployment) — localhost/LAN/Docker setups need no configuration.
  • DFIR_ALLOWED_HOSTS / DFIR_ALLOWED_HOST_SUFFIXES (env vars) — comma-separated hostnames (or domain suffixes such as .lab.example.com) that this companion answers to. Loopback and bare IP addresses are always accepted, so localhost, Docker, and LAN access via http://192.168.1.50:4773 need no configuration. An unrecognised name gets a 403 before any route runs: that is the DNS-rebinding defence, which stops a website you merely visit from pointing its own domain at your machine and reading your case data. Suffixes match on a label boundary, so .acme.com never matches evilacme.com.

Authentication

Authentication settings are deployment security controls, so they are intentionally not editable from the dashboard. Set them in .env or the deployment's secret store and restart.

  • DFIR_AUTH_MODE — single-user (default) or team; stop the server, change this value, and restart to switch modes. Team identities and roles remain stored while single-user mode is active.
  • DFIR_AUTH_COOKIE_SECURE — secure cookies; defaults on in team mode
  • DFIR_AUTH_SESSION_HOURS — browser-session lifetime, default 12 hours
  • DFIR_AUTH_BOOTSTRAP_TOKEN — protects first-administrator setup; required in team mode while no identity exists, on any bind address. At least 32 characters, or the server refuses to start. Wrong guesses are rate-limited and audited.
  • DFIR_AUTH_DATA_DIR — optional identity/session database location
  • DFIR_HTTPS — built-in HTTPS: auto (default; on in team mode), on or off. See Running over HTTPS. The Built-in HTTPS group shows the certificate fingerprints and expiry and links the local CA download.
  • DFIR_HTTPS_CERT / DFIR_HTTPS_KEY — your own PEM certificate (chain allowed) and unencrypted key; unset uses the generated local CA
  • DFIR_HTTPS_HOSTNAMES — extra names/IPs the generated certificate covers (comma-separated)
  • DFIR_HTTPS_PLAIN_HTTP — optional second plain-HTTP port: off (default), redirect or serve; DFIR_HTTPS_PLAIN_HTTP_PORT (default 4774) and DFIR_HTTPS_PLAIN_HTTP_HOST (where serve binds; default 127.0.0.1)
  • DFIR_ALLOW_UNAUTHENTICATED_REMOTE — lets single-user mode bind a non-loopback address; container-loopback-proxy (Docker port published to host loopback only) or public-demo (really exposed on purpose) only. See Public Demo and Network Exposure.
  • DFIR_AUTH_OIDC_ISSUER, _CLIENT_ID, _CLIENT_SECRET, _REDIRECT_URI, _SCOPES — optional organization sign-in

See Team Accounts and Case Roles for setup, roles, service identities, emergency access, and the one-writer deployment model.


AI

  • Provider, model, API key, base URL (extraction)
  • Text model — synthesis and imports (optional separate model for synthesis, Deep Pass and CSV/log imports; blank uses the screenshot model) — also configurable directly in the first-run setup wizard's AI step, not just here. The Screenshot model (vision) above it is used for screenshots only. The two are labelled so one is not read as the other.
  • VQL-generation model (optional dedicated model — many general models struggle with VQL syntax)
  • Second-opinion model (optional rival model for the 2nd opinion button) and the referee that judges each A-vs-B disagreement (blank = model A)
  • Timeout, max tokens, context window size
  • Chain-of-Thought (synthesis thinking tokens)
  • Anonymisation on/off and per-category toggles — IPs (internal and public), hostnames, usernames, domains, emails, paths, encoded commands, SIDs, credit cards, phone numbers, national ID numbers; see AI Analysis → What the AI Sees for exactly what each one catches, the redacted-export exception for public IPs, and known limitations (the narrow IPv4/IPv6 masking gaps, screenshot IP loss, national-ID false positives)
  • Presidio (optional external PII detector) — analyzer URL, confidence floor, and a Test connection button; see Presidio & PII Masking
  • Preflight diagnostics disable
  • Re-run the setup wizard
  • Live AI test — confirms the current key works right now

Provider keys and base URLs — DFIR_AI_KEY_<PROVIDER> and DFIR_AI_BASE_URL_<PROVIDER> (for OPENAI, OPENROUTER, GEMINI, ANTHROPIC, OLLAMA, LITELLM) save one key and one base URL per provider. Every model uses the settings of the provider it is set to, so you can switch a model to another provider without typing them again. Order for each model: its own _KEY / _BASE_URL field, then the provider setting, then DFIR_VISION_KEY / DFIR_VISION_BASE_URL. Leave a model's own fields blank to use the provider settings. The per-model key and base-URL fields are optional overrides and show only in the full (All) view. When saved per-model values can move into the provider fields without changing what any model uses, Settings → AI shows a notice with a Move them button. Values that differ between models on the same provider stay as overrides.

Screenshot/vision provider — DFIR_VISION_PROVIDER/_MODEL/_KEY/_BASE_URL/_IMAGE_DETAIL configure the screenshot-OCR-only model, renamed from DFIR_AI_* (legacy names still work as a fallback). Text-only AI features run off the synthesis provider and never need a vision provider configured.

Second opinion — DFIR_AI_SECOND_OPINION_MODEL/_PROVIDER/_KEY/_BASE_URL configure model B, the rival model behind the 2nd opinion button. Setting _MODEL turns the feature on; the other three fall back to the vision provider settings when blank.

Referee for 2nd-opinion verdicts — DFIR_AI_RECONCILE_MODEL/_PROVIDER/_KEY/_BASE_URL choose the model that writes the referee suggests line on each disagreement. Leave _MODEL blank and model A (the synthesis model) referees. Set it to same-as-b and model B referees its own findings. Set it to any other model ID for a neutral third referee. The other three fall back to the vision provider settings when blank. See AI Analysis → Second opinion.

Fallback synthesis model — DFIR_AI_SYNTH_FALLBACK_PROVIDER/_MODEL/_KEY/_BASE_URL choose a second synthesis model. It is used only when the synthesis model's safety filter stops an answer partway, in synthesis, in a deep-pass batch, in the default second-opinion referee, in Explain event, or in a view summary (View summary, the starred-events report and the session summary). When the fallback writes the referee's verdicts, the second opinion names the fallback as the referee. A referee you pick with DFIR_AI_RECONCILE_MODEL never falls back. Imports and saved case reports never use the fallback. Setting _MODEL turns it on; blank means no fallback. A blank provider uses the synthesis provider, and a blank key or base URL uses that provider's settings. Pick a model from a different vendor, so the same filter does not stop it too. DFIR_AI_SYNTH_SAFETY_RETRIES (0–3, default 1) sets how many times the stopped model is asked again before the fallback runs; 0 switches to the fallback at once. The provider and model pickers sit in Settings → AI beside the synthesis model and show in Essential mode. Restart after you change these values. See AI Analysis → Synthesis.

The safety retries and the fallback run for the Claude Code provider, the direct Anthropic API (a refusal), OpenAI-compatible providers (a content filter or a refusal), Gemini (a safety block) and Codex (a refusal, or a prompt OpenAI rejects under its usage policy).

Save safety-stopped prompts — DFIR_AI_CAPTURE_SAFETY_STOPS (off by default). When it is on and a model's safety filter stops an answer, the exact prompt that model was sent is saved as a JSON file in the case folder, under state/safety-stops/. The file also names the AI step, the provider, the model and the time, so the prompt can be replayed later to find what tripped the filter. Screenshots are not copied; the file lists only how many were sent. The file holds case evidence. When anonymisation is on for the case, it holds the masked text the model saw. Nothing is written while the setting is off. The setting sits in Settings → AI below the safety-stop retries. Restart after you change it.

Missed evidence review — DFIR_JEV_ENABLED/_PROVIDER/_MODEL/_KEY/_MAX_ROWS/_BATCH_SIZE/_TIMEOUT_MS/_GRADING configure the fast decision model that re-grades Info rows. On the OpenRouter provider the Model field is a dropdown of OpenRouter's decision models with their prices, plus Enter model ID manually… and a Refresh button. The review is off until DFIR_JEV_ENABLED is set. See Advanced → Missed Evidence Review.

Deep pass — DFIR_DEEP_PASS_MAX_BATCHES (default 30) caps how many batches a deep pass run may take, refusing oversized runs up front; DFIR_AI_OBSERVE_PROMPT_FILE is an ejectable override of its batch-observation prompt.


Enrichment

Lists all 13 enrichment providers. Each shows:

  • Current status (configured / key missing)
  • Which environment variable to set
  • Enable/disable for this case

Info

Nothing is sent externally until you enable a provider for the specific case you're working on.


Exposure

Customer exposure check configuration:

  • Add customer domains and email addresses
  • Select which providers to use (LeakCheck, HIBP, DeHashed, Shodan)
  • Run the exposure check and view results

Integrations

  • Server sets — the DFIR-IRIS, Timesketch and Velociraptor connections
  • Notion (API token)
  • ClickUp (API token)

Server sets

A server set is one named group of connections: a DFIR-IRIS server, a Timesketch server and a Velociraptor server. Each case uses one set. When you open a case, the Companion uses that case's servers. You do not enter keys again when you move between cases.

  • The list. Settings → Integrations → Server sets shows every set. Each row shows the tools the set uses and Used by n cases. Use + Add server set, Edit, Make default and Delete to manage the sets. A change applies at once, with no restart.
  • The default set. One set has the default badge. New cases use it, and so do actions that run with no case open, such as the client list on the Velociraptor tab. Those actions show Using server set: name next to them. When a case is open, the client list, ↻ Refresh client list and the time-scope preview use the case's set. The client list then names that set. After the upgrade, your old IRIS, Timesketch and Velociraptor settings become a set named Default, and it is the default. (Version 0.45.0 named this set Testing. An existing set keeps its name; rename it here if you like.)
  • The editor. Leave a tool empty when the cases on the set do not use it. The Companion then hides that tool's buttons for those cases. A stored key or password shows as ••••••. It stays the same unless you type a new value. Save keeps the editor open and shows Saved.. Click Close to close it.
  • Testing a connection. Test IRIS, Test Timesketch and Test Velociraptor sit under each tool's fields. Each one tests the values in the editor now, for a new set or an edited one. You do not have to save first, and a test saves nothing. The result shows under the button: Connected. or Failed: and the reason. A button stays off until you type that tool's URL (Velociraptor: the API config path). A key or password that still shows •••••• tests with the stored value. An empty one tests with no secret. Test connection at the bottom tests every tool you filled in.
  • Velociraptor GUI URL. Give the address you open the Velociraptor GUI at. If the GUI has a path, include it, for example https://host/velociraptor. You can paste the address from the browser bar: the Companion removes /app/index.html and everything after it. Hunt links then open under that path.
  • Skip TLS verify. Tick it on a set's IRIS or Timesketch fields to accept that server's certificate without a check. In a set, the tick alone is enough, also for a server on another machine: you do not need DFIR_TLS_ALLOW_INSECURE_EXTERNAL. Use it in a lab only. Anyone on the network path can then read that server's key. Give the server's CA bundle instead to keep the check on. When a certificate is refused, the error tells you to set the CA bundle in the set.
  • New Case form. The Velociraptor / IRIS / Timesketch servers (optional) list preselects the default set. Pick another set, or pick + Create a new server set… to make one. Test each tool before you save. Save closes the editor and selects the new set.
  • The case list. Open the case list in the header. Under each case name, the list shows the case ID and the name of its set, for example CASE-A · Client A. A case with no set shows no server set.
  • Change a case's set. Open the case, then open Settings → Integrations → Server sets. The top line names the open case's set: The open case uses server set: set name. If the case has no usable set, the line is red and says why. Click Change… next to it to attach a different set. If the case has work on its old servers, the Companion warns first. The warning lists the live monitors that will stop, the IRIS case link that will be forgotten and the Velociraptor hunts that will be forgotten. Click Change server set to go on, or Cancel to change nothing.
  • What is kept. Nothing on the old servers is deleted. The Companion only stops talking to them. Events, IOCs and findings in the case stay. The next IRIS push makes a new IRIS case on the new server. The case's activity log records the change.
  • Who can change what. In team mode, only an administrator edits, adds or deletes sets. Other users see the list without the buttons. Only an administrator of the case sees Change….
  • Keys stay on this machine. The Companion stores the keys and passwords of every set encrypted on this machine. They never go into case exports, archives, reports, support bundles or logs.
  • Where the sets are stored. The sets are in the server-sets folder next to the cases folder. In Docker, that folder is /data/server-sets. The supplied docker-compose.yml mounts ./server-sets:/data/server-sets, next to ./cases:/data/cases. If you start the container yourself, mount both folders. If you mount only the cases folder, a restart loses every set, and each case then shows no server set. Back up both folders together: the key that decrypts the sets is in the cases folder.

The DFIR_IRIS_*, DFIR_TIMESKETCH_*, DFIR_VELOCIRAPTOR_API_CONFIG, DFIR_VELOCIRAPTOR_ORG and DFIR_VELOCIRAPTOR_GUI_URL values in .env are read once, on the first start after the upgrade, to make the Default set. After that, edit the sets here. The setup wizard's DFIR-IRIS, Timesketch and Velociraptor steps also save into the default set, not into .env.


Velociraptor

The shared bundle library — global, like every other Settings tab.

  • Reconnect button and ↻ Refresh client list
  • Browse server artifacts
  • Triage bundle management (built-ins, custom, edit in place, Reset to default)
  • Per-bundle defaults (wait, timeout, expiry, per-artifact parameters and exclude filters)

Running a bundle, importing a hunt from the Velociraptor GUI and live monitoring act on the connected case, so they live on the dashboard's Fleet Collection panel. The API config path, the GUI org and the GUI URL belong to each server set. Reconnect uses the default set. The client list and ↻ Refresh client list use the open case's set, or the default set when no case is open.


Tools

Run your own locally-installed external tools against raw evidence the Companion can't parse (EVTX/PCAP/files), then ingest the tool's output through the existing importers. The Companion never downloads or bundles a binary — install and update it yourself (repo links are shown per tool). The one exception is the Velociraptor binary path's Download latest release button, which runs only when you click it: it accepts only https downloads from GitHub's own release hosts, and it checks the file against GitHub's published SHA-256 when the release has one. If either check fails, it refuses and saves nothing.

  • Hayabusa / Velociraptor CLI — EVTX → csv/json-timeline / artifact JSON
  • Suricata / Snort — PCAP → alerts (Snort uses your own rules file); see Packet captures for what a Suricata run imports
  • YARA — scan files/dirs → rule matches (file/hash IOCs)

Custom tools — beyond the five built-ins, add your own: a name, the binary path, a run command (<target> = input file, <output> = output file, omit for stdout), an optional update command, and the file extensions it handles. The output is auto-detected and routed to the right importer. Add as many as you like; each appears in the Import/drop banners for its extensions.

Per tool: binary path (blank = off), run-args template (<target>/<output>/<rules> placeholders), rules path (Snort/YARA), a separate Update rules command + button, an auto-run on drop toggle, and timeout/output caps. Update rules works with no case connected; with team sign-in on, only a global administrator can run it. Click Reconnect / apply to apply saved paths without a restart. A raw .evtx/.evt/.pcap/.pcapng copied into a case's drop/ folder runs automatically when a matching tool has auto-run on; the Import dialog shows a banner for these formats. Config is stored in .env (DFIR_TOOL_*, not a secret). Commands run with no shell (args tokenized) and the target path is contained to the case directory. Master kill-switch: DFIR_TOOL_AUTO_RUN=off.

Before a tool runs, the Companion checks the target file itself. It refuses a link, a named pipe or device, and a file with a second name elsewhere, and the run fails with the reason. In team mode the tool reads a private copy of the checked file, not the file in the case folder, so a file swapped after the check cannot reach the tool. The copy's SHA-256 and size go into the chain-of-custody record of the tool's output, and the copy is deleted when the run ends. A folder-input tool (Velociraptor --ROOT) always gets such a copy. In single-user mode a file-input tool reads the case file in place, with no copy, because many-GB evidence would otherwise be copied on every run. The tool's working folder sits outside the case folder, beside the cases in .export-staging/.

MCP servers

MCP servers are configured in Claude Code, then allowed under Settings → All → Tools → MCP servers. Companion stores policy and evidence-delivery settings, not the MCP endpoint or token. Leaving Restrict to tools blank allows all tools offered by an enabled server; use the field only when you want a narrower allowlist. Investigation timeout sets DFIR_MCP_AGENT_TIMEOUT_MS; it defaults to 3,600,000 ms (one hour) because memory and disk analysis routinely outlast ordinary AI requests.

See MCP Analysis for recommended REMnux and SIFT servers, Claude Code registration, shared-path and SCP configuration, validation, security notes, and plain-English prompt examples.


IOC Whitelist

Global known-good pattern list:

  • Add CIDR, exact, or regex rules
  • Optional type scoping (e.g. "only match IPs")
  • Import/export as CSV or JSON
  • Apply to current case — retroactively marks matching IOCs false-positive

NSRL

Known-good file hash database:

  • Paste hashes, import a flat hash file, or load an NSRL RDS hash list by file path
  • Connect to a large NSRL RDS SQLite database (Node 22.5+)
  • Apply to current case

Importers

Custom declarative importers:

  • List all custom importers (filename, format, match criteria)
  • Add a new importer (paste JSON spec)
  • Reload importers from disk
  • Get AI prompt — copy the prompt to use with your AI assistant to generate a spec for a new file format
  • Precedence setting: built-in-first (default) or external-first

Sigma Rules

Settings → Sigma rules controls the SigmaHQ rules that grade AWS CloudTrail and Entra ID sign-in / audit imports, and Apache / nginx / Squid access logs (details). Every setting is read on every import, so a change applies to the next import without a restart. Cases already imported keep the grading they got, and their report names the release and settings that produced it.

Setting Values Default
DFIR_SIGMA_RULES on / off on
DFIR_SIGMA_RULE_STATUSES any of stable, test, experimental (deprecated and unsupported are never used) stable,test
DFIR_SIGMA_MIN_LEVEL critical, high, medium, low — lower levels only tag the event high
DFIR_SIGMA_UPDATE_CHECK on / off — one check at startup; shows a notice, never installs off

The tab also shows:

  • Installed release — the SigmaHQ release in use, where it came from (bundled, downloaded, or installed from a file), and how many rules load, are filtered by status, disabled, or refused per log source. Any problem reading the installed release is shown here and in the server log.
  • Check for updates — asks GitHub for the latest SigmaHQ release. When it is newer, the release is downloaded and checked against the SHA-256 GitHub publishes, then shown as rules added, changed and removed for the log sources the Companion grades. Nothing is installed until you click Install.
  • Install from file — the same review for a release zip you downloaded yourself (air-gapped labs). A file install is marked "not checked against GitHub" and records the file's SHA-256. The zip must be 32 MB or smaller; a SigmaHQ release is a few MB.
  • Measure against this case — shown beside Install when a case is open. It runs the Re-grade this case preview with the reviewed release instead of the installed one, and counts the rows it would raise, lower, move into the forensic timeline or take out of it. It uses your current statuses, disabled rules, exclusions and minimum level. A lowered row never drops below the importer's own grade. Imports whose stored file is missing or changed are not scanned, and the result says how many. It changes nothing; it runs only when you click it.
  • Roll back — switches back to the previous release (the bundled rules after a first install).
  • Rules — every rule for a graded log source, with its state and reason. Disable switches a noisy rule off with a reason; Enable switches it back on, including the six rules the Companion ships switched off because they need tenant-specific values.
  • Exclude… — keeps a loaded rule on but skips one known-benign value in your environment. Name a field the rule's log source has (for example userIdentity.arn), a value (for example arn:aws:iam::111122223333:role/BackupService) and a reason. The value matches the way a Sigma value does: letter case is ignored, and * and ? are wildcards. A record that matches the rule AND the exclusion is not graded by that rule; every other record still is. If the field was cut short at import, the exclusion does not apply and the rule still matches. The Companion refuses a field the log source cannot read. Each exclusion shows under its rule with its reason and a Remove button. Exclusions are server-wide, like the disabled list, and are kept in sigma/exclusions.yml in the data folder.

Disable, Enable and exclusions apply from the next import only. Events already imported keep their grading until you re-grade the case. The detection-rules line in each report carries a short hash of the exclusions that graded the case, so two imports graded under different exclusions never look the same.

  • Re-grade this case — grades the open case's CloudTrail, Entra and web access-log rows again with the rules and settings shown above. Click Preview re-grade of the open case first: it changes nothing and shows how many rows would be raised, lowered or left unchanged, how many would join or leave the forensic timeline, and each import's state ("already graded with the current rules", "graded with another release or setting", or "never graded by Sigma"). Then click Apply re-grade.
    • Only Sigma's part of a row changes: its severity, the Sigma: text, the Sigma rule: lines, the rule's ATT&CK techniques and the release stamp. The new severity is the highest of the importer's own grade, the new Sigma grade and the content tagger's grade, so it can go down. The row's "changed after first recorded" history records the move.
    • Both timelines change. A row raised above the forensic gate joins the forensic timeline as a normal graded row (it is not marked promoted, so a later re-grade can take it out again). A row lowered below the gate leaves the forensic timeline and stays in the super-timeline. A row you promoted stays in the forensic timeline at its new grade.
    • Only rows the import kept are re-graded. A record that a rule now matches but that the import did not keep as its own row (below the severity floor, over the event cap, or folded into a grouped row) is counted in the preview: re-import the file to add it. A grouped row is re-graded on the record that sets its identity. A row whose stored file is missing or changed, or whose record reads differently now, is left as it is and counted.
    • No AI run starts. The case's conclusions are marked out of date; press Re-synthesize when you want them updated. The content tagger does not run again.
    • With the same rules and settings, a re-grade changes nothing.
    • One re-grade per case at a time: a second Apply for the same case while one runs is refused until the first finishes. On a server short of memory the import memory guard can refuse the apply; nothing changes, and you run the re-grade again later.

Updates, rule changes and Apply re-grade are not available in the demo.

To see which of one case's sources these rules grade, open the Sigma coverage card in the Hunt Workbench.


Content Tagger

Controls the content-based event tagger (companion/data/tags.yaml), env-configured (no dashboard fields):

  • TAGGER_AUTO — run the tagger automatically after every import (default true; false = manual-only, via Super-Timeline → Content tagger → Run tagger)
  • TAGGER_SCOPE — forensic | super | both (default both); super only tags the super-timeline (never mutates severity/MITRE)

KEV

CISA Known Exploited Vulnerabilities integration:

  • Enable/disable KEV cross-reference
  • CVEs in findings/events are checked against CISA KEV
  • KEV-listed CVEs are highlighted and mentioned in synthesis context and report

Loading the catalog. "Load from CISA feed" makes the server fetch the feed, so the URL must be https to a public address. A URL that points at a loopback, private or link-local address is refused — that is how a feed field turns into a way to read the host's own internal services — and every redirect is checked the same way. The connection is then pinned to the address that check approved, so a hostname cannot change what it points at between the check and the connection. A host that will not resolve is refused rather than tried, so on a DNS outage this reports "could not be resolved". "Load from server file" reads a saved copy and needs no outbound access at all; use it when the Companion is air-gapped.

  • DFIR_KEV_ALLOW_INTERNAL_URL=true — allow http and internal addresses, for an organisation that mirrors the KEV feed inside its own network. Off by default.

Report Templates

Manage report templates:

  • Edit the default template or create new ones
  • Set: cover title, subtitle, accent colour, running header/footer, logo visibility
  • Enable/disable and reorder report sections
  • Mark sections as mandatory at release
  • Require independent peer review and/or evidence links for every non-dismissed finding
  • Assign a template per case

Critical and High findings always require valid evidence-event links, regardless of template. A template's evidence requirement extends that gate to every non-dismissed finding. Independent review requires team authentication; solo self-review cannot satisfy that rule.

Built-in templates: Standard (full technical report), Executive (condensed), and any you create.


Dashboard Views

Preset panel layouts:

View Best for
Analyst (default) All technical panels, in the app's intended reading order
Lead Findings, timeline, playbook, hunting
Executive Findings, attack path, countermeasures, exposure
Triage Timeline, IOCs, MITRE, assets
Report Report-oriented panel order
Deep-Dive Evidence chain, hypotheses, threads, notebook
Hunt-Prep Hunting profile, adversary hints, next techniques, query translator

A new case (or any case with no saved per-case dashboard-view choice) opens with Analyst instead of the raw "Custom" section order. Explicitly picking Custom from the dashboard-view menu still sticks across reloads. A permanent note below all panels points back here for further customization.

Each preset is fully customisable — reorder panels, set a severity floor, cap the timeline row count, link a report template. Saved per case.


Notifications

Alert channels for new findings, playbook updates, and investigation milestones:

  • Slack webhook
  • Microsoft Teams webhook
  • Mattermost webhook
  • Discord webhook
  • Telegram bot
  • SMTP email

Each channel has:

  • A minimum severity threshold (only notify for High+, for example)
  • Per-event-type toggles (findings / playbook / milestones)
  • A Test button that sends a test message

Info

Notification configs are stored in a global config file (not .env) and webhook URLs are redacted in all API responses.

Blocked destinations: cloud metadata and link-local addresses

A webhook URL or SMTP host that points at a cloud instance-metadata service or a link-local address is refused. This covers 169.254.0.0/16, fe80::/10, their IPv4-mapped IPv6 spellings, fd00:ec2::254 (AWS), 100.100.100.200 (Alibaba), and names such as metadata.google.internal. You see the error when you save the channel. A host name that resolves to one of these addresses is refused when a message is sent, and Test shows why. The connection then goes to the address that was checked.

Webhooks and mail servers on your own network or on this machine (10.x, 192.168.x, 127.0.0.1, an internal host name) still work, over http:// too.

Webhook redirects are not followed. A webhook that answers with a redirect (HTTP 3xx) is reported as a failed send. Use the final URL in the channel instead.

Telegram: leave the bot token blank to reuse the war-room bot's

A Telegram channel with no token of its own falls back to DFIR_TELEGRAM_BOT_TOKEN from .env, and the token field shows (already set). Fill in only the chat ID. The token is never copied into the notification config, so rotating it in .env rotates the channel too. Type a token here only to send through a different bot.


Audit Export

Forward each case's activity log — who did what, when, to which case, and whether the action succeeded — to a SIEM. This is the surface auditors ask for: SOC 2 and ISO 27001 both expect privileged actions to land in central log aggregation rather than only in the tool that recorded them.

Three destination types:

  • Splunk HEC — collector URL and an HEC token. Optional index and sourcetype.
  • Elasticsearch — cluster URL and an index. Username + password, an API key, or no credential at all for a cluster without authentication.
  • Syslog — host, port, and UDP or TCP. RFC 5424, facility log audit.

Each destination has an on switch, a Test button that sends one clearly-marked test record, and a Send history button.

What each record carries

Field What it says
id The activity entry's own id. A SIEM that honours it collapses a duplicate from a re-send.
timestamp When the analyst acted — not when the batch was sent.
caseId Which case.
category, action, detail What was done.
actor The name recorded against the action.
actorId, actorKind Present only when an authenticated session made the change.
actorVerified Whether the server verified that name, or the client supplied it.
outcome success or error. A failed privileged action is the interesting one.
targetType, targetId What the action was aimed at, when the entry names one.

This sends case content off the box

Case identifiers, analyst names, and what each analyst did go to a third-party system. Off by default — each destination is opt-in, and the list starts empty.

Blocked destinations: cloud metadata and link-local addresses

A Splunk or Elasticsearch URL, or a syslog host, that points at a cloud instance-metadata service or a link-local address is refused. This covers 169.254.0.0/16, fe80::/10, their IPv4-mapped IPv6 spellings, fd00:ec2::254 (AWS), 100.100.100.200 (Alibaba), and names such as metadata.google.internal. You see the error when you save the destination. A host name that resolves to one of these addresses is refused when records are sent, and Test shows why. The connection then goes to the address that was checked. A destination saved before this rule can still be switched off, but it does not send.

Collectors on your own network or on this machine (10.x, 192.168.x, a syslog forwarder on 127.0.0.1:514, an internal host name) still work, over http:// too.

Redirects are not followed. A Splunk or Elasticsearch endpoint that answers with a redirect (HTTP 3xx) is reported as a failed send, and the records stay queued for the next attempt. Use the final URL in the destination instead.

Changing the collector asks for the credential again

Editing a destination and leaving its token, password, or API key blank keeps the saved one — but only while it still points at the same collector. Change the URL and the credential is not carried across: a Splunk destination repointed at a new host asks for a new HEC token, and an Elasticsearch one drops the saved password rather than presenting it to a different cluster. The index, sourcetype, and username travel with the rest of the config, because they say what to write rather than who may write it.

Switching a destination on forwards only what happens next

A destination begins at the current end of every case's log — the position is set the moment it is switched on, not the first time it sends. Turning one on does not ship the history that is already recorded; that is what Send history is for, and it asks before it runs. The reason is the obvious failure: a destination enabled mid-investigation would otherwise push a year of activity into a production SIEM the moment someone ticked a box.

Switching one off and on again works the same way. The gap while it was off is not filled in on the way back up, because "only what happens next" has to mean the same thing every time it is switched on.

After an outage

The export remembers how far it got in each case, per destination, and writes that position only after a send is accepted — and for Elasticsearch, only when the cluster's reply actually accounts for every record sent. A collector that is down holds the position where it was, so the next action re-sends from there rather than skipping, and the companion also drains everything still pending once at startup. That second part matters for a case that has gone quiet or been closed: it will never see another action to trigger a send of its own. Splunk and Elasticsearch receive the entry id, so a re-send is collapsed rather than duplicated; a plain syslog receiver has no such mechanism and will show the line twice.

Info

Destinations and their credentials are stored in a global config file next to cases/ (not .env), and every token, password and API key is redacted in all API responses. Only the TLS trust for a self-hosted collector is an .env setting: DFIR_AUDIT_CA and DFIR_AUDIT_INSECURE.


War-Room Bot

Inbound slash commands from Slack / Teams / Telegram — see War-Room Slash-Command Bot for setup. Configured entirely in .env; each platform switches on when its secret is set.

Variable Meaning
DFIR_SLACK_SOCKET_MODE =on to receive Slack commands over an outbound WebSocket. No tunnel, no Request URL. Needs DFIR_SLACK_APP_TOKEN
DFIR_SLACK_APP_TOKEN App-level token, xapp-…, scope connections:write. Not a bot token
DFIR_TELEGRAM_POLL =on to receive Telegram commands by long polling. No tunnel, no inbound URL. Needs only DFIR_TELEGRAM_BOT_TOKEN
DFIR_TELEGRAM_BOT_TOKEN @BotFather token. Required for polling; in webhook mode it delivers ask/hunt/synthesize results
DFIR_ALLOWED_HOSTS Hostnames the Companion answers to. Required in webhook mode — the tunnel/proxy hostname must be listed, or requests are refused with 403. Not used by Socket Mode or polling
DFIR_SLACK_SIGNING_SECRET Slack app signing secret; enables /integrations/slack/command. Webhook mode only
DFIR_TEAMS_TOKEN Shared bearer token; enables /integrations/teams/command. At least 32 characters, or the server refuses to start. Wrong guesses are rate-limited
DFIR_TELEGRAM_SECRET_TOKEN setWebhook secret; enables /integrations/telegram/command. Webhook mode only. At least 32 characters, or the server refuses to start. Wrong guesses are rate-limited
DFIR_SLACK_ACTION_USERS
DFIR_TEAMS_ACTION_USERS
DFIR_TELEGRAM_ACTION_USERS
Comma-separated user ids allowed to run ask/hunt/synthesize/bind. Unset = open to the whole channel; once set, everyone else is confined to the channel's bound case
DFIR_SLACK_RESPONSE_HOSTS
DFIR_TEAMS_RESPONSE_HOSTS
Extra hosts an async result may be delivered to, for a self-hosted Slack-compatible server. Defaults cover the platforms' own hosts
DFIR_TELEGRAM_API_BASE Bot API base URL override (default https://api.telegram.org)

Channel-to-case bindings are stored alongside the notification config, not in .env.


Updates

Opt-in GitHub release check. Shows a dashboard banner when a newer version is available. Never auto-installs.


Diagnostics

Operator health view:

  • Disk usage and warning level on the cases folder
  • Case count (open / closed)
  • Processing queue (screenshots pending analysis, synthesis in flight)
  • Redacted AI config (provider, model, timeout — never the API key)
  • Recent AI error counts by type
  • AI cost — this case — calls, dollar cost, and token counts broken down by Vision / Synthesis / Other and by model, read from the provider's real per-call cost and token counts (never a guessed price). Providers that don't report cost/tokens show "n/a", never a fabricated $0.00.
  • Importer health (attempt counts over 24h/7d)
  • Local performance and capacity history — bounded importer yield, indexed-query p50/p95, job stalls/retries, AI latency/rate limits/tokens/cost, export duration/size, live-connection health, memory, disk and projected case growth. Metric labels are fixed categories and never contain case titles, evidence, filenames, hostnames, users or IOCs. Set DFIR_LOCAL_TELEMETRY=off to disable it.
  • Previewed support bundle — inspect the aggregate-only JSON before copying or downloading it. Secrets, absolute paths, case identifiers and evidence are excluded by default; nothing is sent externally.
  • Redacted support bundle (.zip) — one file to send with a bug report when you cannot share evidence. It holds the Diagnostics report, the server log, the always-on debug log, optionally the open case's log, and the layout of each failed import: format, encoding, row count and column types. Column names appear only when they are generic forensic names from a fixed list; no cell value from any file is included. Case names and IDs, investigators, file names, hostnames, usernames, IP addresses (internal and public), emails, domains, paths, URLs and configured secrets are replaced by placeholders such as ANON_HOST_3. The same value gets the same placeholder in every file, so you can follow one host across logs. The map from placeholders back to real values is never written. A password-protected case that is locked contributes no log and no import layout. Open the zip and read it before you send it. This is not a case export: a full case export carries real evidence and is never a support artifact. For each failed import the bundle also shows what the importer decided: which column it used for the time, host, user and other fields; how many rows it skipped, removed or kept as unusual, and why; which fallback it took; and where parsing stopped when the parser knows the row. Column names appear only from the same fixed list of generic names, and a custom importer shows as custom.
  • Always-on debug log — every log line at every level, including debug, goes to debug.log and debug.1.log in the global logs folder, whatever the Log verbosity is. You do not need to switch to debug and repeat a problem before you build a support bundle. The two files together never pass DFIR_DEBUG_LOG_MAX_MB (default 50); the oldest lines go first. The debug log is never written inside a case folder, so it never travels with a case export. 0 turns it off; read at startup.
  • Compute case sizes button
  • Live AI test — connectivity test with latency
  • Pre-flight check — re-run startup diagnostics on demand

For repeatable capacity testing, run npm run bench:storage -- --sizes=1000000,10000000 --query-runs=20 from companion/. It creates throwaway data outside the cases folder and emits one JSON result per size with import events/second, cold-open latency, filtered-query p50/p95, export duration and peak memory. Use the same event sizes, query-run count, hardware and filesystem when comparing releases. - Per-case backup list — state backups with one-click restore - State backup configuration (retention counts, interval) - Background-job limits — durable history retention, total concurrency, and a per-case running cap. The per-case cap keeps one large investigation from occupying every worker. - Evidence integrity — the result of the last chain-of-custody verification: which triggers are active, how long ago a case was last checked, how many artifacts verified clean, and any case whose evidence or custody log failed. See Chain of Custody - Host Clock Skew — each host's measured clock offset from cross-tool timestamp anchors (events multiple sources independently timestamped), flagged once it exceeds 60 seconds. A host needs several consistent anchors before its offset is trusted; ↻ Re-measure re-runs detection against the current timeline, and the Align timelines toggle projects every host onto a common time axis — affecting the Forensic Timeline, correlation windows, the evidence graph, and reports while it's on. Alignment never rewrites recorded evidence: each row keeps its original timestamp for display, alongside the corrected one. An analyst can manually override any host's offset; an explicit 0 pins that host's clock as already correct.

A **large** offset — beyond about two days — is measured and flagged but never applied on its
own. A VM restored from an old snapshot can be months out, and a fictional offset that size
would move a whole host's timeline, so the panel shows it as *measured but not applied* and
waits for you to confirm the host's clock and type the offset in yourself.

The panel also warns about a host whose **own** timestamps split across a huge gap — say 17 of
214 events dated nine months before the rest. That check needs no second clock, so it still
fires on a case with a single evidence source, where no offset can be measured at all. It is a
warning only: nothing is corrected, and it is usually the first sign of a wrong VM clock.

Finally, a timeline row whose **year** was adjusted at import says so under its timestamp, with
the time it was imported as. Some sources (BSD syslog, Cisco ASA, Snort, a bare time column in a
CSV) carry no year at all, so the import has to supply one and the merge re-anchors an obvious
stray onto the case's dominant year. Only a guessed year is ever moved — a year read out of the
record is evidence and is left exactly as recorded.

Evidence integrity environment variables

Variable Default Effect
DFIR_CUSTODY_VERIFY_ON_OPEN_MS 14400000 (4h) How long a case's verification stays fresh. Opening a case re-verifies it in the background unless it was checked within this window. 0 turns on-open verification off
DFIR_CUSTODY_VERIFY_INTERVAL_MS 0 (off) Interval for a sweep of every case, archived included. Off by default so an idle install does no background hashing — set it if you want unattended assurance across the whole store

Background-job environment variables

Variable Default Effect
DFIR_JOBS_MAX 100 Durable job rows retained per case; pruning removes only the oldest terminal rows, never active jobs or their separate analysis-run manifests
DFIR_JOBS_CONCURRENCY 4 Maximum jobs running across all cases
DFIR_JOBS_PER_CASE 1 Maximum jobs running for one case, preventing a busy case from starving the others
  • Case Statistics — per-case totals, per-source event breakdown, and import velocity
  • Large-import reliability — atomic state-save retry count for big imports is tunable via DFIR_ATOMIC_WRITE_RETRIES (default 20, ~8.4s of retries) for setups where antivirus/search indexing can outlast the default retry budget on a large USN/MFT import

Tip

The Diagnostics page is your first stop when something breaks. It shows the AI error count by type — auth errors = wrong key, billing errors = quota exceeded, rate limit = slow down — without ever showing your API key.