Files
bouncer/docs/CHEATSHEET.md
user 8cc57a7af4 feat: multi-network namespace multiplexing
Multiplex all networks onto a single client connection using /network
suffixes on channels and nicks. PASS is now just the password (no
network prefix). Channels appear as #channel/network, foreign nicks as
nick/network, own nicks stay bare.

New namespace.py module with pure encode/decode functions. Router
tracks clients globally (not per-network), namespaces messages before
delivery. Client attaches to all networks on connect, sends synthetic
JOIN/TOPIC/NAMES for every channel across all networks.

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

3.0 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)

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 (15s) -> READY
State What happens
CONNECTING TCP + SOCKS5 + TLS handshake
REGISTERING Random nick/user/realname sent to server
PROBATION 15s wait, watching for K-line
READY Switch to configured nick, join channels

Reconnect Backoff

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

Config Skeleton

[bouncer]
bind / port / password
  [bouncer.backlog]
    max_messages / replay_on_connect

[proxy]
host / port

[networks.<name>]                 # repeatable
host / port / tls
nick / channels / autojoin
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)

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)
  network.py     # server connection + state machine
  client.py      # client session handler
  router.py      # message routing + backlog trigger
  server.py      # TCP listener
  backlog.py     # SQLite store/replay/prune