The moderator roster¶
Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: not yet written.
Who holds mc or smc, since when, and whether they can read a record at all.
The roster is not public, unlike the regulation corpus beside it. Which rules apply to everybody is a public fact; who enforces them is not, and the file carries published encryption recipients as well as names.
Routes¶
| Method | Path | Cmdlet | Cap | Write |
|---|---|---|---|---|
| GET | /moderators |
— | moderation.read |
— |
The one row carried a cmdlet name that named nothing. Get-NerthusModerator is defined in no .ps1 in Nerthus.Core and exported by no manifest. The cell is empty now, following the regulations page's precedent for a browser-only family, and ci/assert-cmdlet-names.py in this repository fails the build if it comes back.
Replica-safe and poolable, with one caveat that no other replica row has: the corpus is a second
working tree. Two hosts agree only when both clones sit on the same commit, so the answer carries
corpus and a stale clone is a question a caller must be able to ask. The read itself is still a
pure function of the bytes.
What the answer says about itself¶
{
"count": 8,
"corpus": "present", // null = this host has no moderators clone at all
"items": [
{ "name": "Vinnie", "margonemid": "8420046", "status": "Aktywny",
"roles": ["smc", "mc"], "has_key": true }
],
"findings": []
}
corpus: null and an empty items are different statements, and collapsing them is the
failure this field exists to prevent. A host with no moderators clone answers every moderation
question with an empty roster — 04-daemon.md calls that present and unreachable, and it is
preferred to a host that will not boot — but an empty roster read as "nobody moderates" would let
a caller conclude that no sanction has an author.
has_key, not the key. Whether somebody can be encrypted to is a roster question; the
published age recipient itself is a keyring question and is answered by the keyring routes.
Keys on a block¶
A moderator block carries up to three credential tags, each parsed through the contributor grammar rather than a second one — a person who appears in both rosters must have one key accepted in both:
@klucz_szyfr— the publishedagerecipient a new epoch is wrapped to.@klucz_podpis— anssh-ed25519public signing key, with an optional(od:do)range. A record signature verifies against the ring's published signing keys (keyrings), so a member without one still reads and has every record they submit refused.@klucz_api— asha256:<64 hex>hash with an optional(od:do)range. The key authenticates its holder as an API principal, and for a moderator who is in no other roster it is the only way in: the margonem exchange refuses them withno_gracz_for_margonem_id.
Findings¶
| kind | severity | when |
|---|---|---|
ModeratorWithoutMargonemId |
Error | a block with no @margonemid |
ModeratorWithoutRole |
Error | no @rola, so no bundle can be resolved |
ModeratorUnknownRole |
Error | @rola is neither mc nor smc |
InvertedRange |
Error | a range ending before it begins |
RawSigningSecret |
Error | @klucz_podpis holds a PRIVATE KEY — the pair is burned |
MalformedSigningKey |
Error | @klucz_podpis is not an ssh-ed25519 public key with an optional (od:do) range |
RawApiKeyValue |
Error | a raw nak_ value committed instead of its sha256: hash — the key is burned |
MalformedApiKey |
Error | @klucz_api is not sha256:<64 hex> with an optional (od:do) range |
ModeratorWithoutKey |
Warning | active, no @klucz_szyfr — cannot read any record body |
ModeratorNotAContributor |
Warning | the id resolves to nobody in nerthus.contributors.md |
ModeratorWithoutMargonemId is an Error here where the contributor store tolerates a missing
id, and the asymmetry is the point: a sanction has to be attributable to a person who can be asked
about it. The block is still returned and still resolvable by name, because every row of the
imported ledger names its moderator by display name and nothing else — an unattributable record is
bad, an authorless one is worse.
ModeratorNotAContributor is a Warning deliberately. A chat moderator is not required to be a
player, and treating that as a fault would refuse a legitimate appointment.
Every failed credential parse is an Error, because a dropped credential is a silent lockout
that reads as a typo. RawApiKeyValue and MalformedApiKey also travel into the contributor
model's findings, so a burned or unreadable key surfaces where the contributor store is read.
Tenure¶
@rola carries the estate's standard (VALID_FROM:VALID_TO) range. Standing down closes the
range; it never deletes the line, because every record that person issued would otherwise lose its
author — which is exactly what a contested sanction asks about.
Two consequences worth stating:
- A role with no range holds at any time. No appointment date is attested for any current
moderator, so demanding one would make every record they have ever written a finding. The
authority check reports why it answered yes, so
in-tenureandno-tenure-recordedstay distinguishable. - Two tenures need not meet. An office can genuinely stand vacant, so a gap between one
moderator's
VALID_TOand the next'sVALID_FROMis not a finding — unlike the regulation chain, where a gap means a day with no rule in force. A record dated inside a roster gap isRecordOutsideTenureagainst everybody, which is the correct answer.