Once the log grows past ~3x the account count it is rewritten to one
AccountRegistered per account (churned cert events dropped), via a temp
file and rename. Cert replay is now idempotent, so a snapshot plus
re-delivered events converge and a peer syncing from a compacted node
still gets every account. Runs on a 30m timer.
A broadcast channel forwards each freshly committed entry straight to
connected peers, so a registration propagates in milliseconds. The
periodic digest drops to a 10s anti-entropy backstop.
Nodes run anti-entropy over a TCP link: each advertises its version
vector, the peer replies with the log entries it lacks, and ingest is
idempotent so re-delivery and reconnect after a split both converge.
The engine is shared behind a mutex; config gains [gossip] and [[peer]].
Entries carry a Lamport timestamp alongside origin/seq. EventLog keeps a
per-origin version vector and ingests peer entries idempotently.
Db::ingest folds them in. No transport yet.
Turn the PLAIN verification exchange into a full account login:
- Advertise the mechanism list to the uplink as `saslmechlist` network
metadata, so clients see `sasl=PLAIN` in CAP LS (SASL 3.2).
- On a valid response set the client's `accountname` metadata (drives
RPL_LOGGEDIN 900) before reporting success (D S, drives 903); a bad
credential or unknown mechanism reports failure (D F, drives 904).
- Reassemble the base64 response from the uplink's 400-byte AUTHENTICATE
chunks, ending on a short chunk or a lone "+", with an upper bound to
cap a pre-auth client's buffer.
- Log the user in on NickServ REGISTER and IDENTIFY too, through the same
accountname metadata, and return the canonical account name from the
store so the login keeps its registered casing.
Verified against irctest server_tests/sasl.py (PLAIN success/failure,
non-ASCII, no-authzid, chunked payloads, unknown mechanism, too-long,
retry) on InspIRCd + fedserv.
Accounts are an append-only JSONL event log (state is the fold of the log) with
argon2id-hashed passwords. NickServ gains REGISTER <password> [email] and
IDENTIFY <password>; the engine tracks UID->nick from the burst so a command
resolves to the sender's current nick. Classic and ircd-agnostic — the cap-based
account-registration forward is the next step.