Nerthus.Config¶
Configuration management for the estate: what should exist, what does exist, what differs. One
package, nerthus-config, with a Python shell and a Rust core, downstream of Nerthus.Platform.
Why it exists¶
So that what the estate should be is declared in git, what it is gets observed and recorded, and every difference reaches a person. It is a plan engine with a state file, providers that can list what exists, and hosts that report facts to a site that can tell a person.
What it owns¶
- Declarations against state against live. A plan on every merge request, a drift run every hour, a hardening grade every night. Drift is a finding and never a fix.
- Resources above the VM, by API. Cloudflare, GitLab and R2, each behind one provider trait with
a
list(), so what nobody declared is a plan output rather than an absence, and a403on a listing isunknownrather than "no such resource". It never creates a VM: that is a runbook step. - Hosts, by pull. A static Rust agent,
nerthus-config-agent, fetches the declaration repository, applies its own role's cookbook, decrypts its own secrets and reports facts. A role is a cookbook; a cookbook is an ordered list of typed steps, each with acheckthat reads and anapplythat writes, with one arm per OS family where the families differ. - Four OS families from the first cookbook: Debian, Ubuntu, Alpine, AlmaLinux.
- Hardening against CIS, with two instruments — its own checks as steps, and a scanner — where a disagreement between them is a finding rather than a tie broken silently.
What it does not own¶
Credentials, instances, events, jobs and orchestration stay Nerthus.Platform's: a domain rule exists once. Config registers minter plugins with Platform's secrets store rather than holding its own. It has no HTTP surface and no dashboard of its own; drift and hardening reach a person through Platform's alert path.
Where its declarations live¶
Not here. Nerthus.Infra holds the inventory, the
per-site values, the package pins per family, the hardening catalogue, the vault ciphertext, and
state/. It holds zero code, and state/ is written only by apply, import and drift, on
branch namespaces of their own; a hand branch that touches it is refused by CI. A hand edit to state
is a claim about the estate that no read supports.
Where its CI runs¶
On the estate's runners, through $NERTHUS_RUNNER_TAG, on Nerthus.Platform's CI image by digest —
building a second toolchain would be a second answer to which compiler measured a commit. Its jobs:
lint in both languages, pytest and cargo test with coverage floors, the contract diff, the
regime digest, the programme's Task: trailer and merge-request-title gates, the pin scanner, the
protected-ref lint, and two cross-compiled wheels. One job publishes, on a protected tag alone.
What it pins, and what pins it¶
It pins Nerthus.Platform's distribution to a minor line, and nerthus-config doctor refuses a
nerthus outside it: a client that does not match its site's minor is refused entirely rather than
run against. It pins the interpreter to a patch release, the Rust channel to a version, and every
image it names by digest.
Nothing pins Nerthus.Config yet. When hosts run it, what a host pins is the agent digest and the Config version, and the agent refuses to move backwards on either.
What breaks if it stops¶
Nothing converges and nothing is graded. A host keeps running what it was last converged to — git is
the backup of host configuration, so everything except identities and WireGuard keys is rebuildable
from Nerthus.Infra — and it stops being told when it has drifted, which is the failure the previous
tooling had for its whole life.
Its reference¶
docs/resources.md
in the repository lists every resource kind, generated from its contracts.
See also¶
- The ecosystem map - where it sits among the rest
- Read a plan - what its output means
- The working regime - the rules its merge requests answer to