Skip to content

The learning phase

A behavioural detection system without a baseline is a random alert generator. Nyxtrace therefore spends its first weeks learning, and deliberately suppresses its own findings while it does.

This is the single most common source of “is it broken?” on a new installation. It is not broken. An empty incident list with a green hourly analysis timer is the correct state.

Requirement Value
Learning period 14 days by default, configurable from 14 to 28
Rarity prior window 14 days of prior observation before a destination counts as new
Robust baseline at least 8 comparable hourly samples for that hour-of-week
Beaconing at least 12 intervals over at least an hour, with a coefficient of variation ≤ 0.10; fewer than 10 devices share the destination; not an expected series (since 0.38 — 5 intervals at ≤ 0.15 before)
Scan at least 50 unique ports within 60 seconds on at least 2 destinations, or 200 on one (since 0.38 — 20 on any one host before)
Peer groups at least 24 hours of history, a minimum group size, and a minimum number of devices in the tenant

Note that the requirements are per device and per feature, not global. A device that appeared yesterday is in its own learning phase even if the appliance has been running for a month. That is why a merge in the identity model keeps the earlier first-seen timestamp: a device whose MAC rotates must not have its learning phase restart every few days.

The learning phase of the detectors is a different clock from the learning phase of the automatic response (learning_until, shown by ndr response calibration). They can end on the same day, and on the reference installation they do — an appliance cut over on 2026-09-10 sees its first devices mature on 2026-09-24 — but neither depends on the other.

The hourly analysis runs normally. Since 0.33 it computes every model result exactly as it would for a mature device — the real score and the verdict — and stores it marked suppressed, with the reason learning baseline; followed by the detector’s measurement. A suppressed result reaches no incident, no notification, no sink, no metric and no device risk, and the device detail’s score history leaves it out. Nothing is thrown away: when the requirements are met, the same evidence starts producing results that count.

Before 0.33 the detectors returned before measuring anything, so every suppressed result was stored with score 0 and no verdict. Those rows stay as they are — there is nothing to backfill them from, because the measurement was never made.

Two detectors keep a result without a measurement by design. A baseline model with fewer hourly samples than its minimum (8) in the device’s weekday-hour slot has no reference yet, and that result reads learning baseline alone. The peer-isolation model has no learning-phase result at all: a device inside its learning window is not part of the peer population, so there is nothing to compare it against.

So during the learning phase you can, and should, check:

  • that devices are appearing in the inventory,
  • that the globe draws arcs,
  • that the system view shows every source fresh,
  • that the analysis timer ran successfully in the last hour,
  • that model results exist in the store,
  • and, since 0.33, what the appliance would be reporting — the forecast.

The forecast answers the question the learning phase used to leave open: when the suppression lifts, what will this appliance report? It reads the suppressed results of the last 24 hours and counts, per model, how many carried a positive verdict.

Terminal window
ndr analysis forecast # every tenant, last 24 hours
ndr analysis forecast --tenant example --hours 72 # 1 to 168 hours
ndr analysis forecast --tenant example --json # the API's object

The same report is GET /api/v1/analysis/learning-forecast (reader role, the last 24 hours). It changes nothing; see the API reference for the shape. Since 0.38 both take a candidate model file — --model FILE on the command, ?model=<file> on the endpoint, a bare file name in the model directory — and join the counts with the candidate’s titles and severities, naming it in model_file; the counts stay the stored verdicts, and what a candidate would actually fire is the replay. Per model the forecast shows:

Field Meaning
suppressed_results how many results of the model were evaluated while suppressed in the window
would_fire how many of them carried a positive verdict — each would have been an incident candidate outside the window
devices over how many distinct devices
top_devices the five devices with the most verdicts, by name

The totals sum would_fire by model severity. Every model of the model file is listed, at zero when the window holds nothing for it.

What it cannot show. Results written before 0.33 carry score 0 and no verdict and are not counted; they cannot be backfilled. The first useful reading is therefore a day after the update — and only once the analyzer has caught up, if it was behind: a backlog produces results with the timestamps of the events, not of the run. A model still at suppressed_results: 0 after that is one whose devices have not been evaluated; for the peer-isolation model that is permanent and by design, as above.

How to read it. In the last days before a device population matures, read the forecast on three consecutive days and look at the top devices per model. A device that would fire the same model every hour is a baseline problem — a server whose weekday-hour baseline is simply noisy — not an incident, and the model’s threshold or the device’s grouping is what to adjust before the window ends, not after. The automatic response has its own calibration gate and is unaffected by anything the forecast shows.

What it said on the reference installation. On 2026-09-18, once the analyzer had caught up, the 24-hour forecast counted 119,519 suppressed results of which 3,052 would have fired, all of them High: 2,873 from periodic_destination_v1 across 92 devices — infrastructure pollers, a NAS, IoT — and 179 from destination_scan_v1 across 17, mostly the gateway, the hypervisors and monitoring. The medium models produced nothing. Without tuning, the first day after 2026-09-24 would have opened hundreds of High incidents on devices doing their jobs on a timer. That reading is what 0.38’s detection tuning answers, and the section below is how to use it.

The operator’s target for the day a device population matures is under ten High candidates a day, with true beaconing still detectable. 0.38 ships four things towards it — stricter beaconing and scan thresholds, a learned baseline of expected periodic destinations, role policies for infrastructure, and a daily incident cap per device — and one tool that says what they do on this network: the replay. In order:

1. Deploy 0.38 at least three days before the phase ends. The baseline needs baseline_minimum_days (three) of the 0.38 analyzer to learn a series before the device matures; on the reference installation, where the devices of the 2026-09-10 cutover mature on 2026-09-24, that means by 2026-09-21. Deployed later, the first days after maturity fire on series the analyzer has not had time to learn — the replay’s known series says how many it has. 0.38 carries no ClickHouse migration; ndr migrate adds one SQLite table for the overflow folds, and the update restarts ndr-api and the analyzer as always.

2. Run the replay, read the numbers. Read-only, bounded, safe on a production appliance:

Terminal window
# the configured file, the last three days
ndr analysis replay --tenant example --days 3
# a candidate beside it
ndr analysis replay --tenant example --days 3 --model /etc/ndr/models/candidate.yaml

Check three things: known series in the header is above zero (the baseline has been learning), WOULD FIRE of the High models is under ten a day, and the DROPPED columns show which step is doing the work — a step at zero did nothing on this network. The replay page explains every column.

3. Tag the pollers. The top devices say who is left. A device that keeps firing one series is one incident that will update itself; leave it. A device with many series is an infrastructure poller the roles do not know yet: tag it infra in the inventory, or give it its NetBox role, and it takes the infrastructure role at the next pass — its scans are never an incident, and its periodic targets fall silent once they have been periodic on three days. A whole class of devices tripping one threshold is a threshold to move in a candidate file, replayed until the number is right, then made the file in force.

4. Read it for three consecutive days, the last one the day before the phase ends. The numbers of one day can be a quiet Sunday; three in a row are the network.

What to expect after. A series that ran through the learning phase is expected and silent. A new periodic series on a mature device opens one incident and updates it. A device that opens more than five incidents on a day gets a sixth, Further findings beyond the daily cap, whose summary says “and N more” and lists the models. Infrastructure, by role, never opens a scan incident, and its new periodic targets fall silent after three days. Nothing of this touches the automatic response’s own gate.

Because a week is not enough to know a week. The baselines are keyed on hour-of-week — a device’s Tuesday 03:00 is a different bucket from its Sunday 19:00 — and eight comparable samples for a given bucket cannot exist until several weeks have passed for the rarer buckets.

Fourteen days is the floor at which the daily patterns are established and the weekly ones are beginning to be. The configurable range goes to 28 for installations that would rather be quiet longer.

Not by synthetic data. Not by a fixture. Not by importing someone else’s baseline. The value of the baseline is that it is yours — a camera that uploads nightly in your network is normal precisely because it does so in your network.

An operational acceptance for the whole appliance is therefore a two-to-four week activity that runs in parallel with the technical checks, and it is measured by the thing that actually matters: is the output useful, against the false-positive rate.

Expect a burst — the forecast tells you roughly how big, and the replay what the file in force makes of it. When the suppression lifts, the first genuinely new things start scoring, and some of them will be wrong. Use false-positive feedback — it records tenant-scoped human judgement and feeds the triage proposals a person then reviews. Nothing retrains itself behind your back.