50 Commits

Author SHA1 Message Date
user
2b893969d2 fix: switch to alpine base image and upgrade pip
All checks were successful
CI / secrets (push) Successful in 8s
CI / lint (push) Successful in 11s
CI / test (push) Successful in 26s
CI / build (push) Successful in 31s
Replace python:3.12-slim (Debian) with python:3.12-alpine to reduce
image size and eliminate 68 Debian-inherited CVEs. Upgrade pip to
resolve CVE-2025-8869. Build deps installed temporarily for native
extensions (cryptography) and removed after pip install.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 12:54:16 +01:00
user
f9f38adadc fix: bake source into container image for production builds
All checks were successful
CI / secrets (push) Successful in 9s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 24s
CI / build (push) Successful in 21s
Install deps from requirements.txt for better layer caching and COPY
src/ into the image so pushed artifacts are self-contained. Remove
VOLUME /app/src -- runtime config mount (/data) is sufficient.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 22:54:42 +01:00
user
f3eae9291b fix: use docker build --push for buildx container driver
All checks were successful
CI / secrets (push) Successful in 9s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 26s
CI / build (push) Successful in 17s
Docker buildx with docker-container driver doesn't load images
into the local daemon. Use --push to push directly during build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:45:52 +01:00
user
5eb64d034e fix: gitleaks version resolution and allowlist test fixtures
Some checks failed
CI / secrets (push) Successful in 9s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 26s
CI / build (push) Failing after 28s
Resolve gitleaks version dynamically via GitHub redirect.
Add .gitleaks.toml to allowlist dummy hCaptcha sitekeys in
test_captcha.py (false positives on test UUIDs).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:26:30 +01:00
user
18992c63e1 fix: resolve gitleaks version dynamically from GitHub API
Some checks failed
CI / secrets (push) Failing after 9s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 26s
CI / build (push) Has been skipped
The /releases/latest/download/ URL doesn't expand version in
the filename. Query the redirect to resolve actual version first.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:24:47 +01:00
user
ed513251db fix: run all CI jobs in containers via container directive
Some checks failed
CI / secrets (push) Failing after 9s
CI / lint (push) Successful in 12s
CI / test (push) Successful in 25s
CI / build (push) Has been skipped
The linux runner has no git/node on host and rootless podman
lacks namespace privileges. Use container: directive for every
job: alpine for secrets (gitleaks binary), docker:latest for
build (docker socket mounted by runner).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:20:20 +01:00
user
f14d067779 fix: use alpine/git container for checkout on host jobs
Some checks failed
CI / secrets (push) Failing after 0s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 25s
CI / build (push) Has been skipped
The linux runner has podman but no git. Clone repos via
alpine/git container for secrets and build host jobs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:15:42 +01:00
user
aae9b0f771 fix: replace all actions/checkout with git clone
Some checks failed
CI / secrets (push) Failing after 0s
CI / lint (push) Successful in 11s
CI / test (push) Successful in 26s
CI / build (push) Has been skipped
The linux runner (anvil) has no Node.js, so actions/checkout@v4
fails on every job. Use manual git clone consistently across
all jobs — container and host alike.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:08:22 +01:00
user
e9c8290f9c fix: run gitleaks via podman on host instead of container directive
Some checks failed
CI / secrets (push) Failing after 1s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 26s
CI / build (push) Has been skipped
The gitleaks image lacks a shell compatible with the runner's
script injection. Use podman run with volume mount on the host
instead, matching the proven s5p pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:43:36 +01:00
user
875997aa45 fix: single-line git clone for gitleaks container
Some checks failed
CI / secrets (push) Failing after 4s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 30s
CI / build (push) Has been skipped
Multiline run with backslash continuation gets corrupted inside
the gitleaks container. Collapse to single line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:12:11 +01:00
user
900813fc20 fix: replace actions/checkout with git clone in container jobs
Some checks failed
CI / secrets (push) Failing after 3s
CI / lint (push) Successful in 12s
CI / test (push) Successful in 26s
CI / build (push) Has been skipped
The alpine and gitleaks container images lack Node.js, which
actions/checkout@v4 requires. Use manual git clone instead.
Build job stays on host where actions/checkout works natively.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:07:12 +01:00
user
28f78567df refactor: use native container directive for CI jobs
Some checks failed
CI / secrets (push) Failing after 4s
CI / lint (push) Failing after 6s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
Replace manual podman run invocations with the runner's container:
directive for lint, test, and secrets jobs. Cleaner step definitions,
no volume mounts needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 06:51:50 +01:00
user
2f7b82047d feat: add Gitea CI pipeline with podman containers
Some checks failed
CI / lint (push) Failing after 2s
CI / secrets (push) Failing after 2s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
Lint, test, secrets scan, and Harbor build/push jobs running
on linux runner using ephemeral podman containers. Adds
requirements.txt for container-based pip installs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 06:33:36 +01:00
user
1ea72011b7 fix: reduce reconnect backoff to 1s flat
Exponential backoff up to 300s made no sense with rotating Tor exits
where each reconnect gets a fresh IP. Single 1s delay is sufficient.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:39:30 +01:00
user
0064e52fee feat: DCC stripping in both directions to prevent IP leaks
Block all non-ACTION CTCP/DCC from client-to-server (outbound) and add
security logging when inbound CTCP/DCC is stripped. Hard boundary with
no config toggle -- DCC exposes the client's real IP which defeats the
stealth proxy architecture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:30:44 +01:00
user
f4f3132b6b feat: systemd user service file for headless deployment
Hardened unit with ProtectSystem/ProtectHome, auto-restart on failure,
and ExecReload for SIGHUP hot config reload. Docs updated with setup,
management, and enable-linger instructions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:25:36 +01:00
user
638f12dbb3 fix: resolve all pre-existing ruff lint errors
Fix E501 line-too-long in backlog.py, network.py, test_network.py.
Fix F541 f-string-without-placeholders in network.py.
Fix I001 unsorted imports in network.py.
Remove unused datetime import in test_cert.py (F401).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:13:34 +01:00
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
user
c11bd5555a feat: channel key support for +k channels
Add channel_keys dict to NetworkConfig for storing per-channel keys.
Keys are used in KICK rejoin, passed via AUTOJOIN +#channel key syntax,
supported in ADDNETWORK channel_keys= parameter, and propagated through
REHASH. Extract rehash() as reusable async function for SIGHUP reuse.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:03:23 +01:00
user
bf4a589fc5 feat: client-side TLS for encrypted client connections
Accept TLS-encrypted connections from IRC clients. Auto-generates a
self-signed EC P-256 listener certificate (bouncer.pem) when no custom
cert is provided. Remove CTCP response items from roadmap (stealth by
design -- router already suppresses all CTCP except ACTION).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:47:20 +01:00
user
bfcebad6dd feat: background account farming with ephemeral connections
Add RegistrationManager that periodically spawns ephemeral Network
connections to register new NickServ accounts across all configured
networks. Ephemeral connections reuse the existing registration
lifecycle (random nick, email verification, captcha solving) with
two new Network parameters: cred_network (redirect credential storage
to the real network name) and ephemeral (suppress status broadcasts,
skip SASL/IDENTIFY, go straight to REGISTER).

- backlog: add count_verified_creds() query
- config: farm_enabled, farm_interval, farm_max_accounts
- network: cred_network/ephemeral params, credential ref redirection
- farm: new module with sweep loop, per-network cooldown, stats
- router: farm lifecycle integration, farm property
- commands: FARM (status/trigger) and ACCOUNTS (list stored creds)
- tests: 14 farm tests + 5 ephemeral/cred_network network tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:17:22 +01:00
user
ae8de25b27 fix: verify success completion signal + cross-session verify_url restore
_on_verify_success() was missing _nickserv_complete() call, causing
_go_ready() to hang at _nickserv_done.wait() when registration
completed immediately (no email verification needed).

get_pending_registration() was not returning verify_url from the DB,
so _resume_pending_verification() never restored self._verify_url --
breaking cross-session captcha resume for OFTC-style networks.

Four regression tests added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:55:33 +01:00
user
0d762ced49 feat: PING watchdog, IRCv3 server-time, push notifications
PING watchdog sends PING after configurable silence interval and
disconnects on timeout, detecting stale connections that TCP alone
misses. IRCv3 server-time capability is requested on every connection;
timestamps are injected on dispatch and backlog replay for clients
that support message tags. Push notifications via ntfy or generic
webhook fire on highlights and PMs when no clients are attached,
with configurable cooldown and optional SOCKS5 routing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:41:38 +01:00
user
4dd817ea75 test: add 94 tests for network connection manager
Cover state machine, markov nick generation, SASL negotiation,
NickServ/Q-bot auth flows, probation timer, reconnection backoff,
read loop buffering, and IRC message handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:11:58 +01:00
user
b8d8c22dc8 feat: add --cprofile flag for runtime profiling
Writes cProfile stats to a file on shutdown for performance analysis.
Enable in compose.yaml for container profiling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:54:29 +01:00
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
user
ed576b002d feat: auto-visit OFTC verification URLs, captcha fallback
Detect /verify/ URLs in NickServ notices and attempt automated
verification via SOCKS proxy (POST with token). If the page requires
a captcha, save creds as pending and log the URL for manual visit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:49:28 +01:00
user
246b77e90a feat: Q bot auth for QuakeNet, configurable auth_service
Add auth_service config field ("nickserv", "qbot", "none") to support
networks with non-standard auth systems. QuakeNet uses Q bot AUTH
instead of NickServ. Also bumps NickServ timeout from 15s to 30s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:42:46 +01:00
user
0e06a18851 feat: per-network proxy override, CERT ADD timing fix
config: add optional proxy_host/proxy_port to NetworkConfig
router: resolve per-network proxy via _proxy_for() helper
commands: trigger REHASH reconnect on proxy config changes
network: send CERT ADD before CAP END to beat K-line race

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 02:25:39 +01:00
user
15f0d374d2 feat: remote DNS fallback, .onion TLS handling, SASL EXTERNAL fallback
proxy.py:
- Refactor connection logic into _connect_once() helper
- Fall back to remote DNS via SOCKS5 when local resolution fails
  (enables .onion and proxy-only hostnames)
- Skip TLS hostname verification for .onion addresses (Tor routing
  provides authentication)

network.py:
- Fall back from SASL EXTERNAL to PLAIN on 904 (same connection)
- Auto-register cert fingerprint with NickServ CERT ADD immediately
  after SASL PLAIN success (903) and after RPL_WELCOME (001)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 01:39:57 +01:00
user
2f40f5e508 feat: add CertFP authentication with SASL EXTERNAL
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).
2026-02-21 01:15:25 +01:00
user
e6b1ce4c6d fix: block PASS/USER/NICK from clients post-registration
All three registration commands are now explicitly intercepted after
the client has authenticated. NICK gets a notice pointing to the
bouncer command; PASS and USER are silently dropped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:48:03 +01:00
user
ee2175f565 fix: block direct NICK from clients, require bouncer command
Clients sending /nick are intercepted with a NOTICE pointing them
to /msg *bouncer NICK <network> <nick> instead. Prevents unmanaged
nick changes that bypass the bouncer's identity tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:45:45 +01:00
user
3d9aa33ec4 feat: add 16 extended bouncer control commands
Network control (CONNECT, DISCONNECT, RECONNECT, NICK, RAW), visibility
(CHANNELS, CLIENTS, BACKLOG, VERSION), config management (REHASH,
ADDNETWORK, DELNETWORK, AUTOJOIN), and NickServ operations (IDENTIFY,
REGISTER, DROPCREDS). Total command count: 22.

Adds stats()/db_size() to Backlog, add_network()/remove_network() to
Router, and _connected_at timestamp to Client. 74 command tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:34:23 +01:00
user
6478c514ad feat: add bouncer control commands via /msg *bouncer
Users can now inspect bouncer state and manage it from their IRC client
by sending PRIVMSG to *bouncer (or bouncer). Supported commands:
HELP, STATUS, INFO, UPTIME, NETWORKS, CREDS. Responses arrive as
NOTICE messages. All commands are case-insensitive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:10:39 +01:00
user
532ceb3c3d fix: track reconnect task for clean shutdown
Reconnect backoff sleeps (up to 300s) were not cancellable, causing
SIGKILL on container stop. Now _schedule_reconnect spawns a tracked
task that stop() cancels, enabling graceful shutdown within the
podman timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 20:16:57 +01:00
user
54218d2677 fix: suppress connection noise, MOTD, CTCP, and DCC from clients
Filter out messages that are useless to bouncer clients:
- Server notices (prefix without !, NOTICE to */AUTH)
- MOTD numerics (375, 372, 376, 422)
- Welcome/stats numerics (001-005, 042, 250-255, 265-266)
- User mode changes (MODE to non-channel targets)
- CTCP queries and DCC requests (PRIVMSG with \x01, except ACTION)
- CTCP replies in NOTICE

Filter applies to both live dispatch and backlog replay. Purged
existing noise from the backlog database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 20:01:20 +01:00
user
3c6f0bcf19 fix: use client nick for synthetic JOINs and own-nick rewriting
irssi (and other IRC clients) only open a channel window when they see
a JOIN from their own nick. The synthetic JOINs were using the network
nick (e.g. pagumowa) but the client registered as tester -- mismatch.

Three changes:
- Synthetic JOIN prefix is now client_nick!user@bouncer
- 001 welcome uses the client's registered nick
- encode_nick/encode_message accept client_nick param to rewrite own
  nicks from any network to the client's nick, so irssi recognizes
  all self-actions consistently

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:42:10 +01:00
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
user
ab7603f638 fix: wait for nick confirmation before joining channels
Send NICK and wait for server confirmation (up to 10s) before
issuing JOIN commands, ensuring channels are joined under the
correct identity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:26:25 +01:00
user
280d0c3949 feat: host-derived nicks and generic identity
Nick is now deterministically generated from the exit endpoint
hostname via seeded markov chain. Same exit IP always produces the
same nick. Config nick field is optional fallback only.

Registration uses generic ident (user/ident) and realname
(realname/unknown) instead of random markov words.

Also fixes compose env vars and build target to use podman-compose.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 22:22:16 +01:00
user
2a55620ccc fix: relay raw IRC bytes instead of re-formatting messages
Preserve original server bytes in IRCMessage.raw and forward those
to clients, avoiding parse/format round-trip that altered messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 20:41:15 +01:00
user
9954a890c3 docs: add podman deployment guide
New docs/DEPLOY.md covering container image, compose config, volume
mounts, host networking, operations, and troubleshooting. Updated
README, INSTALL, CHEATSHEET, and DEBUG to reference it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:51:29 +01:00
user
48459c8506 fix: mount source via volume instead of baking into image
Containerfile now only installs dependencies; source code and config
are mounted at runtime via compose volumes. Adds k8s-file log driver
and PYTHONUNBUFFERED for reliable container logging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:47:39 +01:00
user
d2144fc029 feat: add Containerfile and podman-compose setup
Host network mode for direct access to SOCKS5 proxy on localhost.
Config volume mounted from ./config. Makefile targets: build, up,
down, logs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:34:48 +01:00
user
a58848395c docs: rewrite all documentation for stealth connect and current state
Update README, PROJECT, ROADMAP, TASKS, TODO, USAGE, CHEATSHEET,
INSTALL, and DEBUG to reflect stealth connect, probation window,
markov nick generation, local DNS resolution, and multi-IP failover.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:31:20 +01:00
user
845496f1b3 feat: markov bigram nick generator for natural-looking identities
Replace uniform random chars with English bigram frequency table.
Enforces max 2 consecutive consonants for pronounceability. Nicks,
idents, and realnames now look like plausible human-chosen words.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:08:57 +01:00
user
86832b8fe5 feat: stealth connect with random identity and probation window
Register with a fully random nick, user, and realname (no fixed
pattern) to avoid fingerprinting. Enter a 15s probation period
after registration -- if the server k-lines, reconnect with a
fresh identity. Only after surviving probation: switch to the
configured nick and join channels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:05:54 +01:00
user
41ba680dcb fix: resolve DNS locally and try all IPs via SOCKS5
Many SOCKS5 proxies cannot resolve hostnames reliably. Resolve
locally and iterate through all returned addresses until one
succeeds. Also exclude personal config from git.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 11:54:30 +01:00
user
ced6232373 feat: initial IRC bouncer implementation
Async Python IRC bouncer with SOCKS5 proxy support, multi-network
connections, password auth, and persistent SQLite backlog with replay.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 11:29:59 +01:00