The corpora a host holds¶
Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: not yet written.
A fleet host serves more than the lore repository. This page is the operational half of that: which repositories a host clones, which settings key names each one, who may write it, and what a host does when one is absent.
The ecosystem map says what each corpus is for. This page says what the fleet does with it.
Six remotes, and one of them is not a corpus¶
Nerthus.Core.Infra.Common@7cb02cb schema/remotes.yml declares seven remotes. Six are git and
one is the container registry, so the number a host operator cares about is six git remotes: the
lore checkout and five corpora beside it.
Every remote is declared once, by shape rather than by value, because a corpus reaches a running host
through five layers that each name it in their own vocabulary - the provision descriptor, the agent's
field read, the agent's environment render, the compose passthrough, and the container entrypoint's
clone block. remotes.yml is where those names are joined.
| Remote | Settings key | Where the daemon reads it | Write | Absent means |
|---|---|---|---|---|
lore |
lore_repo_url |
/repo itself |
yes, by merge request | fatal - the entrypoint refuses to clone an empty /repo and the daemon has no tree to answer from |
mc |
mc_repo_url |
corpora.mc |
no | empty roster, and the regulation corpus falls back to the lore tree |
logi |
logi_repo_url |
corpora.logi |
yes, pushed directly | the host reads nerthus.logs/ from its lore checkout |
lingw |
lingw_repo_url |
corpora.lang |
no | the host reads nerthus.lang/ from its lore checkout |
dziel |
dziel_repo_url |
corpora.dziel |
no | the external search tier holds nothing |
adnotacje |
adnot_repo_url |
corpora.adnotacje |
no | the labelled-sample surface holds nothing |
registry |
- | not a tree | - | the ambient credential is used |
Absent is a supported state at every hop, and that is what lets a fleet migrate host by host
rather than all at once. Only lore is fatal.
The write bit is not uniform, and that is the part to get right¶
Every host's deploy key is minted per host, and which bit it carries depends on the remote:
logiis the one committed corpus this fleet writes. Every host that fetches a transcript writes it there and its sync tick pushes plain commits, so every host's key needs the write bit on that project.mcis read-only for the whole fleet. One account has write. The repository split exists so that merge rights on the lore repository do not reach the tariff a sanction is judged against, and that only holds while the two trees stay apart.lingwis read-only more strictly still. No host writes it at all: one CI job regenerates the whole tree and opens a merge request against it.
remotes.yml is where that asymmetry is declared, and Invoke-FleetProvision raises the bit it
names and says so loudly when it cannot.
Every corpus has a fetcher, and mc was the last to get one¶
A clone is not a refresh. The container clones a corpus only into an empty volume, so a
recreated container onto an existing volume takes no clone, and a checkout with nothing fetching it
serves what it was born with until somebody deletes the volume — with GET /health reading ok the
whole time. Each corpus therefore has a sync tick of its own on the serve loop.
mc was the exception until it was not, and the exemption was written into the guard that exists to
catch exactly this: the moderation corpus converges through the moderation path rather than the
sync loop. Nothing implemented that. No service reading the moderators tree calls git at all, so
the roster and the versioned regulations were frozen at clone time on every running host. It has a
tick now, GET /sync and /health's corpora report its freshness beside the other five, and the
guard has no exemption left.
It is the one read-only corpus a daemon writes into: the moderation record store, the evidence
blobs and the disclosure log live under <mc>/.nerthus/moderation, and nothing commits or pushes
them. The tick publishes nothing, so no moderation byte leaves the host. Where an incoming path
overlaps a locally dirty one the fast-forward refuses and the tick reports diverged with the
reason — a regulation update that cannot land because the host is sitting on unpublishable
moderation writes, which used to be silent.
A host announces what it takes up¶
Each fast-forward posts one line to the tech channel naming the host, the repository, the short sha
and the people who wrote the commits — the lore checkout and every corpus alike. One message per
host, so a host that stays quiet while its peers announce is the signal. The knobs are
integrations.discord.converge_announce.
What the dev fleet actually holds¶
Four of the six keys are filled, measured through GET /health on 2026-08-28 at 17:42 UTC:
| Host | mc |
logi |
lang |
dziel |
adnotacje |
|---|---|---|---|---|---|
dev-torneg |
present | present | present | not configured | not configured |
dev-evocation |
present | present | present | not configured | not configured |
dev-eder |
not measured |
dev-eder did not answer, and the reading is two hosts rather than three
Three requests at 17:42 UTC all returned 530 from the edge, which is Cloudflare saying it
could not reach the origin. dev-torneg and dev-evocation answered 200 in the same minute.
That belongs to the fleet rather than to this page - see
Tell which watcher is live and
Recover a diverged host.
dziel_repo_url and adnot_repo_url are unset on purpose, and the reason is an authorization
question rather than an oversight. Both corpora change what a caller holding only the ordinary read
capability receives: the labelled-sample store is served under that capability while the routes that
serve transcript speaker attribution cost a capability a player does not hold, and the works tree
feeds derived surfaces that carry no capability at all. Filling either key is one line and no code
change, which is exactly why leaving it unset is a decision somebody took.
Their volumes exist and mount empty, and every layer names that rather than failing on it: the
compose template reports the external tier as holding nothing, the entrypoint skips the clone
block, and each daemon tick returns before it builds a sync view. An operator reading an empty
works tier is reading the intended state.
What it costs¶
Every corpus is a clone on every host. The dev fleet's lore repository is a private copy of the campaign rather than a fixture, so the blast radius of a lost or compromised contributor laptop is the lore itself. Two things bound it and both have to stay true: every replica's deploy key is read-only, and the repository stays private. The moderation corpus carries the same note with more force, because it is the sanction record.
Re-read this before bootstrapping a new host.
See also¶
- The ecosystem map - what each corpus is, and why it was split out
fleet.settings.yml- every key, its bounds and its default- The fleet - the model these hosts converge under
- Configuration - the
corpora.*keys the daemon reads