# Configuration reference Every configuration block and field echoIRCd understands. For the format itself and a quick start, see [Configuration](/docs/configuration). Most options re-apply on `echoircd rehash` (SIGHUP) with no restart; the TLS certificate is re-read on rehash too. ## Format echoIRCd config files use a **block format**: `section { field value; field value; … }`. - **One field per `;`** terminator; a block may span multiple lines. - **Comments**: `#` and `//` for line comments, `/* … */` for block comments. An inline `#` on an *active* line is treated as part of the value unless the value is quoted — quote values that contain `#`. - **Booleans** are `yes` / `no`. A bare field with no value (e.g. `operprefix;`) enables it. - **Quoting**: quote any value containing spaces or `#`. - A legacy flat `key = value` format is also accepted; blocks are translated to flat keys internally, so the two are exactly equivalent. **Structural blocks** (`server`, `listen`, `tls`, `cloak`, `brand`, `oper`, `opertype`, `class`, `link`, `webirc`, `motd`, `opermotd`) map their fields to specific keys. **Grouping blocks** (`limits`, `timeouts`, `flood`, `connections`, `channels`, `users`, `opers`, `logging`, `modules`, `dns`, `antiabuse`, `restrictions`, `accounts`, `verification`, `reputation`, `securitygroups`, `websocket`, `sts`, `services`, `dnsbl`, …) are cosmetic — each field inside is simply a flat key, so you could equally place it at top level. Password fields accept plaintext, `sha256:`, `pbkdf2:…`, or a bcrypt `$2b$` hash. Generate one with `printf '%s' 'pw' | ./echoircd mkpasswd`. ## server Server identity (not repeatable). | Field | Description | | --- | --- | | `name` | This server's unique name on the network | | `network` | Network name shown to clients | | `sid` | 3-char server ID for S2S (a digit + two alphanumerics) | | `description` (`desc`) | Shown in LINKS / WHOIS 312 | | `pidfile` | PID file so `echoircd rehash` / `kill -HUP` can find the process (omit to disable) | ## listen A bound socket. **Repeatable.** `ip "*"` / `"[::]"` binds IPv4+IPv6 dual-stack. | Field | Description | | --- | --- | | `ip` | Bind address (`0.0.0.0`, `[::]`, `*`, `127.0.0.1`, …) | | `port` | Bind port | | `type` | `client` (default) or `server` (S2S) | | `tls` | `yes` = direct-TLS listener | | `wss` | `yes` = WebSocket-over-TLS (browser clients) | | `ws` | `yes` = plaintext WebSocket | ## tls TLS certificate (re-read on rehash). | Field | Description | | --- | --- | | `cert` | Path to the certificate PEM | | `key` | Path to the private-key PEM | | `backend` | `openssl` (default) or `rustls` (pure-Rust, no system OpenSSL) | | `sni` | Per-host cert `" "` — **repeatable** | | `handshake_timeout` | Seconds before a stalled TLS handshake is dropped | ## brand Per-SNI branding — one daemon presenting multiple identities. A client that connected via this TLS SNI host sees this `servername`/`network` (welcome burst, ISUPPORT `NETWORK=`, numeric source) instead of the globals. Needs TLS+SNI. **Repeatable.** | Field | Description | | --- | --- | | `host` | The TLS SNI hostname this brand applies to | | `servername` | Server name shown on this host | | `network` | Network name shown on this host | ## motd / opermotd Lines of the MOTD (shown on connect) and the oper MOTD (`/OPERMOTD`). Each quoted line is one entry; **repeatable**. ## oper An oper account. **Repeatable.** A block with neither password nor fingerprint is refused. | Field | Description | | --- | --- | | `name` | Oper account name | | `password` | Auth secret (plaintext / `sha256:` / `pbkdf2:` / bcrypt) | | `fingerprint` (`fp`, `certfp`) | Require this TLS client-cert SHA-256 fingerprint (2FA, or alone) | | `type` | References an `opertype` (role); no type = full access | | `level` | Operlevel (KILL-protection tier) | ## class A reusable capability class that opertypes compose. **Repeatable.** | Field | Description | | --- | --- | | `name` | Class name | | `commands` | Commands granted (`*` = all, `-TOKEN` removes one) | | `privs` | Privileges granted (`*` = all, `-token` removes one) | | `snomasks` | Snomask letters granted | | `usermodes` | Oper-only user modes the type may set (letters, or `*`) | | `chanmodes` | Oper-only channel modes the type may set (letters, or `*`) | ## opertype A named oper role (WHOIS "is a ``"). **Repeatable.** Ships five: `helpop`, `globop`, `admin`, `servadmin`, `netadmin` (only netadmin holds every privilege). | Field | Description | | --- | --- | | `name` | Opertype name (referenced by `oper.type`) | | `classes` | Space-separated capability classes to compose | | `modes` | User modes auto-applied on oper-up | | `title` | WHOIS title | | `level` | Operlevel for this type | ## link A trusted S2S peer. **Repeatable.** | Field | Description | | --- | --- | | `name` | Peer server name | | `ip` | Peer address | | `port` | Peer port | | `password` | Shared link secret | | `autoconnect` | Dial the peer on boot / after a netsplit (`no` default) | | `services` (`uline`) | Mark the peer as a U-lined services server | ## services The linked server that handles SASL (client `AUTHENTICATE` is relayed to it). Unset = SASL disabled. | Field | Description | | --- | --- | | `sasl_server` | Server name that handles SASL | ## webirc Trusted web gateways (kiwiirc / CGI:IRC) that send `WEBIRC` with the real client's host+ip. **Repeatable.** | Field | Description | | --- | --- | | `password` | Shared WEBIRC secret | | `name` (`gateway`) | Gateway name | | `mask` (`ipmask`) | Restrict which source IP may use the password | ## cloak Host cloaking for user mode `+x`. | Field | Description | | --- | --- | | `key` | Long random hex secret (keep private; changing it re-cloaks everyone) | | `method` | `hmac-sha256` (default) / `account` / `fingerprint` / `static` — **ordered, first match wins** | | `account_prefix` | Prefix for the `account` method (default `account`) | | `cert_prefix` | Prefix for the `fingerprint` method (default `cert`) | | `static_host` (`static`) | Host shown for the `static` method | ## dns Reverse-DNS and ident behaviour. | Field | Description | | --- | --- | | `resolve_hosts` | Reverse-DNS on connect (default `yes`; `no` = bare IP) | | `use_resolved_host` | Use a resolved name in the hostmask (default `yes`) | | `useident` | Look up every client's ident (RFC 1413) | | `requireident` | Refuse clients whose ident can't be confirmed | | `ident_timeout` | Seconds to wait for the ident reply (default `5`) | ## dnsbl A DNS blocklist checked on connect. **Repeatable.** | Field | Description | | --- | --- | | `dnsbl` | Blocklist zone, or a full per-list string `"domain=<zone> name=<label> action=<a> duration=<d> reason=<r>"` | | `dnsbl_action` | `mark` (default) / `kill` / `kline` / `gline` / `zline` | | `dnsbl_reason` | Reason string | | `dnsbl_duration` | Ban seconds for a *line action (default `86400`) | ## limits Advertised in ISUPPORT and enforced. | Field | Default | Description | | --- | --- | --- | | `maxnick` | 30 | Nick length (NICKLEN) | | `maxchannel` | 50 | Channels a user may join | | `maxbans` | 100 | Ban-list entries per channel | | `maxinvites` | 100 | Pending invites per user | | `modes` | 20 | Mode changes per MODE line | | `max_line` | 16384 | Max bytes per line / recvq | | `max_sendq` | 1048576 | Queued output before a slow client is dropped | | `whowas_maxentries` | 256 | Historical nick records retained | | `maxwatch` | 128 | WATCH entries per user | | `maxmonitor` | 128 | MONITOR entries per user | | `maxsilence` | 32 | SILENCE entries per user | | `maxaccept` | 64 | `/ACCEPT` (caller-id) entries per user | | `maxsignore` | 64 | Server-ignore entries per user | | `metadata_maxkeys` | 32 | IRCv3 METADATA keys per target | | `metadata_maxvalue` | 512 | METADATA value length | | `multiline_maxbytes` | 4096 | draft/multiline max bytes | | `multiline_maxlines` | 24 | draft/multiline max lines | | `chathistory_limit` | 256 | CHATHISTORY messages kept per conversation | | `chathistory_maxage` | 604800 | Max age (s) a client may request | | `event_playback` | yes | Record JOIN/PART/QUIT/NICK/MODE/TOPIC/KICK into history | | `webpush` | yes | draft/webpush master switch (RFC 8291/8292) | | `webpush_away_only` | yes | Only push to away users | | `webpush_ttl` | 259200 | Push TTL (s) | | `dccallow_maxentries` | 20 | `/DCCALLOW` entries per user | | `http_max_concurrent` | 32 | In-flight outbound HTTP requests (API modules) | `webpush_sub`, `webpush_vapid_file`, `webpush_database` tune the Web Push key/store paths. ## timeouts | Field | Default | Description | | --- | --- | --- | | `registration_timeout` | 60 | Drop clients that never register | | `ping_frequency` | 90 | Idle time before a PING is sent | | `ping_timeout` | 60 | Then drop if no PONG within this long | | `slow_command_ms` | 200 | Snotice when one core event takes ≥ this (0 = off) | ## flood | Field | Default | Description | | --- | --- | --- | | `flood_messages` | 8 | Messages per window before dropping (opers exempt) | | `flood_seconds` | 4 | The flood window | | `joinflood_duration` | 60 | `+j` default window | | `nickflood_duration` | 60 | `+F` default window | | `connflood` | "5 10" | Refuse > N connections per S seconds from one IP | | `blockamsg` | yes | Block mass `/amsg` and `/ame` | | `blockamsg_delay` | 3 | Same text to a different target list within N s = block | | `blockamsg_action` | block | `block` / `kill` / `gline` / `kline` / `zline` | | `blockamsg_duration` | 900 | Ban seconds for a *line action | ## connections | Field | Default | Description | | --- | --- | --- | | `conn_waitpong` | yes | Hold registration until the client echoes a PING cookie | | `conn_waitpong_killonbadreply` | yes | Drop on a wrong pong | | `conn_waitpong_exempt_localhost4/6` | yes | Exempt loopback | | `connectban` | yes | Z-line an IP range that opens too many connections | | `connectban_threshold` | 10 | Connections from a range before it's banned | | `connectban_duration` | 21600 | Ban seconds | | `connectban_bootwait` | 120 | Grace after startup (reconnect storm) | | `connectban_ipv4cidr` / `connectban_ipv6cidr` | 32 / 128 | Range width for counting | | `connectban_exempt` | — | Never ban this glob/CIDR (**repeatable**) | | `accept_rate` / `accept_burst` | 0 | Max new connections/sec per source IP at the accept edge | | `proxy` | — | Trust the PROXY header from these sources (**repeatable**) | ## classes Per-class connection policy. `connectclass` is **repeatable**; each is a single quoted string (first match wins). Tokens: `allow=<mask[,mask]>`, `deny=yes`, `parent=<name>`, `requiressl=yes|trusted`, `password=<pw>`, `hash=<algo>`, `port=<p[,p]>`, `asn=<n[,n]>`, `localmax=<n>`, `globalmax=<n>`, `limit=<n>`, `maxchans=<n>`, `pingfreq=<s>`, `timeout=<s>`, `modes=<+modes>`, `recvq=<bytes>`, `softsendq=<bytes>`, `hardsendq=<bytes>`, `fakelag=no`, `penaltythreshold=<n>`, `commandrate=<s>`, `useident=yes`, `requireident=yes`, `resolvehostnames=no`, `maxconnwarn=yes`. `connectclass_required yes` refuses clients that match no allow class. ## sts Strict Transport Security. Off unless `sts_duration > 0`. | Field | Description | | --- | --- | | `sts_duration` | Seconds clients should stick to TLS (`0` = off) | | `sts_port` | The TLS port to upgrade to | | `sts_preload` | Advertise preload eligibility | ## opers | Field | Default | Description | | --- | --- | --- | | `operprefix` | yes | Give every oper a `!` prefix (mode `y`, above owner) | | `ojoin` / `ojoin_op` | yes | `/OJOIN` as network staff, optionally opped | | `oper_svslogin` | yes | Allow services to SVSLOGIN opers | | `maphide` | yes | Hide LINKS / MAP from non-opers | | `hideservices` | yes | Hide U-lined services from MAP/LINKS/STATS | | `hidewhois` | yes | Hide sensitive WHOIS lines from ordinary users | `hidewhois_*` sub-keys tune exactly which WHOIS lines (server, idle, secure) are hidden and whether opers/self see everything. ## channelvis Restrict who sees a mode change or list mode, by rank. **Repeatable.** | Field | Description | | --- | --- | | `hidemode` | Hide a mode change below a rank: `"<mode> <rank>"` | | `hidelist` | Only this rank+ may view a list mode: `"<mode> <rank>"` | ## channels | Field | Description | | --- | --- | | `announce_channels` (`chancreate`) | Snotice opers when a new channel is created | | `channames_deny` | Forbid these chars in new channel names | | `permchannels_database` | `+P` permanent-channel store path | | `markread_database` | draft/read-marker store path | | `restrictchans` | Only opers may **create** channels | | `restrictchan` | Glob whitelist ordinary users may create (**repeatable**) | | `badchan` | Forbid joining a glob: `"<glob> reason=… redirect=… allowopers=yes"` | | `goodchan` | Whitelist back out of a broad `badchan` (**repeatable**) | ## users On-connect behaviour. | Field | Description | | --- | --- | | `connbanner` | NOTICE sent at connect (**repeatable**) | | `conn_umodes` (`autoumodes`) | User modes auto-set on every client | | `autojoin` (`conn_join`) | Channels every client auto-joins (**repeatable**) | | `seenicks` | Snotice opers on every nick change | | `opermodes` (`oper_umodes`) | Extra user modes set on oper-up | | `operjoin` | Channels an oper auto-joins on oper-up (**repeatable**) | | `vhost` | Self-service vhost: `"<user> <pass> <host>"` (**repeatable**) | ## accounts Bridges IRCv3 draft/account-registration to an HTTP backend. | Field | Description | | --- | --- | | `account_registration` | Master switch (default `no`) | | `acctregister_registerurl` / `acctregister_verifyurl` | REGISTER / VERIFY endpoints | | `acctregister_apikey` | Sent as `X-API-Key` | | `acctregister_autologin` / `acctregister_beforeconnect` / `acctregister_emailrequired` / `acctregister_requiretls` | Policy toggles | | `acctregister_ratecount` / `acctregister_ratetime` | Rate limit per IP | ## verification Anti-bot gates (reCAPTCHA / Cloudflare challenge). Keys: `recaptcha`, `recaptcha_secret`, `recaptcha_url`, `recaptcha_issuer`, `recaptcha_ttl`, `recaptcha_message`, `recaptcha_whitelistports` (repeatable) — and the matching `cloudflare_*` set. ## antiabuse | Field | Description | | --- | --- | | `antirandom` | Score random-looking nick/ident/realname (drones) | | `antirandom_threshold` / `antirandom_checkfull` / `antirandom_action` / `antirandom_duration` / `antirandom_reason` / `antirandom_showfailed` | Tuning + action | | `hashident` / `hashident_key` | Replace ident with a stable opaque per-IP token (HMAC key) | | `locale` / `locale_dir` | Server-wide language (`en` default; `fr`/`es` ship); rehash to switch | | `solvemsg` | Unvouched users answer one arithmetic question before PMs deliver | | `antimixedutf8` / `amu_threshold` / `amu_minlen` / `amu_action` / `amu_target` / `amu_reason` | Block look-alike mixed-script spam | | `badword` | `+G` censor word `"<find> [replacement]"` (**repeatable**) | ## restrictions | Field | Description | | --- | --- | | `restrictmsg` | Only opers/services may be PM'd by ordinary users | | `disabled_commands` | Refuse these commands to ordinary users (**repeatable**) | | `restrictcommand` | Per-command: `"<CMD> connectdelay=<s> exemptidentified=yes exempttls=no reason=…"` | | `securelist` + `securelist_*` | Delay `/LIST` for new connections (defeats list-spam bots) | | `autodrop_commands` | Silently drop a pre-registration client that sends any of these (HTTP scanners) | ## reputation Per-address scoring plus the `y:` score extban. | Field | Description | | --- | --- | | `reputation_database` | Store path | | `reputation_minchanmembers` | Only bump if in a channel this big | | `reputation_scorecap` | Maximum score | | `reputation_whois` | Who sees the score: `all` / `opers` / `self` / `none` | | `reputationexpire` | Decay rule `"<score> <age>"` (**repeatable**) | ## securitygroups Named groups usable as an extban (`+b g:<name>`). `securitygroup` is **repeatable**, each a quoted string: `"<name> <criteria…>"` where criteria include `public`, `tls`, `insecure`, `account`, `unregistered`, `oper`, `exclude-oper`, `bot`, `webirc`, `mask=<glob>`, `exclude=<glob>`, `scoremin=<n>`, `scoremax=<n>`, `asn=<n[,n]>`. ## logging | Field | Description | | --- | --- | | `syslog` / `syslog_target` / `syslog_facility` / `syslog_tag` | Mirror the log stream to syslog (Unix socket or `host:port` UDP) | | `log_json` | Append the log stream to a file as JSONL | | `snoop_stderr` | Also echo server-notices to stderr | | `metrics_bind` | OpenMetrics/Prometheus scrape endpoint (bind privately), e.g. `"127.0.0.1:9109"` | | `chanlog` | Mirror oper snotices into a channel, optionally filtered by snomask letters (**repeatable**) | ## modules Extra features toggled here. | Field | Description | | --- | --- | | `abbreviation` | A unique command prefix resolves to its command (WHOI→WHOIS) | | `alias` | Command alias `"<ALIAS> <target>"`, e.g. `"NS NickServ"` (**repeatable**) | | `customprefix` | Reconfigure or add channel-prefix tiers | | `customtitle` | `/TITLE <name> <pass>` grants a WHOIS title (+ optional vhost) | | `randquote` | Greet each connecting user with a random line (**repeatable**) | | `showfile` | Serve a text file as its own command: `"<CMD> <path>"` | | `filter_engine` | Oper `/FILTER` pattern engine: `glob` (default) or `regex` | | `geoip_database` / `geoip_asn_database` | MaxMind `.mmdb` for country/city and ASN (enables `G:`/`asn=` and WHOIS geo) | | `network_icon` | Advertise a network icon (draft/ICON) | | `relaymsg_separators` / `relaymsg_ident` / `relaymsg_host` | draft/relaymsg (`/RELAYMSG`) for bridges | | `extjwt_secret` / `extjwt_duration` / `extjwt_chunk` / `extjwt_service` | `/EXTJWT` token issuance | | `filehost_*` | Advertise a file-upload service + issue upload tokens | | `dccallow_blockfile` / `dccallow_blockchat` | Block unwanted DCC unless `/DCCALLOW +<nick>` | | `http_tls_verify` | Verify upstream TLS certs for the HTTP client (API modules) | ## websocket Relevant only with a `ws`/`wss` listener. | Field | Default | Description | | --- | --- | --- | | `ws_origin` | — | Allowed Origin globs (**repeatable**; empty = any) | | `ws_proxyranges` | — | Proxies whose `X-Real-IP`/XFF we trust (**repeatable**) | | `ws_trust_proxy` | no | Trust those headers from ANY peer | | `ws_allowmissingorigin` | yes | Allow clients that send no Origin | | `ws_defaultmode` | text | Frame mode with no subprotocol: `text` / `binary` / `reject` | | `ws_nativeping` | yes | Liveness via WebSocket pings (else IRC PING) | | `ws_handshake_timeout` | 10 | Seconds to complete the HTTP Upgrade | | `ws_ping_interval` | 60 | Seconds between keepalive pings | | `ws_timeout` | 120 | Drop after this many seconds of silence |