Replace hardcoded values across network, captcha, email, and cert modules with BouncerConfig fields. All values have safe defaults and are overridable in the [bouncer] section of the config file. Configurable: probation_seconds, backoff_steps, nick_timeout, rejoin_delay, http_timeout, captcha_poll_interval/timeout, email_poll_interval/max_polls/request_timeout, cert_validity_days. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
420 lines
12 KiB
Markdown
420 lines
12 KiB
Markdown
# 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 probation
|
|
window (default 45s, configurable via `probation_seconds`). 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
|
|
(configurable via `backoff_steps`):
|
|
|
|
| Attempt | Default 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 <password>
|
|
```
|
|
|
|
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
|
|
|
|
# Captcha solving (NoCaptchaAI)
|
|
captcha_api_key = "" # API key (optional, for auto-verification)
|
|
captcha_poll_interval = 3 # seconds between solve polls
|
|
captcha_poll_timeout = 120 # max seconds to wait for solve
|
|
|
|
# Connection tuning
|
|
probation_seconds = 45 # post-connect watch period for k-lines
|
|
backoff_steps = [5, 10, 30, 60, 120, 300] # reconnect delays
|
|
nick_timeout = 10 # seconds to wait for nick change
|
|
rejoin_delay = 3 # seconds before rejoin after kick
|
|
http_timeout = 15 # per-request HTTP timeout
|
|
|
|
# Email verification
|
|
email_poll_interval = 15 # seconds between inbox checks
|
|
email_max_polls = 30 # max inbox checks (~7.5 min)
|
|
email_request_timeout = 20 # per-request timeout for email APIs
|
|
|
|
# Certificate generation
|
|
cert_validity_days = 3650 # client cert validity (~10 years)
|
|
|
|
[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)
|
|
```
|
|
|
|
## Automatic Captcha Solving
|
|
|
|
Some IRC networks (e.g. OFTC) require visiting a URL with hCaptcha to verify
|
|
nick registration. The bouncer can solve these automatically using NoCaptchaAI.
|
|
|
|
### Setup
|
|
|
|
1. Sign up at [dash.nocaptchaai.com](https://dash.nocaptchaai.com) (free tier: 6000 solves/month)
|
|
2. Copy your API key from the dashboard
|
|
3. Add to config:
|
|
```toml
|
|
[bouncer]
|
|
captcha_api_key = "your-api-key-here"
|
|
```
|
|
4. Reload config:
|
|
```
|
|
/msg *bouncer REHASH
|
|
```
|
|
|
|
### How It Works
|
|
|
|
When NickServ sends a verification URL containing `/verify/`:
|
|
|
|
1. The bouncer fetches the page via the SOCKS proxy
|
|
2. If hCaptcha is detected and an API key is configured, it submits the
|
|
challenge to NoCaptchaAI for solving (all traffic routed through the proxy)
|
|
3. The solved token is submitted with the verification form
|
|
4. On success, the nick is promoted from `pending` to `verified` status
|
|
|
|
If no API key is set, or solving fails, the URL is stored as `pending` and
|
|
shown via the `CREDS` command for manual verification.
|
|
|
|
## 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 <fingerprint>` 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 <network>` | 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 <network>` | Start a disconnected network |
|
|
| `DISCONNECT <network>` | Stop a network |
|
|
| `RECONNECT <network>` | Stop and restart with a fresh identity |
|
|
| `NICK <network> <nick>` | Change nick on a network |
|
|
| `RAW <network> <command>` | Send a raw IRC command to a network |
|
|
|
|
### Config Management
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `REHASH` | Reload config file, add/remove/reconnect networks |
|
|
| `ADDNETWORK <name> key=val ...` | Create a network at runtime |
|
|
| `DELNETWORK <name>` | Stop and remove a network |
|
|
| `AUTOJOIN <network> +/-#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 <network>` | Force NickServ IDENTIFY with stored credentials |
|
|
| `REGISTER <network>` | Trigger NickServ registration attempt |
|
|
| `DROPCREDS <network> [nick]` | Delete stored NickServ credentials |
|
|
|
|
### CertFP
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `GENCERT <network> [nick]` | Generate client cert, auto-register with NickServ |
|
|
| `CERTFP [network]` | Show certificate fingerprints (all or per-network) |
|
|
| `DELCERT <network> [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.
|