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.
The numbers
Section titled “The numbers”| 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.
What happens during it
Section titled “What happens during it”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
Section titled “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.
ndr analysis forecast # every tenant, last 24 hoursndr analysis forecast --tenant example --hours 72 # 1 to 168 hoursndr analysis forecast --tenant example --json # the API's objectThe 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.
Before the learning phase ends
Section titled “Before the learning phase ends”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:
# the configured file, the last three daysndr analysis replay --tenant example --days 3# a candidate beside itndr analysis replay --tenant example --days 3 --model /etc/ndr/models/candidate.yamlCheck 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.
Why 14 days minimum
Section titled “Why 14 days minimum”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.
What it cannot be replaced with
Section titled “What it cannot be replaced with”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.
After it
Section titled “After it”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.