Read a finding¶
Everything the estate judges about itself — a plan against what is live, a host's CIS grade, a package pin against its archive — produces conditions that used to live only in the log of the job that printed them. A finding is one of those conditions written down: it has an identity, it is opened on a commit, it is closed on a commit, and it reaches a person.
nerthus-config findings . # every open finding, with the populations
nerthus-config findings . --scope nerthus-host-2 # one host, provider or family
nerthus-config findings . --new-since <commit> # what is new; exit 2 when anything is
nerthus-config --json findings . # the same, as data
The ledger is state/findings/<scope>.yaml in Nerthus.Infra, one file per host, provider or OS
family. It is state, so the tool writes it and a person does not: a change to it arrives on an
apply/*, import/* or drift/* branch and the pipeline refuses it on any other.
What a finding is called¶
A condition is identified by three things — its kind, its scope and its subject — and
its id is those three hashed together with the commit it was first seen on. Nothing in the
identity is a clock, so the same judgement over the same estate produces the same ids on any
machine and at any hour.
That last part of the hash is what makes a regression visible. When a condition stops being
observed it is closed with a row, not deleted; if it comes back it opens again with a different
id, because the second occurrence is its own event. A ledger that reused the first id would let an
alert path stay silent about a problem that had returned.
| Field | What it holds |
|---|---|
id |
the identity and the opening commit, hashed |
kind |
one of the seven below |
scope |
the host, provider or family; it is the file's name |
subject |
the control, resource, package or host inside that scope |
first_seen_commit |
the commit the condition was first observed on |
last_seen_commit |
the most recent commit it was still observed on |
closed_commit |
the commit it stopped being observed on; empty while it is open |
detail |
the sentence a person reads; it may change without the finding changing |
The seven kinds, and what to do first¶
| Kind | What it means | What to do first |
|---|---|---|
drift |
a resource live differs from what Nerthus.Infra declares | Read the plan (nerthus-config plan --against live). Either the declaration is behind — land it — or somebody changed a resource by hand, and apply puts it back. Drift is a finding and never a fix. |
hardening |
a host does not hold a control of its profile | Open the grade: nerthus-config hardening report <host>. Fix it unless fixing it breaks the role, in which case it becomes an exemption with a reason, an expiry and the measurement that shows the breakage. |
scan_disagree |
the two hardening instruments contradict each other about one control | This is the one the second instrument exists for. Neither reading is trusted over the other: read both, decide which is wrong, and fix the cookbook step or the control's scanner rule in the catalogue. |
exemption_expired |
an exemption is past its date, or never had one | Either the control is now fixable — fix it and delete the exemption — or the reason still holds, and the exemption is renewed with a new expiry at most ninety days out. An exemption with no date is refused by the same rule. |
pin_moved |
an archive no longer serves a package at its pinned version | Read what the archive serves now and move the pin by merge request. A pin never moves by itself, so this finding stays open until somebody decides. |
provider_error |
a provider could not be read, so its population is unknown | This is not "nothing is wrong there": it is "nobody knows". Check the credential and the provider's status, and re-run the plan. Until it reads, every conclusion about that provider's resources is suspended. |
agent_stale |
a host's facts are older than three of its own ticks | The agent is not reporting. Reach the host through the admin door and look at the timer; a host that has never reported at all is a different condition and is not stale. |
How a finding reaches you¶
Two arms, and one of them needs nothing else in the estate to be working.
The scheduled pipeline. Nerthus.Infra runs nerthus-config findings --new-since on a schedule.
When the run finds something that was not open at the commit it compares against, it exits 2, the
job fails, and GitLab mails the schedule's owner. There is no notifier to keep working: the delivery
is the exit code. A run with nothing new is green and silent, which is why the comparison is
against a commit and not against zero — an estate with a hundred known-open findings is the ordinary
state, and a job that reddened on it would be red for ever.
Discord. Findings are also formatted as one message per batch, every row carrying its finding
id, for #szary-monitoring through Platform's bot. The message is cut at Discord's limit and says
how many rows it left out. This arm is built and has no caller yet: the route it speaks to belongs
to the bot, which does not exist. Until it does, the scheduled mail is the whole path.
The dead-man on frog01 is neither of these and is described in Read a dead-man alert: it curls the site from outside and posts to a Discord webhook directly, so an estate where both the site and the bot are down still has a voice.
What writes a finding¶
The judgement that observed a condition is the one that records it, so no second reading of the same archives can disagree with the first.
nerthus-config pins check . --record # pin_moved, one file per family
nerthus-config hardening report <host> --record # hardening, scan_disagree, exemption_expired
A run records only inside the scopes and kinds it actually judged. A pins check of four families says nothing about a host's hardening and may not close a hardening finding; a scope that was judged and found clean gets an empty ledger file, which is a different answer from a scope nobody looked at.
drift, provider_error and agent_stale have no recorder yet: they arrive with the hourly drift
run and the host facts route, and until then those three kinds are defined, tested and never
written.