Incidents
An incident is a device, a time window, a set of model results, and the evidence behind them. It is not a notification and not a verdict.
What an incident carries
Section titled “What an incident carries”| Field | Meaning |
|---|---|
| Title | Short, generated from the models that fired |
| Severity | info, low, medium, high, critical |
| Score | 0–100, with its history over time |
| Status | open, investigating, resolved, false_positive |
| Devices | The devices involved |
| Models | Which detection models produced it |
| Evidence | The source records, in order |
| Workflow history | Every status change, with actor, comment and sequence |
| Revision | Incremented on every write |
Each model result links to the specific persisted analysis result that
produced it, with its own reason and evidence identifiers. You can always get
from “this incident exists” to “because this model saw these records at this
time”. Since 0.38 the reason of a beaconing or scan result is the detector’s
full measurement — interval_cv=0.0312,period_seconds=60.00,…,destination=…
or unique_ports=60,window_seconds=60,hosts=3,destination=… — and a
policy= token at its end says when a role, a shared destination or the
learned baseline withdrew the verdict; the models page
has the grammar.
An incident is one per device, model and pattern, and it grows rather
than multiplies: a series that keeps beaconing updates its incident, and
since 0.38 a scan is keyed by the destination’s /24 (IPv6 /64), so a
device sweeping a subnet is one incident with more hosts and evidence every
hour, not one incident per host.
Evidence that outlives its payload
Section titled “Evidence that outlives its payload”Raw payloads have a retention window. Observation evidence stays resolvable for 90 days, past the payload’s own expiry, so an incident from last month is still explicable even after the underlying raw record has aged out.
If an evidence identifier cannot be resolved — its source record is unavailable, or its record kind has no public form — it is listed separately as unresolved. It is never silently relabelled as model output, because “the model said so” and “we no longer have the record” are very different statements.
The workflow
Section titled “The workflow”Changing status requires the analyst role, takes a comment, and appends to the history. Four statuses, and the honest ones matter most:
investigating— someone is looking.resolved— dealt with.false_positive— the detection was wrong. This is the feedback that makes the models better, and it is worth spending the thirty seconds on.
Further findings beyond the daily cap
Section titled “Further findings beyond the daily cap”A device can open at most five new incidents on one UTC day (the model file’s
aggregation.max_new_incidents_per_device_per_day; see
aggregation).
The sixth and every later candidate of that day are folded into one
overflow incident per device and day:
| Identifier | inc_overflow_… (stable per tenant, device and day) |
| Title | Further findings beyond the daily cap |
| Summary | “and N more on Periodic destination contact ×3, Destination port scan ×1) |
| Models | the union of the folded models |
| Evidence | the union of the folded evidence, so every folded candidate is still explicable |
It is an ordinary detection incident on the wire and in the list: it is delivered to the sinks once when it opens, escalated only when a later fold carries a higher score, and it goes through the same workflow. The interface shows it as a normal incident, without a display of its own. Read it as a count, not as a finding: a device that fills its cap on a day is a device with a role or a threshold to look at — the learning-phase checklist says how — and the folded candidates keep updating the overflow incident rather than opening their own. Incidents that already exist are updated whatever the count; the cap only bounds what is new.
The optional analyst
Section titled “The optional analyst”If enabled, an analyst can be asked to explain an incident. It returns a title, a narrative, hypotheses with confidence values, recommendations, and the list of evidence identifiers it was given — plus the model name and prompt version that produced it, so an odd answer is traceable rather than mysterious.
Its state is always visible: not_requested, queued, running, complete,
failed, provider_disabled, budget_exhausted, or — since 0.39 —
quota_exhausted, the licence’s
monthly quota rather than your
daily budget.
Four hard rules:
- Off by default. It requires an explicit provider, an explicit model name, a nonzero daily budget and explicit token rates. Nyxtrace contains no guessed prices.
- Metadata only. Addresses, domains, ports, times, scores. No payloads, no credentials, no whole log files.
- Read only. It can never approve a proposal, change a threshold, activate a model or execute an action.
- A disabled provider or an exhausted budget is an explicit error. There is no automatic paid fallback and no hidden retry.
Network data is treated as attacker-controlled input: host names, user agents and DNS names go into the request as data, never as instructions.
There is also a deep mode. In this version it is a workflow label that uses the same bounded request as the standard mode — it does not enable extra tools, retries or spend. It is documented that way rather than implying more.
Switching the analyst on — the variables, the budget arithmetic and the licence feature — is an operator task: see the OpenAI analyst.
The daily brief
Section titled “The daily brief”A local summary of new devices and active incidents is available for a given date. It is local metadata only — retrieving it does not send a message anywhere. Sending is a sink connector’s job — the webhook delivers every new, escalated and closed incident to a receiver of your choice, and the chat sinks (Pro) post the same as a message to Telegram, Discord, Slack or Matrix.