# Usage ## Starting the Bouncer ```bash bouncer -c config/bouncer.toml -v ``` | Flag | Description | |------|-------------| | `-c, --config PATH` | Config file (default: `config/bouncer.toml`) | | `-v, --verbose` | Debug logging | | `--version` | Show version | ## Connection Lifecycle The bouncer goes through several states when connecting to an IRC server: ``` DISCONNECTED -> CONNECTING -> REGISTERING -> PROBATION -> READY | | | `--------------+--------------' (failure = reconnect) ``` ### 1. Stealth Registration On connect, the bouncer registers with a **random identity**: - **Nick**: pronounceable markov-generated word (e.g., `heliagu`, `crewo`, `midon`) - **User/Ident**: random pronounceable word - **Realname**: random capitalized word No fixed prefix or pattern -- each attempt looks like a different person. ### 2. Probation (15 seconds) After registration succeeds (001 RPL_WELCOME), the bouncer enters a 15-second probation window. During this time it watches for: - `ERROR` messages (K-line, ban) - Server closing the connection If the connection drops during probation, the bouncer reconnects with a fresh random identity and tries again. ### 3. Ready Once probation passes without incident: 1. Bouncer switches to your configured nick (`NICK mynick`) 2. Joins configured channels (if `autojoin = true`) 3. Begins relaying messages to/from connected clients ### 4. Reconnection On any disconnection, the bouncer reconnects with exponential backoff: | Attempt | Delay | |---------|-------| | 1 | 5s | | 2 | 10s | | 3 | 30s | | 4 | 60s | | 5 | 120s | | 6+ | 300s | Each reconnection uses a fresh random identity. ## DNS Resolution Hostnames are resolved locally before being passed to the SOCKS5 proxy. If a hostname resolves to multiple IPs, the bouncer tries each one until a connection succeeds. This handles proxies that don't support remote DNS and avoids IPs that are unreachable through the proxy. ## Connecting with an IRC Client Configure your IRC client to connect to the bouncer: | Setting | Value | |---------|-------| | Server | `127.0.0.1` | | Port | `6667` (or as configured) | | Password | `yourpassword` | ### Password Format ``` PASS ``` The password is the `bouncer.password` value from config. A single connection automatically attaches to **all** configured networks. ### Client Examples **irssi:** ``` /connect -password mypassword 127.0.0.1 6667 ``` **weechat:** ``` /server add bouncer 127.0.0.1/6667 -password=mypassword /connect bouncer ``` **hexchat:** Set server password to `mypassword` in the network settings. ## Multi-Network Namespacing All configured networks are multiplexed onto a single client connection. Channels and nicks carry a `/network` suffix so you can tell which network they belong to: ``` Client sees: Server wire: #libera/libera <-> #libera (on libera network) #debian/oftc <-> #debian (on oftc network) user123/libera <-> user123 (on libera network) ``` ### Rules - **Channels**: `#channel/network` in client, `#channel` on wire - **Foreign nicks**: `nick/network` in client, `nick` on wire - **Own nicks**: shown without suffix (prevents client confusion) - **Sending messages**: include the `/network` suffix in the target ``` /msg #libera/libera hello -> sends "hello" to #libera on libera network /join #test/oftc -> joins #test on oftc network /msg user123/libera hi -> private message to user123 on libera ``` ### Comma-Separated JOIN/PART Targets can span networks: ``` /join #a/libera,#b/oftc -> joins #a on libera AND #b on oftc ``` Multiple clients can attach simultaneously. All receive the same namespaced messages in real time. ## What Clients Receive on Connect When a client authenticates: 1. **Backlog replay** -- missed messages (namespaced) from all networks 2. **Synthetic welcome** -- 001-004 numeric replies listing all networks 3. **Channel state** -- synthetic JOIN, TOPIC, and NAMES for every joined channel across all networks (all namespaced with `/network` suffix) ## Backlog Messages are stored in `bouncer.db` (SQLite) next to the config file. When you reconnect, missed messages are automatically replayed. Configure in `bouncer.toml`: ```toml [bouncer.backlog] max_messages = 10000 # per network, 0 = unlimited replay_on_connect = true # set false to disable replay ``` Stored commands: `PRIVMSG`, `NOTICE`, `TOPIC`, `KICK`, `MODE`. ## Configuration Reference ```toml [bouncer] bind = "127.0.0.1" # listen address port = 6667 # listen port password = "changeme" # client authentication password [bouncer.backlog] max_messages = 10000 # per network, 0 = unlimited replay_on_connect = true # replay missed messages on client connect [proxy] host = "127.0.0.1" # SOCKS5 proxy address port = 1080 # SOCKS5 proxy port [networks.libera] host = "irc.libera.chat" # IRC server hostname port = 6697 # server port (default: 6697 if tls, 6667 otherwise) tls = true # use TLS for server connection nick = "mynick" # desired IRC nick (set after probation) channels = ["#test"] # channels to join (after probation) autojoin = true # auto-join channels on ready (default: true) password = "" # IRC server password (optional, for PASS command) ``` ## CertFP Authentication The bouncer supports client certificate fingerprint (CertFP) authentication via SASL EXTERNAL. Each certificate is unique per (network, nick) pair and stored as a combined PEM file at `{data_dir}/certs/{network}/{nick}.pem`. ### Authentication Cascade When connecting, the bouncer selects the strongest available method: | Priority | Method | Condition | |----------|--------|-----------| | 1 | SASL EXTERNAL | Stored creds + cert file exists | | 2 | SASL PLAIN | Stored creds, no cert | | 3 | NickServ IDENTIFY | Fallback after SASL failure | ### Setup 1. Generate a certificate: ``` /msg *bouncer GENCERT libera ``` This creates an EC P-256 self-signed cert (10-year validity) and auto-sends `NickServ CERT ADD ` if the network is connected. 2. Reconnect to use CertFP: ``` /msg *bouncer RECONNECT libera ``` The bouncer will now present the client certificate during TLS and authenticate via SASL EXTERNAL. 3. Verify the fingerprint is registered: ``` /msg *bouncer CERTFP libera ``` ### Certificate Storage Certificates are stored alongside the config file: ``` {data_dir}/certs/ libera/ fabesune.pem # cert + private key (chmod 600) oftc/ mynick.pem ``` ## Bouncer Commands Send a PRIVMSG to `*bouncer` (or `bouncer`) from your IRC client to inspect and control the bouncer. All commands are case-insensitive. Responses arrive as NOTICE messages from `*bouncer`. ### Inspection | Command | Description | |---------|-------------| | `HELP` | List available commands | | `STATUS` | Overview: state, nick, host per network | | `INFO ` | Detailed info for one network (state, server, channels, creds) | | `UPTIME` | Bouncer uptime since process start | | `NETWORKS` | List all configured networks with state | | `CREDS [network]` | NickServ credential status (all or per-network) | | `CHANNELS [network]` | List joined channels with topics (all or per-network) | | `CLIENTS` | List connected bouncer clients | | `BACKLOG [network]` | Message counts per network and database size | | `VERSION` | Bouncer and Python version | ### Network Control | Command | Description | |---------|-------------| | `CONNECT ` | Start a disconnected network | | `DISCONNECT ` | Stop a network | | `RECONNECT ` | Stop and restart with a fresh identity | | `NICK ` | Change nick on a network | | `RAW ` | Send a raw IRC command to a network | ### Config Management | Command | Description | |---------|-------------| | `REHASH` | Reload config file, add/remove/reconnect networks | | `ADDNETWORK key=val ...` | Create a network at runtime | | `DELNETWORK ` | Stop and remove a network | | `AUTOJOIN +/-#channel` | Add or remove channel from autojoin list | **ADDNETWORK keys:** `host` (required), `port`, `tls` (yes/no), `nick`, `channels` (comma-separated), `password`. ### NickServ | Command | Description | |---------|-------------| | `IDENTIFY ` | Force NickServ IDENTIFY with stored credentials | | `REGISTER ` | Trigger NickServ registration attempt | | `DROPCREDS [nick]` | Delete stored NickServ credentials | ### CertFP | Command | Description | |---------|-------------| | `GENCERT [nick]` | Generate client cert, auto-register with NickServ | | `CERTFP [network]` | Show certificate fingerprints (all or per-network) | | `DELCERT [nick]` | Delete a client certificate | ### Examples ``` /msg *bouncer HELP /msg *bouncer STATUS /msg *bouncer INFO libera /msg *bouncer CHANNELS /msg *bouncer CLIENTS /msg *bouncer BACKLOG /msg *bouncer VERSION /msg *bouncer CONNECT libera /msg *bouncer DISCONNECT libera /msg *bouncer RECONNECT libera /msg *bouncer NICK libera newnick /msg *bouncer RAW libera WHOIS someuser /msg *bouncer REHASH /msg *bouncer ADDNETWORK oftc host=irc.oftc.net port=6697 tls=yes channels=#test /msg *bouncer DELNETWORK oftc /msg *bouncer AUTOJOIN libera +#newchannel /msg *bouncer AUTOJOIN libera -#oldchannel /msg *bouncer IDENTIFY libera /msg *bouncer REGISTER libera /msg *bouncer DROPCREDS libera /msg *bouncer DROPCREDS libera oldnick /msg *bouncer GENCERT libera /msg *bouncer GENCERT libera fabesune /msg *bouncer CERTFP /msg *bouncer CERTFP libera /msg *bouncer DELCERT libera /msg *bouncer DELCERT libera fabesune ``` ### Example Output ``` [STATUS] libera ready fabesune user/fabesune oftc ready ceraty cloaked.user hackint connecting (attempt 3) quakenet ready spetyo -- [CHANNELS] libera #test Welcome to the test channel libera #dev oftc #debian Debian support [CLIENTS] myuser 127.0.0.1:54321 connected 2h 15m 3s [BACKLOG] libera 1,500 messages oftc 842 messages DB size: 2.1 MB ``` ## Stopping Press `Ctrl+C` or send `SIGTERM`. The bouncer shuts down gracefully, closing all network connections and the backlog database.