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 801bfc5c96
BotServ: fantasy commands in-channel
A message beginning with ! in a channel that has a bot assigned is a
fantasy command: !op, !kick, !topic and the rest route through ChanServ's
existing handlers (the word becomes the command, the channel its first
argument), so access checks and behaviour are reused verbatim. The
resulting modes, kicks and replies are re-sourced from the bot, so the
bot is the visible actor.
2026-07-13 14:22:49 +00:00
api BotServ: add SAY and ACT 2026-07-13 14:15:20 +00:00
botserv BotServ: add SAY and ACT 2026-07-13 14:15:20 +00:00
chanserv chanserv: channel SUSPEND / UNSUSPEND (oper-gated) 2026-07-13 04:17:17 +00:00
example modules: [modules] config + example service crate + SDK guide 2026-07-13 01:48:09 +00:00
inspircd botserv: ASSIGN / UNASSIGN a bot to a channel 2026-07-13 13:42:14 +00:00
memoserv Split BotServ and MemoServ into one-file-per-command modules 2026-07-13 14:01:59 +00:00
nickserv memoserv: complete + improve the memo service 2026-07-13 04:50:27 +00:00
proto grpc: full account-authority write API (Register through UngroupNick) 2026-07-12 23:57:55 +00:00
scripts Add mutual-TLS to the gossip link 2026-07-12 07:31:16 +00:00
src BotServ: fantasy commands in-channel 2026-07-13 14:22:49 +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
build.rs grpc: directory replication API for websites to mirror accounts/channels 2026-07-12 23:10:21 +00:00
Cargo.lock memoserv: complete + improve the memo service 2026-07-13 04:50:27 +00:00
Cargo.toml memoserv: complete + improve the memo service 2026-07-13 04:50:27 +00:00
config.example.toml engine: services-operator privilege foundation 2026-07-13 03:42:59 +00:00
MODULES.md modules: [modules] config + example service crate + SDK guide 2026-07-13 01:48:09 +00:00
README.md modules: [modules] config + example service crate + SDK guide 2026-07-13 01:48:09 +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 are separate crates in a Cargo workspace, each depending only on the fedserv-api SDK crate.

  • api/ (fedserv-api) the module SDK: the Service, Protocol, Store and NetView traits a module implements or is handed, plus the normalized NetEvent/NetAction vocabulary and the read views. It has no storage or runtime dependencies, so a third-party module builds against it alone.
  • src/engine/ the services engine: live network state, the account store, and the log. The store is event-sourced: every change is an Event appended to a per-node log, and state is a fold over that log. A service touches it only through the Store/NetView traits, so the log, gossip and credential material stay out of a module's reach.
  • src/gossip.rs node-to-node replication over the logs.
  • inspircd/ (fedserv-inspircd) the ircd link layer. A Protocol impl maps raw server-to-server lines to and from the normalized model, so the engine never touches a raw line and a new ircd is one new crate.
  • nickserv/, chanserv/ (fedserv-nickserv, fedserv-chanserv) the pseudo-clients, each a crate implementing Service. OperServ and others follow the same shape.

Writing your own module — a service or a new ircd protocol — is documented in MODULES.md; example/ is a minimal service to copy from.

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.

Directory API (gRPC)

src/grpc.rs exposes the account/channel directory over gRPC (see proto/fedserv.proto) so a website can mirror it without touching IRC: a one-shot Snapshot for the initial load, then Subscribe for a live stream of every change from that point on. It subscribes to the same committed-entry channel gossip does, so a change reaches a subscriber in the same push, no polling. Deliberately excludes anything credential-shaped (password hashes, SCRAM verifiers, cert fingerprints) and the finer channel-ops-list events (access/akick/mlock/entrymsg) — identity and metadata only. Bearer-token authenticated, optional server TLS. Omit [grpc] in config.toml to run without it.

The same port also serves Accounts, the write side: Register, Authenticate, SetPassword, SetEmail, Confirm, Drop, ForceLogout, GroupNick, UngroupNick — a trusted caller (e.g. a website's own backend, already having done its own login/session check) managing accounts the same way an IRC user does through NickServ, minus the command syntax. The bearer token is the authorization: unlike the NickServ commands these mirror, most calls do not re-check the account's own password (an admin-level override, the same trust model a JSON-RPC integration to another services package would use) — Register and Authenticate are the two exceptions, since the password is the actual input there. A write committed this way replicates to every other fedserv node exactly like an IRC-originated one does — gossip doesn't know or care where it came from.

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) and ChanServ over an event-sourced account store replicated between nodes by gossip, with an optional mutually authenticated TLS peer link, plus a gRPC directory API for websites to mirror the account/channel directory (bearer-token authenticated, optional TLS).