Apply from the laptop¶
nerthus-config apply is the one command that changes the estate's resources: it deletes a DNS record nobody declares, it turns a project's job logs private, it protects a branch. This runbook is the bootstrap way of running it — from the operator's own machine, with the two scoped tokens — and it is the only way there is until the site's worker takes over.
In steady state this is not the path. The estate has two (ruling 49): --from laptop at bootstrap, and --from site afterwards, where the site's worker instance applies on a merged change to Nerthus.Infra. The second is not built yet: --from site parses and refuses.
Before you start¶
You need three things, and the command refuses without each of them.
- A machine that carries no role.
--from laptopreadsinventory/hosts.yamland compares this machine's hostname and fully qualified name with every declared host. A match that carries any role is refused by name: a laptop is aclient(ruling 31), and a host the fleet converges has a different writer. - The two scoped tokens in this instance's own store, under the names the inventory gives them:
CLOUDFLARE_API_TOKENandGITLAB_API_TOKEN. They are sops values thatnerthus secrets setwrites andnerthus-configreads; nothing prints them back. Mint the Cloudflare token says what the first one carries. - A clean checkout of
Nerthus.Infra, onmain, up to date. The command cuts a branch of its own and returns you to the one you were on; it commits nothing butstate/.
The procedure¶
Read the plan first. It is the same plan the merge request shows and the same one the apply recomputes a minute later, so nothing is carried out that a person has not read:
Then the dry run, narrowed to what you mean to change. --only takes a kind, a kind and an id, or a prefix:
Then the apply itself. It asks before it acts; --yes answers, and --json never prompts at all:
What happens, in order: a fresh plan against live, the four refusals below, the plan printed, your confirmation, then one provider call per row in the plan's own order — creates and updates first, deletes last in reverse. Every call carries one request id, printed with the run, so a log can be joined to it afterwards.
What it refuses, and why¶
| Refusal | The rule behind it |
|---|---|
| this machine is a declared host with a role | ruling 49: a laptop is a client, and a host's writer is the site's worker |
| the declaration has findings | a plan from a declaration validate refuses is a plan about an estate nobody declared |
the plan carries anything unknown |
ruling 47: a plan with a hole in it is not one to act from; --only narrows to what a provider here can read |
the plan carries a blocked row |
something other than apply brings such a resource into existence — a host somebody rents, an instance nerthus init makes |
The first three are measured before any provider is asked at all.
What it leaves behind¶
state/ is what the tool last observed or made, and this command is one of its three writers (ruling 47). It writes the tree, commits it on a branch named apply/<utc>, and pushes it with GitLab's own merge_request.create push option — no API call, no second credential. You merge that merge request; the tool never pushes to main.
A remote that does not take push options still gets the branch: the push is retried without them and the answer says no merge request was opened. The estate has already been changed by then, and a record that refused to leave the machine would be the worse failure of the two.
When a row is refused mid-plan¶
The run stops at the first refusal and says so: how many rows were carried out, which one was refused and why, and which rows nobody attempted. What was done stays done — an estate is not a transaction — so the state tree it writes equals the prefix that really happened, and the next plan shows the remainder.
Read the refusal before re-running. A provider refuses for reasons that are usually about order: a tunnel whose DNS record still points at it is refused by name until the record goes first.
--from site, and when this page stops being the path¶
When it is built, the site's worker instance runs the same command with --from site: it applies on a merged change to Nerthus.Infra, through the job queue, with the tokens as ordinary vault credentials of that instance. This page stays as the bootstrap procedure and as what you do when the site is down.