Files
bouncer/docs/USAGE.md
user d13d090e8e feat: make all operational constants configurable via bouncer.toml
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>
2026-02-21 16:33:08 +01:00

12 KiB

Usage

Starting the Bouncer

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:

[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

[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 (free tier: 6000 solves/month)
  2. Copy your API key from the dashboard
  3. Add to config:
    [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.