website/content/docs/configuration.md

10 KiB

Configuration

echoIRCd reads one file — echoircd.conf by default. This page walks you from a two-line starter config to a full, production-ready one, section by section. Every option (with its default) is listed in the Configuration reference, and the shipped echoircd.conf.example is the same thing as a file you can copy.

cp echoircd.conf.example echoircd.conf   # then edit it

The format

Config is a set of blockssection { field value; … }:

  • One field per ;. A block may span multiple lines.
  • Comments: # and // for a line, /* … */ for a range. An inline # on an active line is part of the value unless you quote the value, so quote anything containing # or spaces.
  • Booleans are yes / no. A bare field (e.g. operprefix;) means yes.
  • Defaults are built in — you only set a field to change it.
  • Most changes re-apply on echoircd rehash (SIGHUP) with no restart; the TLS cert is re-read too.

echoircd.conf holds secrets (oper password, cloak key, link password) — it's gitignored in the source tree for that reason. Never commit your real config.

Minimal config

The smallest thing that boots — plaintext on 6667:

server {
    name    "irc.example.net";   # your server's unique name
    network "ExampleNet";        # the network name clients see
    sid     "0AA";               # 3 chars: a digit + 2 alphanumerics
}

listen { ip "[::]"; port 6667; }   # plaintext, IPv4 + IPv6

That's a working IRC server. Everything below adds TLS, operators, services, and hardening.

A complete example

A realistic config you can copy and adapt. Uncommented lines are a sensible starting point; commented lines show common extras.

# ── server identity ──────────────────────────────────────────────
server {
    name        "irc.example.net";
    network     "ExampleNet";
    sid         "0AA";
    description "ExampleNet IRC";
    pidfile     "echoircd.pid";     # lets `echoircd rehash` find the process
}

# ── listeners (repeatable; [::] = IPv4 + IPv6) ───────────────────
listen { ip "[::]"; port 6667; }              # plaintext
listen { ip "[::]"; port 6697; tls yes; }     # direct TLS
listen { ip "[::]"; port 7799; wss yes; }     # WebSocket (browser clients)
listen { ip "[::]"; port 7000; type server; } # server-to-server links

# ── TLS (cert re-read on rehash) ─────────────────────────────────
tls {
    cert "./tls/cert.pem";
    key  "./tls/key.pem";
    # backend openssl;   # or rustls (pure-Rust, no system OpenSSL)
}

# ── message of the day ───────────────────────────────────────────
motd {
    "Welcome to ExampleNet.";
    "Be excellent to each other.";
}

# ── host cloaking: mode +x hides a user's real host ──────────────
cloak { key "REPLACE-WITH-A-LONG-RANDOM-HEX-STRING"; }

# ── operators ────────────────────────────────────────────────────
# Hash the password:  printf '%s' 'yourpass' | ./echoircd mkpasswd
oper {
    name     "admin";
    password "$2b$11$REPLACE_WITH_A_BCRYPT_HASH";
    type     netadmin;              # full access (a built-in type)
    # fingerprint "<sha256-cert-fp>";   # optional TLS-cert 2FA
}

# ── link to services (NickServ / ChanServ / …) ───────────────────
# link {
#     name     "services.example.net";
#     ip       127.0.0.1;
#     port     7000;
#     password "REPLACE-WITH-A-LINK-SECRET";
#     services yes;                 # mark it a U-lined services server
# }
# services { sasl_server "services.example.net"; }

# ── sensible tuning ──────────────────────────────────────────────
flood     { flood_messages 8; flood_seconds 4; }
timeouts  { registration_timeout 60; ping_frequency 90; ping_timeout 60; }
antiabuse { antirandom yes; solvemsg yes; }
logging   { metrics_bind "127.0.0.1:9109"; }

The rest of this page explains each piece.

Listeners

Each listen block opens one socket; add as many as you need. ip "[::]" (or "*") binds IPv4 and IPv6 at once. type server is the port other servers (and the services package) link to.

listen { ip "[::]";      port 6667; }              # plaintext
listen { ip "[::]";      port 6697; tls yes; }     # direct TLS
listen { ip "[::]";      port 7799; wss yes; }     # WebSocket-over-TLS
listen { ip "127.0.0.1"; port 8097; ws  yes; }     # plaintext WebSocket (behind a proxy)
listen { ip "[::]";      port 7000; type server; } # S2S

TLS

Point the tls block at a certificate and key. Use a real certificate (Let's Encrypt) in production; for a quick test, a self-signed one works:

openssl req -x509 -newkey rsa:2048 -keyout tls/key.pem -out tls/cert.pem \
        -days 3650 -nodes -subj "/CN=irc.example.net"
tls {
    cert    "./tls/cert.pem";
    key     "./tls/key.pem";
    backend openssl;   # or: rustls
    # Per-host certificate by TLS SNI (repeatable):
    # sni "irc.other.net ./tls/other.crt ./tls/other.key";
}

The certificate is re-read on echoircd rehash, so a renewed cert applies without a restart.

Operators

First hash a password:

printf '%s' 'my-strong-password' | ./echoircd mkpasswd

Then an oper block ties a login to a role. type netadmin gives full access; drop type for full access with no role. A block with neither a password nor a fingerprint is refused.

oper {
    name        "alice";
    password    "$2b$11$…";           # from mkpasswd
    type        netadmin;
    host        "*@192.0.2.0/24";      # optional: restrict where alice may oper from
    # fingerprint "AA:BB:…";           # require this TLS client-cert (2FA, or alone)
}

/oper alice my-strong-password logs in. For custom roles — limiting which commands, privileges, and modes an operator has — define class and opertype blocks; see Operators.

Host cloaking

Mode +x replaces a user's host with a stable, keyed cloak. Set a long random secret and keep it private (changing it re-cloaks everyone):

cloak {
    key "a-long-random-hex-secret";
    # method hmac-sha256;   # default; or account / fingerprint / static
}

Connection classes

Fine-grained per-client policy, matched by IP/host/TLS/port — first match wins. Each class is one quoted string of key=value tokens.

classes {
    connectclass "trusted allow=10.0.0.0/8 maxchans=200 pingfreq=120 fakelag=no";
    connectclass "secure  allow=* requiressl=yes";
    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 matching no allow class
}

Common tokens: allow=, deny=yes, requiressl=yes|trusted, password=, port=, asn=, localmax=/globalmax=, maxchans=, pingfreq=/timeout=, modes=, recvq=/softsendq=/ hardsendq=, fakelag=no, useident=yes. The full list is in the reference.

DNS & ident

dns {
    resolve_hosts     yes;   # reverse-DNS on connect (no = show the bare IP)
    use_resolved_host yes;   # use the resolved name in the hostmask
    # useident      yes;     # RFC 1413 ident lookups (adds connect latency)
    # requireident  yes;
}

Limits, timeouts & flood

All optional — the defaults are sane. Set only what you want to change.

limits   { maxchannel 50; maxnick 30; chathistory_limit 256; }
timeouts { registration_timeout 60; ping_frequency 90; ping_timeout 60; }
flood    { flood_messages 8; flood_seconds 4; connflood "5 10"; }

Anti-abuse

Layered spam/drone defence — turn on what you need:

antiabuse {
    antirandom yes;                 # score random-looking nick/ident/realname
    solvemsg   yes;                 # unvouched users answer a sum before PMs deliver
    # badword  "spamword ***";      # +G censor (omit the replacement to block)
}

restrictions {
    # restrictmsg yes;              # only opers/services may be PM'd by regular users
    autodrop_commands "GET POST HEAD CONNECT PUT DELETE OPTIONS TRACE PATCH";
}

# DNS blocklists (repeatable):
# dnsbl {
#     dnsbl        "dnsbl.dronebl.org";
#     dnsbl_action "mark";          # mark | kill | kline | gline | zline
# }

See Security & anti-abuse for the whole picture.

Logging & metrics

logging {
    metrics_bind "127.0.0.1:9109";               # Prometheus endpoint (keep it private)
    # syslog       yes;                            # mirror to the system logger
    # syslog_target "/dev/log";
    # log_json     "/var/log/echoircd/events.jsonl";
    # chanlog      "#snotices";                     # mirror oper notices into a channel
}

See Metrics & RPC.

Linking services

The services package (NickServ, ChanServ, …) connects over the S2S port. Give it a link block, mark it U-lined, and name it as the SASL server:

listen { ip "[::]"; port 7000; type server; }

link {
    name     "services.example.net";
    ip       127.0.0.1;
    port     7000;
    password "shared-link-secret";
    services yes;                    # U-lined services server
}

services { sasl_server "services.example.net"; }

Full details on both sides are in Server links and Services.

WebSocket & STS

If you run a ws/wss listener, tune it and (once TLS is solid) advertise STS to push plaintext clients onto TLS:

websocket {
    ws_origin      "https://webchat.example.net";   # allowed Origins (repeatable; empty = any)
    ws_proxyranges "127.0.0.1";                      # trusted reverse-proxy IPs
}

# sts {
#     sts_duration 2592000;   # seconds clients should stick to TLS (0 = off)
#     sts_port     6697;
# }

Validate & apply

Always check a config before deploying it, then reload in place:

echoircd checkconfig echoircd.conf   # parse + print every resolved key (catches typos)
echoircd rehash      echoircd.conf   # SIGHUP the running server — no restart, no disconnects

For every field and its default, see the Configuration reference.