readme: polished front-page layout — badges, about, grouped features, quick start, links

This commit is contained in:
Jean Chevronnet 2026-08-10 12:16:08 +00:00
parent 75de56f532
commit 02b2061561

267
README.md
View file

@ -1,189 +1,124 @@
<div align="center">
# echoIRCd
A from-scratch IRC daemon written in Rust. Commands are objects, modes are
handler objects, and modules hook lifecycle events. Design goals:
`#![forbid(unsafe_code)]`, dependency-light (just two small crates — `openssl`
for TLS and `mio` for the epoll socket engine), and lock-free (a single core
thread owns all state).
**A from-scratch, memory-safe IRCv3 server written in Rust.**
> Status: capable and broad. It speaks a large slice of the IRC + IRCv3 protocol
> — **100+ commands**, the full channel/user mode set, **28 IRCv3 capabilities**,
> and **~55 pluggable modules** — with server-to-server linking, a services
> interface, TLS, WebSocket, GeoIP and a JSON-RPC control plane. One reactor thread
> has served 5,000 concurrent connections in testing. Not battle-tested yet.
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](Cargo.toml)
[![Language: Rust](https://img.shields.io/badge/rust-stable-orange.svg)](https://www.rust-lang.org)
[![unsafe: forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](src/lib.rs)
[![IRCv3](https://img.shields.io/badge/IRCv3-supported-blueviolet.svg)](https://ircv3.net)
[![dependencies: 2](https://img.shields.io/badge/dependencies-openssl%20%2B%20mio-lightgrey.svg)](Cargo.toml)
## Run it
</div>
## About
echoIRCd is a full IRC + IRCv3 server built from the ground up in safe Rust
(`#![forbid(unsafe_code)]`) with just two dependencies — `openssl` for TLS and
`mio` for the socket engine. A single lock-free core thread owns all state while
one epoll reactor drives tens of thousands of connections without an async
runtime. It ships **100+ commands**, the **complete channel & user mode set**,
**28 IRCv3 capabilities**, server-to-server linking, a services interface, TLS,
WebSocket, GeoIP, layered anti-spam, and a JSON-RPC control plane — with every
operational limit configurable and nothing hardcoded.
## Features
- **Full IRC core** — registration, channels (`JOIN`/`PART`/`KICK`/`INVITE`/
`KNOCK`/`CYCLE`/`REMOVE`/`TOPIC`), messaging (`PRIVMSG`/`NOTICE`/`TAGMSG`, CTCP),
and info (`WHO`/`WHOIS`/`WHOWAS`/`LIST`/`STATS`/`MAP`/`LUSERS`/`MOTD`).
- **Complete mode set** — prefixes `qaohv` (+ a network-staff `!` prefix), list
modes `beIgXw`, keyed/limit/flood/redirect/history/anticaps params, the full flag
set, all the standard user modes, and matching + acting **extbans**
(`g y r j s G b`, `m c n`).
- **IRCv3** — 28 capabilities including `message-tags`+`msgid`, `server-time`,
`labeled-response`, `batch`, `echo-message`, `account-tag`, **CHATHISTORY**,
**multiline**, **message-redaction**, **read-marker**, **relaymsg**, and
`WATCH`/`MONITOR`/`SILENCE`/callerid.
- **Operators**`OPER`/`KILL`/`WALLOPS`/`GLOBOPS`, the `SA*`/`CHG*`/`SET*`
override toolbox, x-lines (`K`/`G`/`Z`/`E`/`SHUN`/`QLINE`/`CBAN`) persisted to
disk, staff prefix (`operprefix`/`OJOIN`), rank-gated `hidelist`/`hidemode`, and
a reload-safe `REHASH`.
- **Services & accounts** — SASL PLAIN/EXTERNAL relayed over S2S, the `SVS*` /
`ENCAP` / `METADATA` interface, account-gated modes, and optional ircd-side
account registration.
- **Server-to-server linking**`UID`/`FJOIN` netburst, cross-server users and
channels, multi-hop routing, nick-collision handling and clean netsplit.
- **Security & anti-spam** — TLS with cert fingerprints, keyed host cloaking,
DNSBL, connection/message flood limits, mixed-script & gibberish detection,
CAPTCHA / PONG-cookie / arithmetic gates, and DCC filtering.
- **GeoIP** — a native MaxMind `.mmdb` reader with a `G:<cc>` geoban, `GEOIP`
command, and WHOIS country line.
- **Transports & control** — a native WebSocket layer (`ws://` / `wss://`), a
from-scratch forward-confirmed DNS resolver, and a token-authenticated JSON-RPC
control plane over HTTP.
## Quick start
```sh
cp echoircd.conf.example echoircd.conf # then edit: oper pass, cloak_key, TLS paths
cargo run --release # reads ./echoircd.conf
# point a client at it, e.g. /server 127.0.0.1 6667
git clone https://git.devtronic.pro/fedserv/echoIRCd
cd echoIRCd
cp echoircd.conf.example echoircd.conf # edit: oper pass, cloak_key, TLS paths
cargo run --release # reads ./echoircd.conf
```
Config is plain `key = value` (see `echoircd.conf.example`). Your real
`echoircd.conf` is gitignored because it holds secrets (oper password, cloak
key, link password) — never commit it. For TLS, generate a cert/key into `tls/`
(the example config has the one-liner).
Then point a client at it: `/server 127.0.0.1 6667` (or `6697` for TLS once a
certificate is configured).
## Configuration
Configuration is a plain `key = value` file; see
[`echoircd.conf.example`](echoircd.conf.example) for the full, documented set of
keys. Your live `echoircd.conf` is gitignored — it holds secrets (oper password,
cloak key, link password), so never commit it. Generate a TLS certificate into
`tls/` with the one-liner in the example config.
## Architecture
A single **core thread** owns every `User` and `Channel`, so command and module
code is plain single-threaded logic over `&mut Server` — no `Arc<Mutex<…>>`
anywhere. The I/O edge feeds it events over mpsc channels:
code is ordinary single-threaded logic over `&mut Server` — no `Arc<Mutex<…>>`
anywhere. The I/O edge feeds it events over channels:
- **Client connections run on one `mio` epoll reactor thread.** The daemon drives
tens of thousands of sockets without a thread per connection — measured at 5,000
concurrent clients on **4 threads total**, and it scales toward ~50k (use a
release build and a high `LimitNOFILE`). It's a bare epoll/kqueue readiness
reactor — no async runtime is pulled in, so the single-threaded core is
untouched.
- **TLS and server links** keep a thread per connection — there are few of them,
and a TLS session can't be split across reader/writer threads.
- **One `mio` epoll reactor** drives all client sockets — measured at 5,000
concurrent clients on 4 threads total, scaling toward ~50k with a release build
and a high `LimitNOFILE`.
- **TLS sessions and server links** run a thread each; both hand the core the same
`OutSink`, so it never knows which transport a connection uses.
Both models hand the core the same `OutSink`, so it never knows or cares which one
a connection uses.
**Why a raw reactor and not async?** IRC is one large shared mutable graph, and
almost every command mutates it and then broadcasts. With one thread owning all of
it, handlers are plain synchronous code — no locks, no `.await`, no `Send + 'static`
bounds. A multi-threaded async runtime would force that shared state behind mutexes
or an actor mailbox, and a channel broadcast is serialized anyway, so you'd pay for
parallelism the workload can't use. `mio` is the same readiness layer async runtimes
build on, so you keep the scaling without the runtime; blocking work (DNS, TLS,
HTTP) is offloaded to its own threads.
Memory-safety by design: `Uid` handles instead of raw pointers (no use-after-free,
no cull list), an `Extensible` typemap instead of `void*` module data (freed
automatically on drop), `&str` slices, and compiled-in trait objects instead of a
fragile `.so` ABI.
Memory safety is structural: `Uid` handles instead of raw pointers, an `Extensible`
typemap instead of `void*` module data (freed on drop), and compiled-in trait
objects instead of a fragile plugin ABI.
### Why a raw reactor, not async?
## Extending
IRC is one big shared mutable graph (users, channels, the nick index), and almost
every command mutates it and then broadcasts. With one thread owning all of it,
handlers are plain `&mut Server` code — no locks, no `.await`, no `Send + 'static`
bounds. A multi-threaded async runtime would force that shared state behind
mutexes or an actor mailbox, and a channel broadcast is serialized anyway, so
you'd pay locking cost for parallelism the workload can't use. `mio` is the same
readiness layer async runtimes are built on, so you keep the C50k scaling without
the runtime. CPU-heavy or blocking work (DNS, TLS, outbound HTTP) is pushed to its
own threads; the network scales out by **linking servers**, not by adding cores to
one process.
Three small extension points, each one file + one table line:
### The two extension points
- **Commands** (`src/command.rs`, `src/coremods/`) — a handler with `name`,
`min_params`, `before_reg`, `handle(&mut Server, uid, params)`.
- **Modes** (`src/mode.rs`) — channel/user modes as `ChanMode` / `UserMode`
handler objects; adding one never touches the parser.
- **Modules** (`src/module.rs`, `src/modules/`) — lifecycle hooks; pre-hooks can
**Deny** a register/command/message, notify-hooks fire after.
- **Commands** (`src/command.rs`, `src/coremods/`) — a handler declares `name`,
`min_params`, `before_reg` and `handle(&mut Server, uid, params)`, registered
in `command_table()`. Adding a command is one struct + one table line.
- **Modes** (`src/mode.rs`) — channel/user modes are handler objects
(`ChanMode` / `UserMode`) in a table; adding a mode never touches the parser.
- **Modules** (`src/module.rs`, `src/modules/`) — lifecycle hooks. *Pre-hooks*
(`on_user_register`, `on_pre_command`, `on_pre_message`) return a `ModResult`
and can **Deny**; *notify-hooks* fire from a queue after the command.
## Links
## What works
### Registration & session
- `CAP` negotiation, `NICK`/`USER`, `PING`/`PONG` with configurable idle +
registration timeouts, welcome burst (001005) + ISUPPORT (optionally batched).
- `WEBIRC` (real client IP from a trusted web gateway), `conn_waitpong` (require a
PONG cookie before registering — filters bots), `autodrop` (silently drop
pre-registration HTTP scanners), configurable nick/channel length limits.
### Channels
- `JOIN`/`PART`/`NAMES`/`TOPIC`/`KICK`/`INVITE`, plus `KNOCK`, `CYCLE`, `REMOVE`,
`UNINVITE`.
- Bans / excepts / invex, ban **redirect** (`+b mask$#chan`), and matching +
acting **extbans** (below).
### Messaging
- `PRIVMSG`/`NOTICE`/`TAGMSG`, CTCP handling, `echo-message`, per-message `msgid`,
`server-time`, `account-tag`.
- **CHATHISTORY** (`draft/chathistory`: `LATEST`/`BEFORE`/`AFTER`/`AROUND`/`BETWEEN`
/`TARGETS`) with the `+H` join backlog, **REDACT** (`draft/message-redaction`),
**MARKREAD** (`draft/read-marker`), and **multiline** (`draft/multiline`).
- **RELAYMSG** (`draft/relaymsg`) — bridge messages under a spoofed relay nick.
### The full mode set
- **Prefixes** `+qaohv` (`~&@%+`), plus an optional network-staff prefix `+y` (`!`)
above owner (`operprefix` / `OJOIN`).
- **List modes** `+b` ban, `+e` except, `+I` invex, `+g` word filter, `+X`
exemptchanops, `+w` auto-status.
- **Parametered** `+k` key, `+l` limit, `+f` message-flood, `+j` join-flood, `+F`
nick-flood, `+L` redirect-when-full, `+H` history, `+B` anticaps, `+J`
kick-no-rejoin, `+d` delay-msg, `+K` no-repeat.
- **Flags** `+imnpstz`, `+O` oper-only, `+N` no-nick, `+C` no-CTCP, `+T` no-notice,
`+c` no-colour, `+S` strip-colour, `+R` reg-only, `+M` reg-moderated, `+G` censor,
`+u` auditorium, `+Q` no-kicks, `+A` allow-invite, `+P` permanent, `+U`
op-moderated, `+D` delay-join.
- **User modes** `+i w o x s g` plus `+B` bot, `+D` deaf, `+I` hide-chans, `+H`
hide-oper, `+r` logged-in, `+R` reg-only-PM, `+z` TLS-only-PM, `+W` show-whois,
`+h` helpop, `+c` common-chans-only.
- **Extbans** — matching `g:` security-group, `y:` reputation, `r:` realname,
`j:` in-channel, `s:` server, `G:` country, `b:` other-channel's ban list; acting
`m:` mute, `c:` no-colour, `n:` no-nick.
### Operators
- `OPER`/`KILL`/`WALLOPS`/`GLOBOPS`, snomasks (`+s`) with an optional `chanlog`.
- Overrides: `SAJOIN`/`SAPART`/`SANICK`/`SAMODE`/`SATOPIC`/`SAKICK`/`SAQUIT`,
`CHGHOST`/`CHGIDENT`/`CHGNAME`/`SETHOST`/`SETIDENT`/`SETIDLE`, `NICKLOCK`/
`NICKUNLOCK`, `SWHOIS`, `CHECK`, `CLEARCHAN`, `ALLTIME`, `OPERMOTD`, `VHOST`,
`TITLE`, oper-override-with-accountability, `operprefix`/`OJOIN`, `hidelist`/
`hidemode`.
- **X-lines** `KLINE`/`GLINE`/`ZLINE`/`ELINE`/`SHUN`/`QLINE`/`CBAN`, persisted to
disk and restored on boot; `STATS`; on-demand `CONNECT`; `DIE`/`RESTART`; and a
reload-safe **`REHASH`** (keeps the running config if the file can't be read).
### IRCv3 capabilities
`sasl`, `server-time`, `message-tags` (+ `msgid`), `multi-prefix`, `away-notify`,
`account-notify`, `extended-join`, `chghost`, `userhost-in-names`, `echo-message`,
`invite-notify`, `setname`, `extended-monitor`, `account-tag`, `standard-replies`,
`labeled-response`, `batch`, `cap-notify`, and drafts `chathistory`,
`message-redaction`, `pre-away`, `metadata-2`, `multiline`, `account-registration`,
`json-log`, `extended-isupport`, `relaymsg` — plus `WATCH`/`MONITOR`/`SILENCE` and
`ACCEPT` (+ `+g` callerid).
### Services interface & accounts
- **SASL** PLAIN + EXTERNAL (TLS client-cert), relayed to an external services
server over S2S.
- `SVSNICK`/`SVSJOIN`/`SVSPART`/`SVSMODE`/`SVSLOGIN`/`SVSLOGOUT`, `ENCAP`,
`METADATA`; account-gated modes (`+r`/`+R`/`+M`). The ircd is *ready* for an
external services package — it is not one itself.
- Optional ircd-side account **registration** (`REGISTER`/`VERIFY` over an HTTP
API), CAPTCHA gating, and `EXTJWT` / file-host tokens.
### Server-to-server linking
Handshake, `UID`/`FJOIN` netburst, cross-server users and channels, multi-hop
routing, nick-collision handling, and clean netsplit.
### Anti-abuse
`antimixedutf8` (look-alike script spam), `antirandom` (gibberish nicks), message
flood (`+f`) + global rate limits, `connflood`/`connectban` (connection floods),
`blockamsg`, `securelist`, `dnsbl`, CAPTCHA / challenge gating, `solvemsg`
(arithmetic gate), `dccallow` (DCC filtering), `conn_waitpong`, `autodrop`.
### GeoIP
A **native MaxMind `.mmdb` reader** (no crate): the `G:<cc>` geoban extban, a
`GEOIP` oper command, and a country line in WHOIS.
### TLS, cloaking, transports
- **TLS** (openssl) with `SSLINFO`, cert fingerprints, and `+z` secure-only.
- Keyed-SHA-256 host **cloaking** (`+x`).
- A **native WebSocket transport** (`ws://` and `wss://`) with real-IP / scheme
behind a trusted proxy.
- Reverse-DNS on connect via a from-scratch forward-confirmed PTR resolver over
UDP (no DNS crate), off the core thread, fail-safe to the IP.
### JSON-RPC control plane
A native inbound HTTP server + JSON-RPC interface (no `serde`, no `hyper`) with
token auth and ~30 methods across core/user/channel/server/stats/ban/message.
### Native building blocks
Everything is hand-rolled to stay dependency-light and `unsafe`-free: the DNS
resolver, JWT (HS256), JSON scan/build, the mmdb reader, an HTTP client, and
password hashing (`md5`/`sha1`/`sha256`/`sha512`/`pbkdf2`).
### Everything is configurable
Limits, thresholds, durations, timeouts and list sizes are all config keys — the
literal in code is only the default; nothing operational is hardcoded.
## Originality
echoIRCd is original Rust — no code is copied or translated from any other
project. `scripts/native-rust-guard.sh` enforces this (no `unsafe`, no C/FFI, and
dependencies limited to `openssl` + `mio`); it runs on every edit.
- **Repository**<https://git.devtronic.pro/fedserv/echoIRCd>
- **Issues**<https://git.devtronic.pro/fedserv/echoIRCd/issues>
- **Config reference** — [`echoircd.conf.example`](echoircd.conf.example)
## License
See the repository for licensing.
echoIRCd is released under the [MIT License](Cargo.toml). It is original Rust —
no code is copied or translated from any other project, enforced on every edit by
`scripts/native-rust-guard.sh` (no `unsafe`, no C/FFI, dependencies limited to
`openssl` + `mio`).