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>
2.3 KiB
2.3 KiB
Project: bouncer
Purpose
IRC bouncer that maintains persistent connections to IRC servers through a SOCKS5 proxy, allowing IRC clients to connect/disconnect while keeping the session alive and replaying missed messages.
Architecture
IRC Client(s) --> [bouncer:6667] --> Router --> [SOCKS5:1080] --> IRC Server(s)
|
Backlog
(SQLite)
Connection State Machine
DISCONNECTED -> CONNECTING -> REGISTERING -> PROBATION (15s) -> READY
| | |
'--------------+--------------'
(failure = backoff + retry)
- CONNECTING: SOCKS5 + optional TLS handshake
- REGISTERING: Random nick/user/realname sent to server
- PROBATION: 15s window to detect K-lines before exposing real identity
- READY: Configured nick set, channels joined, clients served
Components
| Module | Responsibility |
|---|---|
irc.py |
IRC protocol parser/formatter (RFC 2812 subset) |
config.py |
TOML configuration loading and validation |
namespace.py |
/network suffix encode/decode for multi-network multiplexing |
proxy.py |
SOCKS5 async connector with local DNS + multi-IP failover |
network.py |
Server connection state machine, stealth registration |
server.py |
TCP listener accepting IRC client connections |
client.py |
Per-client session, PASS/NICK/USER handshake, multi-network attach |
router.py |
Namespaced message routing between clients and networks |
backlog.py |
SQLite message storage and replay |
Key Decisions
- asyncio: single-threaded async for all I/O
- python-socks: async SOCKS5 proxy support
- aiosqlite: non-blocking SQLite for backlog
- No IRC library: manual protocol handling (IRC is line-based)
- Markov nicks: English bigram frequencies for pronounceable random nicks
- Local DNS: resolve before SOCKS5 to handle proxies without DNS support
- Multi-IP: try all resolved addresses, skip unreachable exit IPs
Dependencies
| Package | Version | Purpose |
|---|---|---|
| python-socks[asyncio] | >=2.4 | SOCKS5 proxy |
| aiosqlite | >=0.19 | Async SQLite |
Requirements
- Python 3.10+
- SOCKS5 proxy on 127.0.0.1:1080