Skip to content

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:

  • logi is 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.
  • mc is 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.
  • lingw is 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