From d30f5c05a678122ae113a1aeaf28fd997ad6eb90 Mon Sep 17 00:00:00 2001 From: Jean Date: Sun, 12 Jul 2026 07:31:16 +0000 Subject: [PATCH] Refresh README and config example Document the event-sourced store, gossip replication, push-on-commit, compaction and the TLS peer link; drop the stale persistence status. --- README.md | 60 +++++++++++++++++++++++++++++++++++++-------- config.example.toml | 14 +++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f02a720..bdc14c4 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,56 @@ A federated IRC services daemon in Rust. Protocol-agnostic core, InspIRCd link first. -Clean-room — inspired by the ideas behind modern event-log/gossip services, not +Clean-room, inspired by the ideas behind modern event-log/gossip services, not derived from any project's source. ## Design -- **`proto/`** — the ircd link layer. A `Protocol` trait maps raw server-to-server - lines to/from a normalized `NetEvent`/`NetAction` model. The engine never touches - a raw line, so a new ircd = one new module. InspIRCd is the first. -- **`engine/`** — the services engine: live network `state`, an `EventLog` (every - persistent change is an `Event`; state is a fold over the log — this is what makes - replicating across nodes a later addition, not a rewrite), and the `Service` trait. -- **`services/`** — the pseudo-clients: NickServ first, then ChanServ / OperServ / … +- **`proto/`** the ircd link layer. A `Protocol` trait maps raw server-to-server + lines to and from a normalized `NetEvent`/`NetAction` model, so the engine never + touches a raw line and a new ircd is one new module. InspIRCd is the first. +- **`engine/`** the services engine: live network `state`, the account store, and + the `Service` trait. The store is event-sourced: every change is an `Event` + appended to a per-node log, and state is a fold over that log. +- **`gossip/`** node-to-node replication over the logs. +- **`services/`** the pseudo-clients: NickServ first, then ChanServ / OperServ. + +## Replication + +Each log entry carries an `origin`, a per-origin `seq`, and a Lamport clock. Peers +connect, exchange version vectors, and send each other the entries the other +lacks; a freshly committed entry is also pushed immediately, so a registration +propagates in milliseconds. Ingest is idempotent, so re-delivery and reconnect +after a split both converge. The log is compacted to one entry per account as it +grows. An account registered on any node works on all of them. + +## Config + +```toml +[uplink] +host = "127.0.0.1" +port = 7000 +password = "linkpw" + +[server] +name = "services.example" +sid = "42S" + +[gossip] # omit to run a single, non-replicating node +bind = "0.0.0.0:16700" +secret = "shared-secret" + +[gossip.tls] # omit for a plaintext link +cert = "certs/nodeA.crt" +key = "certs/nodeA.key" +ca = "certs/ca.crt" # a peer must present a certificate signed by this CA + +[[peer]] +addr = "nodeB.example:16700" +name = "nodeB" # TLS name to expect; must match the peer certificate +``` + +Generate certificates with `scripts/gen-certs.sh`. ## Run @@ -24,5 +62,7 @@ cargo run -- config.toml ## Status -Early scaffold: links to an InspIRCd uplink and introduces NickServ. Burst/mode -handling and account persistence are next. +Links to an InspIRCd uplink and runs NickServ (REGISTER, IDENTIFY, LOGOUT, CERT, +and SASL PLAIN / SCRAM-SHA-256/512 / EXTERNAL) over an event-sourced account store +replicated between nodes by gossip, with an optional mutually authenticated TLS +peer link. Next: ChanServ. diff --git a/config.example.toml b/config.example.toml index 10e55b5..a82c583 100644 --- a/config.example.toml +++ b/config.example.toml @@ -10,3 +10,17 @@ name = "services.example.net" sid = "42S" # 3 chars, unique on the network description = "Federated Services" protocol = 1206 # InspIRCd link protocol version (1206 = insp4, 1205 = insp3) + +# Node-to-node replication. Omit this section (and [[peer]]) to run a single node. +# [gossip] +# bind = "0.0.0.0:16700" # where peers reach us; omit for a dial-only node +# secret = "shared-secret" # both nodes must present the same secret +# +# [gossip.tls] # omit for a plaintext link; see scripts/gen-certs.sh +# cert = "certs/node.crt" +# key = "certs/node.key" +# ca = "certs/ca.crt" # a peer must present a certificate signed by this CA +# +# [[peer]] # one block per other node +# addr = "other-node:16700" +# name = "other-node" # TLS name to expect; must match the peer certificate