Files
bouncer/docs/CHEATSHEET.md
user 2ab5f95476 feat: SIGHUP hot reload for headless config updates
Add signal handler that calls rehash() on SIGHUP, logging results
instead of sending to a client. Useful for systemd and container
environments where no IRC client is attached. Update docs with
channel key config, hot reload section, and roadmap checkoffs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:03:35 +01:00

7.4 KiB

Cheatsheet

Run

bouncer -c config/bouncer.toml         # start
bouncer -c config/bouncer.toml -v      # start (debug)
bouncer --version                      # version
bouncer --help                         # help

Podman

make build      # build container image
make up         # podman-compose up -d
make down       # podman-compose down
make logs       # podman logs -f bouncer
podman logs bouncer 2>&1 | grep -E 'INFO|WARN'   # filtered logs
podman exec -it bouncer bash                      # shell into container
podman ps --filter name=bouncer                   # status

Develop

make dev        # install with dev deps into .venv
make test       # pytest
make lint       # ruff check
make fmt        # black + ruff --fix
make run        # run with config/bouncer.toml
make clean      # rm .venv, build artifacts

Client Auth

PASS <password>                 # authenticate (all networks)

Bouncer Commands

Inspection

/msg *bouncer HELP                  # list commands
/msg *bouncer STATUS                # all network states
/msg *bouncer INFO libera           # detailed network info
/msg *bouncer UPTIME                # process uptime
/msg *bouncer NETWORKS              # list networks
/msg *bouncer CREDS [network]       # NickServ creds
/msg *bouncer CHANNELS [network]    # joined channels + topics
/msg *bouncer CLIENTS               # connected clients
/msg *bouncer BACKLOG [network]     # message counts + DB size
/msg *bouncer VERSION               # bouncer + Python version

Network Control

/msg *bouncer CONNECT libera        # start disconnected network
/msg *bouncer DISCONNECT libera     # stop network
/msg *bouncer RECONNECT libera      # restart with fresh identity
/msg *bouncer NICK libera newnick   # change nick
/msg *bouncer RAW libera WHOIS user # send raw IRC command

Config Management

/msg *bouncer REHASH                # reload config file
/msg *bouncer ADDNETWORK name host=h port=N tls=yes nick=n channels=#a,#b
/msg *bouncer DELNETWORK name       # remove network
/msg *bouncer AUTOJOIN net +#chan        # add to autojoin
/msg *bouncer AUTOJOIN net +#chan key   # add with channel key
/msg *bouncer AUTOJOIN net -#chan       # remove from autojoin

NickServ

/msg *bouncer IDENTIFY libera       # force IDENTIFY
/msg *bouncer REGISTER libera       # trigger registration
/msg *bouncer DROPCREDS libera      # delete all creds
/msg *bouncer DROPCREDS libera nick # delete one nick's creds

CertFP

/msg *bouncer GENCERT libera        # generate cert (current nick)
/msg *bouncer GENCERT libera nick   # generate cert (specific nick)
/msg *bouncer CERTFP                # list all cert fingerprints
/msg *bouncer CERTFP libera         # list certs for one network
/msg *bouncer DELCERT libera        # delete cert (current nick)
/msg *bouncer DELCERT libera nick   # delete cert (specific nick)

Account Farming

/msg *bouncer FARM                  # global farming status
/msg *bouncer FARM libera           # network stats + trigger attempt
/msg *bouncer ACCOUNTS              # list all stored accounts
/msg *bouncer ACCOUNTS libera       # accounts for one network

Namespacing

#channel/network                # channel on a specific network
nick/network                    # foreign nick on a specific network
own-nick                        # own nicks shown without suffix
/msg #libera/libera hello       # send to #libera on libera network
/join #test/oftc                # join #test on oftc
/join #a/libera,#b/oftc         # comma-separated, different networks

Connection States

DISCONNECTED -> CONNECTING -> REGISTERING -> PROBATION (45s) -> READY
State What happens
CONNECTING TCP + SOCKS5 + TLS handshake
REGISTERING Random nick/user/realname sent to server
PROBATION 45s wait (configurable), watching for K-line
READY Switch to configured nick, join channels

Auth Cascade

SASL EXTERNAL (cert + creds) > SASL PLAIN (creds) > NickServ IDENTIFY

Reconnect Backoff

5s -> 10s -> 30s -> 60s -> 120s -> 300s (cap)

PING Watchdog

Detects stale connections where TCP stays open but server stops responding.

ping_interval = 120   # silence before PING (seconds)
ping_timeout = 30     # wait for PONG (seconds)

Total detection time: ping_interval + ping_timeout (default 150s).

server-time (IRCv3)

Automatic -- no config needed. Timestamps injected on all messages. Backlog replay includes original timestamps.

Push Notifications

notify_url = "https://ntfy.sh/my-topic"   # ntfy or generic webhook
notify_on_highlight = true                 # channel mentions
notify_on_privmsg = true                   # private messages
notify_cooldown = 60                       # rate limit (seconds)
notify_proxy = false                       # use SOCKS5 for notifications

Only fires when no clients are attached.

Hot Reload

kill -HUP $(pidof bouncer)              # reload config via signal
/msg *bouncer REHASH                    # reload config via command

Config Skeleton

[bouncer]
bind / port / password
client_tls / client_tls_cert      # client-side TLS
client_tls_key                    # separate key file (optional)
captcha_api_key                   # NoCaptchaAI key (optional)
captcha_poll_interval / captcha_poll_timeout
probation_seconds / nick_timeout / rejoin_delay
backoff_steps / http_timeout
email_poll_interval / email_max_polls / email_request_timeout
cert_validity_days
ping_interval / ping_timeout      # PING watchdog
notify_url / notify_on_highlight / notify_on_privmsg
notify_cooldown / notify_proxy    # push notifications
farm_enabled / farm_interval      # background account farming
farm_max_accounts
  [bouncer.backlog]
    max_messages / replay_on_connect

[proxy]
host / port

[networks.<name>]                 # repeatable
host / port / tls
nick / channels / autojoin
channel_keys                      # keys for +k channels
password                          # optional, IRC server PASS

Files

Path Purpose
config/bouncer.toml Active config (gitignored)
config/bouncer.example.toml Example template
config/bouncer.db SQLite backlog (auto-created)
{data_dir}/bouncer.pem Listener TLS cert (auto-created)
{data_dir}/certs/{net}/{nick}.pem Client certificates (auto-created)

Backlog Queries

-- recent messages
SELECT * FROM messages ORDER BY id DESC LIMIT 20;

-- per-network counts
SELECT network, COUNT(*) FROM messages GROUP BY network;

-- last seen state
SELECT * FROM client_state;

Source Layout

src/bouncer/
  __main__.py    # entry point, event loop
  cli.py         # argparse
  config.py      # TOML loader
  irc.py         # IRC message parse/format
  namespace.py   # /network encode/decode for multiplexing
  proxy.py       # SOCKS5 connector (local DNS, multi-IP, CertFP)
  network.py     # server connection + state machine + SASL
  client.py      # client session handler
  cert.py        # client certificate generation + management
  captcha.py     # hCaptcha solver via NoCaptchaAI
  farm.py       # background account farming
  commands.py    # bouncer control commands (/msg *bouncer)
  notify.py      # push notifications (ntfy/webhook)
  router.py      # message routing + backlog trigger + server-time
  server.py      # TCP listener
  backlog.py     # SQLite store/replay/prune