Skip to content

Configure a Platform instance

Every Nerthus.Platform instance, whatever roles it runs, reads its settings from one file, one set of NERTHUS_* environment variables and the command's flags, in one order, through one module. This page is every key there is and how an instance resolves it.

flags  >  NERTHUS_*  >  nerthus.toml  >  built-in defaults

The order applies per key, not per file: a file that sets database.port and an environment that sets database.url give you both. nerthus config show --effective prints each resolved value beside the source that answered it, which is where every configuration question starts.

Which file is read

The first of these that exists:

  1. --config <path> on the command. A path that does not exist is an error, not a fall-back.
  2. NERTHUS_CONFIG, with the same rule.
  3. ./nerthus.toml in the working directory.
  4. <config dir>/nerthus.toml, where the configuration directory is --config-dir, then NERTHUS_CONFIG_DIR, then ~/.config/nerthus.

A clone's own file beats the one in the configuration directory. No file at all is supported: a container can configure itself from NERTHUS_* alone.

Make one

nerthus init --site local

writes ~/.config/nerthus/nerthus.toml, creates the data directory and the instance's age identity. Running it again writes nothing and says so; change a setting by editing the file.

Flag Default What it does
--site <name> required The site's display name. The id beside it is a fresh UUIDv7.
--config-dir <path> ~/.config/nerthus Where nerthus.toml and the age identity live.
--data-dir <path> ~/.local/share/nerthus The instance's own directory.
--env prod\|dev\|staging dev The site's environment.
--kind site\|client\|isolated site What this instance is.
--roles <list> none written Roles to write into the file.
--json off Print the result as JSON.

The keys

A key the schema does not declare is refused, and the message names it and the nearest key that does exist.

Key Type Default Environment Meaning
data_dir path ~/.local/share/nerthus NERTHUS_DATA_DIR The cluster, the corpus clones, everything the instance owns.
roles list or string none NERTHUS_ROLES Roles this process runs; "all" expands to every one. No default: an instance told nothing refuses to serve.
site.id UUIDv7 minted by init NERTHUS_SITE_ID What events, grants, rulings and audit rows name.
site.name string unset NERTHUS_SITE_NAME A display name. May change; the id never does.
site.env prod\|dev\|staging dev NERTHUS_SITE_ENV The site's environment.
site.image image reference unset NERTHUS_SITE_IMAGE The image this site runs. Required at prod; see below.
instance.kind site\|client\|isolated site NERTHUS_INSTANCE_KIND See Instance kinds.
database.url string unset NERTHUS_DATABASE_URL Set only when the site uses a Postgres it does not run; the bundled server is then never started.
database.port integer 5432 NERTHUS_DB_PORT The port the bundled server binds.
database.bind list or comma string ["127.0.0.1"] NERTHUS_DB_BIND Addresses, or interface names ("wg0") resolved on the host.
listen.host string 0.0.0.0 NERTHUS_LISTEN_HOST Where the read role answers. Loopback would make a container unreachable.
listen.port integer 8080 NERTHUS_LISTEN_PORT The read role's port.
backup.endpoint URL unset NERTHUS_BACKUP_ENDPOINT The S3-compatible endpoint backups go to. Unset means this site takes none.
backup.bucket string unset NERTHUS_BACKUP_BUCKET The bucket in it.
backup.region string auto NERTHUS_BACKUP_REGION auto is R2's own name; another S3-compatible store wants a region name.
backup.prefix string empty NERTHUS_BACKUP_PREFIX Everything this site writes lives under it, so one bucket may hold several sites.
backup.retain_days integer 0 NERTHUS_BACKUP_RETAIN_DAYS How many days of backups a retention pass keeps. 0 deletes nothing.
backup.r2_budget_bytes integer 6442450944 (6 GiB) NERTHUS_BACKUP_R2_BUDGET_BYTES What this site may keep in the bucket. A backup that would pass it is refused. 0 means no budget.
corpora.<key>.path path unset NERTHUS_CORPUS_<KEY>_PATH The clone this instance reads.
corpora.<key>.remote string unset NERTHUS_CORPUS_<KEY>_REMOTE The remote it converges against.

Corpora

A corpus <key> is one of lore, metadata, moderation, transcripts, annotations, works; anything else is refused. Everything about a corpus that is the same on every site (its kind, whether and how the write role publishes to it, the credential that reaches it, its fetch cadence, its pair, and what a reader gets when the site holds no clone) is in the corpora manifest carried in the package. A site's file says only where each clone is.

A path is a clone adopted where it is. A remote with no path is a clone the instance owns, under <data_dir>/corpora/<key>. Neither means the instance does not carry that corpus. lore and metadata are a pair: both remotes must name one pair of repositories, the same group and the same suffix after the repozytorium-<name> stem, or the settings refuse to load.

nerthus corpus add lore --path ../repozytorium-fabularne-prodkopia
nerthus corpus list          # the manifest beside this site's tables
nerthus corpus status        # each clone's reached commit, whether it is dirty, why it is absent

corpus add refuses, before the file changes, anything the load would refuse.

The backup credential is not a key

backup.endpoint and backup.bucket are facts. The access key pair lives in the instance's sops store as R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY, and nowhere else: no environment variable, no configuration key. Back up and restore has the command that puts it there.

What a production site must pin

site.env = "prod" refuses a site.image that is not pinned by digest, and refuses a tag of main or latest even with a digest: an image is pinned only when a release tag built it, and production never tracks main.

[site]
env = "prod"
image = "registry.gitlab.com/margonem/nerthus/it/nerthus.platform:v0.3.0@sha256:0123…"

Read it back

nerthus config show              # the file as written
nerthus config show --effective  # the resolved values, each with the source that won it
nerthus config show --effective --json

--effective also reports, on stderr, any NERTHUS_* variable in the environment that the package reads nowhere. A mistyped variable (NERTHUS_DB_PORTT) does nothing at all, so it is reported; it is not refused, because containers and CI jobs carry NERTHUS_* names the package does not own.

Instance kinds

instance.kind is configuration, never inferred from the roles.

Kind What it is What it may not do
site An instance carrying roles for a shared site.
client Somebody's machine: a full local site that subscribes to a site. Hold write or load for the site it subscribes to; be pushed into.
isolated A client that subscribes to nothing: it clones corpora with its owner's own read access, derives locally, runs live, and exports drafts somebody else submits. Publish, subscribe or command. nerthus site enrol refuses it, and the write role over a corpus that has a remote is refused when the settings load.

Reserved roles

live and bridge are in the role vocabulary and carry no behaviour yet. An instance holding one starts and answers /health, and the answer names it:

{"status": "ok", "roles": ["read", "live"], "reserved": ["live"], "kind": "site", }

nerthus doctor prints the same two facts: the instance kind, and which of its roles are reserved.