IRC services for InspIRCd networks, written in Rust. NickServ, ChanServ, and a dozen more, backed by an append-only event log instead of a database. https://echo.dev.tronic.pro
Find a file
Jean f46662db90
gossip: deterministic winner for a concurrently-registered account
Two nodes registering the same name before gossip converges no longer
end up disagreeing. Each account records its home node, and the merge
keeps the earliest registration (by timestamp, then home node), so every
node folds the log to the same owner regardless of delivery order. The
merge is commutative and idempotent.
2026-07-12 13:11:22 +00:00
modules nickserv: IDENTIFY reports an unregistered account distinctly 2026-07-12 12:38:09 +00:00
scripts Add mutual-TLS to the gossip link 2026-07-12 07:31:16 +00:00
src gossip: deterministic winner for a concurrently-registered account 2026-07-12 13:11:22 +00:00
testing Add SASL SCRAM-SHA-256 and SCRAM-SHA-512 2026-07-12 03:59:24 +00:00
.gitignore chanserv: op, deop, voice, devoice, kick, ban, unban 2026-07-12 11:21:35 +00:00
Cargo.lock Add mutual-TLS to the gossip link 2026-07-12 07:31:16 +00:00
Cargo.toml Add mutual-TLS to the gossip link 2026-07-12 07:31:16 +00:00
config.example.toml Refresh README and config example 2026-07-12 07:31:16 +00:00
README.md Reorganize protocol and pseudoclients into a modules/ tree 2026-07-12 10:37:59 +00:00

fedserv

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 derived from any project's source.

Design

The core lives in src/; the pluggable parts live in modules/ (see modules/README.md).

  • src/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.
  • src/gossip.rs node-to-node replication over the logs.
  • modules/protocol/ 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.
  • modules/nickserv/, modules/chanserv/ the pseudo-clients, one directory each. OperServ and others follow the same shape.

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

[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

cp config.example.toml config.toml   # edit uplink + link password
cargo run -- config.toml

Status

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.