# echoIRCd config — simple key = value (repeat `motd` for extra lines). # Copy this file to `echoircd.conf` and fill in your own values. # NOTE: echoircd.conf is gitignored because it holds secrets (oper password, # cloak key, link password). Never commit your real config. servername = irc.example.net network = ExampleNet bind = 0.0.0.0:6667 # TLS listener. Generate a cert/key first, e.g.: # openssl req -x509 -newkey rsa:2048 -keyout tls/key.pem -out tls/cert.pem \ # -days 3650 -nodes -subj "/CN=irc.example.net" bind_tls = 0.0.0.0:6697 tls_cert = ./tls/cert.pem tls_key = ./tls/key.pem motd = Welcome to echoIRCd — a from-scratch IRC daemon in Rust. motd = Edit the MOTD in your echoircd.conf. # --- server-to-server linking --- sid = 0AA serverdesc = echoIRCd server bind_server = 0.0.0.0:7000 # link = [autoconnect] (the password is a shared secret) # link = peer.example.net 203.0.113.5 7000 CHANGE_THIS_LINK_SECRET autoconnect # services: the linked server that handles SASL (client AUTHENTICATE is relayed to # it). Leave unset to disable SASL. SASL EXTERNAL additionally needs the client on # TLS with a client certificate (its fingerprint is sent to services). # sasl_server = services.example.net # trusted web gateways (CGI:IRC / kiwiirc-style): they send WEBIRC to declare the # real client's host+ip. webirc = [gateway-name] [ip-mask]; the ip-mask # restricts which source IP may use the password (recommended). Repeat for more. # webirc = CHANGE_THIS_WEBIRC_SECRET mygateway 203.0.113.9 # IRC operators — oper = oper = admin CHANGE_THIS_PASSWORD # host-cloaking secret (+x). Use a long random hex string; keep it private. # Changing it re-cloaks everyone. cloak_key = CHANGE_THIS_TO_A_LONG_RANDOM_HEX_STRING # reverse-DNS clients on connect (the "*** Looking up your hostname..." notices). # on (default) performs the lookup and reports the result; off skips it (bare IP). resolve_hosts = on # whether a resolved hostname is used in the hostmask (nick!user@host). on (default) # shows the domain; off keeps the IP in the mask even though the lookup still runs # and reports "Found your hostname". Only matters when resolve_hosts = on. use_resolved_host = on # allow a unique command prefix to resolve to its full command (e.g. WHOI -> WHOIS) # abbreviation = yes # customprefix — reconfigure built-in prefix tiers, or define brand-new ones. # Built-in tiers (oper founder admin op halfop voice): a bare token = the sigil; # ranktoset / ranktounset = min rank to grant / revoke it (a number or a tier name); # depriv=no forbids removing it from yourself. # customprefix = op * ranktoset=admin ranktounset=admin depriv=no # customprefix = voice - # New prefix (name is anything that isn't a built-in tier): letter + prefix required; # rank (default 1), ranktoset/ranktounset (default rank), depriv (default yes). # Ranks: voice=10 halfop=20 op=30 admin=40 founder=50 oper=60 (room to slot between). # customprefix = helper letter=V prefix=? rank=25 ranktoset=op ranktounset=op # DNS blocklist (DNSBL) checks on connect. Repeat `dnsbl` # for multiple zones. On a listing, `dnsbl_action` decides what happens: # mark = just show the "*** ... LISTED" notice, let them in (default, safe) # kill = disconnect them (no persistent ban) # kline / gline / zline = add a 1-day ban and disconnect # (leave commented to disable DNSBL entirely) # dnsbl = dnsbl.dronebl.org # dnsbl = rbl.efnetrbl.org # dnsbl_action = mark # dnsbl_reason = Your host is listed in a DNS blocklist # antimixedutf8 — block spam that mixes look-alike scripts within words. # action = block | kill | gline | kline | zline ; target = both | channel | private antimixedutf8 = off amu_threshold = 8 amu_minlen = 10 amu_action = block amu_target = both # +G censor words: `badword = [replacement]` (omit replacement to block). # badword = examplebadword *** # --- OPERMOTD: message shown to opers via /OPERMOTD (one line per entry) --- # opermotd = Welcome to the staff team. # --- self-service vhosts: /VHOST sets your displayed host --- # vhost = alice s3cret alice.staff.example # --- command aliases: /NS ... -> PRIVMSG :... (services shortcuts) --- # alias = NS NickServ # alias = CS ChanServ # --- connflood: refuse >max connections per from a single IP --- # connflood = 5 10 # --- connectclass: per-class connection policy. Each line matches connecting # clients by IP/host mask (glob OR CIDR) and optional TLS/port; the first match # wins, else the global limits apply. Masks are tested against the IP at connect # and re-tested against the resolved host at registration. Keys: # allow= IP/host globs or CIDR (e.g. 10.0.0.0/8); any match hits # deny=yes reject clients matching this class # parent= inherit this class's other settings (not allow/deny) # requiressl=yes|trusted require TLS; "trusted" also requires a client cert # password= client must send it via PASS; may be hashed # hash= names the hash of a hashed password (md5/sha256/…) # port= only clients that connected to these listener ports # localmax= max connections per IP in this class (local server) # globalmax= max connections per IP across the whole network # limit= max total local users in this class # maxchans= max channels a member may join # pingfreq= ping frequency; timeout= registration timeout # modes=<+modes> usermodes set on connect # recvq= receive-queue cap; hardsendq= send-queue cap # softsendq= send-queue level above which reads pause (backpressure) # fakelag=no disconnect flooders instead of rate-limiting them # penaltythreshold= flood message cap; commandrate= flood window # useident=yes do an ident (RFC1413) lookup for this class # requireident=yes refuse clients whose ident can't be confirmed # resolvehostnames=no skip reverse-DNS for this class # maxconnwarn=yes snotice opers when a limit refuses a client # (recvq/hardsendq/softsendq apply to plaintext clients; TLS clients and links # use the global max_line/max_sendq below.) # connectclass = trusted allow=10.0.0.0/8 maxchans=200 pingfreq=120 fakelag=no # connectclass = secure allow=* requiressl=yes password=sha256: hash=sha256 # connectclass = vpn allow=* parent=trusted localmax=2 maxchans=20 modes=+ix # connectclass = banned allow=1.2.3.0/24 deny=yes # connectclass_required = yes # refuse clients that match no allow class (default no) # --- global connection limits (per-class recvq/hardsendq/softsendq override these) --- # max_line = 16384 # max bytes in one line / receive queue (default 16 KiB) # max_sendq = 1048576 # max queued output before a slow client is dropped (1 MiB) # --- ident (RFC1413): off by default; a connection class can also enable it --- # useident = yes # look up every client's ident (adds connect latency) # requireident = yes # refuse clients whose ident can't be confirmed # ident_timeout = 5 # seconds to wait for the ident reply # --- RLINE: the /RLINE [] : oper command bans users whose # "nick!user@host realname" matches a regex (native engine; no config to enable). # rline_matchonnickchange = yes # also re-check the R-lines when a user changes nick # --- syslog: mirror the server-notice / log stream to the system logger --- # syslog = yes # syslog_target = /dev/log # a Unix socket path, or host:port for UDP # syslog_facility = daemon # kern user mail daemon auth ... local0..local7 # syslog_tag = echoircd # --- log_json: append the server-notice / log stream to a file as JSONL --- # log_json = /var/log/echoircd/events.jsonl # --- PROXY protocol: trust the HAProxy/nginx PROXY header (v1 or v2) from these # sources (glob or CIDR, repeatable), so the real client IP is used instead of # the proxy's. A connection from a trusted proxy MUST lead with a PROXY header. # Applies to the plaintext and TLS client listeners (WebSocket uses XFF instead). # proxy = 127.0.0.1 # proxy = 10.0.0.0/8 # --- WebSocket transport (browser IRC clients connect straight to echoIRCd) --- # bind_ws = 127.0.0.1:8097 # ws:// listener # bind_wss = 0.0.0.0:7799 # wss:// listener (uses tls_cert/tls_key) # ws_origin = https://x.example # (repeatable) allowed Origin globs; empty = any # ws_defaultmode = text # frame mode with no subprotocol: text|binary|reject # ws_proxyranges = 127.0.0.1 # (repeatable) glob/CIDR of proxies whose # # X-Real-IP / X-Forwarded-For we trust (scoped) # ws_trust_proxy = no # trust those headers from ANY peer (simpler but # # allows IP spoofing; prefer ws_proxyranges) # ws_allowmissingorigin = yes # allow clients that send no Origin header # ws_nativeping = yes # liveness via WebSocket pings (no = IRC PING) # ws_handshake_timeout = 10 # seconds to complete the HTTP Upgrade # ws_ping_interval = 60 # seconds between WebSocket keepalive pings # ws_timeout = 120 # drop after this many seconds of silence # --- security groups: securitygroup = [criteria...] # criteria: public tls insecure account unregistered oper exclude-oper # bot exclude-bot webirc exclude-webirc mask= exclude= # scoremin= scoremax= — use as an extban: MODE #c +b g: # securitygroup = trusted account tls public # securitygroup = newbies scoremax=10 public # --- reputation: per-address scoring + y: score extban --- # reputation_database = reputation.db # default: .reputation # reputation_ipv4prefix = 32 # CIDR bits used to key IPv4 scores # reputation_ipv6prefix = 64 # CIDR bits used to key IPv6 scores # reputation_bumpinterval = 5m # how often a score bumps (+1, +2 if logged in) # reputation_expireinterval = 605 # how often decay rules run # reputation_saveinterval = 902 # how often the db is written # reputation_minchanmembers = 3 # only bump if in a channel this big # reputation_scorecap = 10000 # max score # reputation_whois = all # all | opers | self | none # reputationexpire = 2 1h # score<=2 decays after 1h (repeatable; * = any) # reputationexpire = * 90d # any score decays after 90d # extban usage: MODE #chan +b y:<100 (ban score below 100) +b y:>500 (above 500) # --- whoisport: opers see the target's listener port in WHOIS (always on) --- # --- ircv3_network_icon: advertise a network icon via draft/ICON ISUPPORT --- # network_icon = https://example.org/icon.png # --- profileLink: a profile URL in WHOIS for logged-in users --- # profilelink_baseurl = https://example.org/profile/ # --- hidewhois: hide sensitive WHOIS lines from ordinary users --- # hidewhois = yes # hidewhois_opers = yes # opers still see everything # hidewhois_selfview = yes # a user sees their own full WHOIS # hidewhois_hide_server = yes # hide 312 # hidewhois_hide_idle = yes # hide 317 # hidewhois_hide_secure = yes # hide 671 # --- chanlog: mirror the oper server-notice stream into a channel so # staff can watch it in a normal window. Set the channel (create/keep it opped): # chanlog = #snotices # --- extbanbanlist: no config — adds the matching extban # `b:<#channel>`, so `+b b:#staff` catches everyone banned in #staff (shares a # ban list between channels). # --- relaymsg (draft/relaymsg): a member whose client negotiated the # capability can /RELAYMSG <#chan> to speak under a spoofed relay # nick (for bridges). The nick must contain a separator and not collide. # relaymsg_separators = / # relaymsg_ident = relay # relaymsg_host = relay.example.com # default: the server name # --- operprefix: give every oper a `!` prefix (mode y, above owner) # in all their channels — visible staff, and ops can't kick/deop them. Applied # on oper-up/join, removed on de-oper. # operprefix = yes # --- ojoin: the /OJOIN <#chan> oper command — join as network staff with # the `!` prefix (and channel op unless ojoin_op = no). # ojoin = yes # ojoin_op = yes # --- helpmode: no config — adds oper-settable user mode +h (helpop), # which shows "is available for help" in the user's WHOIS. # --- globops: no config — adds the oper command /GLOBOPS , # broadcasting to all opers (like the server-notice stream). # --- autodrop: silently drop a not-yet-registered client that sends # any of these commands (HTTP scanners blurt GET/POST before NICK/USER): # autodrop_commands = GET POST HEAD CONNECT PUT DELETE OPTIONS TRACE PATCH # --- hidemode: hide changes to a mode from members below a rank # (the setter, opers and links always see it). Repeatable, # `hidemode = ` (owner|admin|op|halfop|voice). e.g. hide bans: # hidemode = b op # --- hidelist: list modes (+b/+e/+I/…) are viewable by members by # default; this restricts a given list to a minimum rank. Repeatable, # `hidelist = ` (rank: owner|admin|op|halfop|voice). Opers see # everything. e.g. only ops may view the ban list: # hidelist = b op # --- autoop: no config needed — it's the channel list mode +w. Grant a # status prefix to matching users on join, `+w :`, e.g. # /MODE #chan +w o:*!*@trusted.host (auto-op) # /MODE #chan +w v:*!*@*.friend.net (auto-voice) # /MODE #chan +w lists the entries. # --- banredirect: no config needed — it extends ban syntax. A # ban `+b $<#channel>` bounces a matching user into #channel instead of # refusing them, e.g. /MODE #main +b *!*@*.spammer.net$#quarantine # The redirect fires at most once (never loops). # --- solvemsg: an un-vouched user must answer one arithmetic # question before their private messages are delivered (opers & logged-in # accounts are exempt). Cheap anti-spam-bot gate. # solvemsg = yes # --- dccallow: block unwanted DCC transfers unless the recipient # ran /DCCALLOW +. Blocked file globs are repeatable; blockchat also # gates DCC CHAT. Recipients manage their allow-list with DCCALLOW +/-/LIST. # dccallow_blockfile = *.exe # dccallow_blockfile = *.scr # dccallow_blockchat = yes # dccallow_maxentries = 20 # --- conn_waitpong: hold registration until the client answers # a server PING with the exact cookie — filters bots that never PONG. Real # clients auto-reply, so it's transparent to them. # conn_waitpong = yes # conn_waitpong_killonbadreply = yes # drop on a wrong pong (default: keep waiting) # --- showfile: serve a text file as its own command. One line per # file: `showfile = `. The file is read fresh each use, so # edits show without a rehash. e.g. make /RULES stream a rules file: # showfile = RULES /etc/echoircd/rules.txt # --- geoip: native MaxMind .mmdb country lookup. Enables the # G: ban extban (e.g. +b G:CN,RU), the oper GEOIP command and # a country line in WHOIS (opers). Point at a GeoLite2-Country.mmdb file: # geoip_database = /etc/echoircd/GeoLite2-Country.mmdb # --- tunable limits & timeouts (every one shown with its built-in default; set a # line only to override it). None of these are hardcoded — all read at runtime. # flood: allow this many messages per this many seconds before dropping (opers exempt) # flood_messages = 8 # flood_seconds = 4 # dnsbl: ban length in seconds when dnsbl_action is kline/gline/zline (default 1 day) # dnsbl_duration = 86400 # draft/multiline: the max-bytes / max-lines advertised in the cap AND enforced # multiline_maxbytes = 4096 # multiline_maxlines = 24 # CHATHISTORY: messages kept per conversation (also the ceiling a client may request) # chathistory_limit = 256 # EXTJWT: token line-chunk size (bytes) when splitting a long token across lines # extjwt_chunk = 200 # per-user list sizes (advertised in ISUPPORT WATCH/MONITOR/SILENCE where applicable) # maxwatch = 128 # maxmonitor = 128 # maxsilence = 32 # maxaccept = 64 # WHOWAS: number of historical nick records retained # whowas_maxentries = 256 # nick / channel name length limits (advertised as NICKLEN / CHANNELLEN) # maxnick = 30 # maxchannel = 50 # connection timeouts, in seconds # registration_timeout = 60 # drop clients that never register (NICK+USER) in time # ping_frequency = 90 # send a PING after this much idle time # ping_timeout = 60 # then drop if no PONG within this much longer