Per-network, per-nick client certificates (EC P-256, self-signed, 10-year validity) stored as combined PEM files. Authentication cascade: SASL EXTERNAL > SASL PLAIN > NickServ IDENTIFY. New commands: GENCERT, CERTFP, DELCERT. GENCERT auto-registers the fingerprint with NickServ CERT ADD when the network is connected. Includes email verification module for NickServ registration and expanded NickServ interaction (IDENTIFY, REGISTER, VERIFY).
367 lines
10 KiB
Markdown
367 lines
10 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 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 <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
|
|
|
|
[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 <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.
|