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. |
||
|---|---|---|
| modules | ||
| scripts | ||
| src | ||
| testing | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| config.example.toml | ||
| README.md | ||
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 networkstate, the account store, and theServicetrait. The store is event-sourced: every change is anEventappended to a per-node log, and state is a fold over that log.src/gossip.rsnode-to-node replication over the logs.modules/protocol/the ircd link layer. AProtocoltrait maps raw server-to-server lines to and from a normalizedNetEvent/NetActionmodel, 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.