106 Commits

Author SHA1 Message Date
user
0ae7b13407 docs: clarify tor_nodes overrides listener first hop
Some checks failed
ci / secrets (push) Successful in 5s
ci / test (push) Successful in 19s
ci / build (push) Failing after 28s
The first hop in each listener's chain is replaced at connection time
by round-robin selection from tor_nodes. Added comments in both the
example config and the server code to make this precedence explicit.
2026-02-22 16:42:20 +01:00
user
a1996b1c9e fix: raise pool test_timeout and max_fails defaults
Accommodate HTTP CONNECT proxies through Tor (p99 latency >8s) and
reduce pool erosion when the upstream proxy count is low.

- test_timeout: 8 -> 12 (Tor + HTTP CONNECT overhead)
- max_fails: 3 -> 5 (10 min tolerance vs 6 min)
2026-02-22 16:39:55 +01:00
user
051c0ac719 fix: use podman --remote for builds via host socket
All checks were successful
ci / secrets (push) Successful in 6s
ci / test (push) Successful in 21s
ci / build (push) Successful in 43s
Building images inside an unprivileged container fails on remount.
Use podman --remote with the runner-mounted podman socket to
delegate builds to the host engine.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:39:19 +01:00
user
b4cf4fc8ae fix: write vfs storage config to system path for root podman
Some checks failed
ci / secrets (push) Successful in 6s
ci / test (push) Successful in 20s
ci / build (push) Failing after 29s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:35:25 +01:00
user
251d99795b fix: use vfs storage driver for podman-in-container builds
Some checks failed
ci / secrets (push) Successful in 6s
ci / test (push) Successful in 21s
ci / build (push) Failing after 24s
Runner ignores --privileged, so overlay mounts fail inside the
container. Switch to vfs storage driver via containers config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:11:42 +01:00
user
cca76d4974 fix: add gitleaks allowlist for docs/tests false positives
Some checks failed
ci / secrets (push) Successful in 5s
ci / test (push) Successful in 20s
ci / build (push) Failing after 33s
All 11 findings are API endpoint URLs (http://api:8081/...) in
documentation and test files, not actual secrets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:09:46 +01:00
user
a64b09de8e fix: run CI jobs in rootless podman containers
Some checks failed
ci / secrets (push) Failing after 6s
ci / test (push) Successful in 21s
ci / build (push) Has been skipped
Use container: directive per job instead of nested podman run.
Each job specifies its execution image directly:
- test: python:3.13-alpine
- secrets: ghcr.io/gitleaks/gitleaks:latest
- build: quay.io/podman/stable (--privileged for nested builds)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 07:08:55 +01:00
user
de5f586bc7 fix: replace actions/checkout with git clone (no node on runner)
Some checks failed
ci / test (push) Failing after 0s
ci / secrets (push) Failing after 0s
ci / build (push) Has been skipped
The linux runner has no Node.js, so actions/checkout@v4 fails.
Use manual git clone with token auth instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 06:54:18 +01:00
user
3e2c431f49 feat: switch CI to linux runner with podman containers
Some checks failed
ci / test (push) Failing after 2s
ci / secrets (push) Failing after 2s
ci / build (push) Has been skipped
Replace ubuntu-latest runner with linux label and migrate all
container operations from docker to podman. Add requirements.txt
as single source of truth for runtime dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 06:33:32 +01:00
user
9a56dc778e feat: add quantiles() method to LatencyTracker for OpenMetrics
All checks were successful
ci / secrets (push) Successful in 10s
ci / test (push) Successful in 20s
ci / build (push) Successful in 15s
Returns {count, sum, 0.5, 0.95, 0.99} in seconds for Prometheus
summary exposition. Companion to the existing stats() (milliseconds).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 20:42:15 +01:00
user
3593481b30 feat: listener retry override, pool protocol filter, conn pool docs
- Per-listener `retries` overrides global default (0 = inherit)
- Pool-level `allowed_protos` filters proxies during merge
- Connection pooling documented in CHEATSHEET.md
- Both features exposed in /config and /status API responses
- 12 new tests (config parsing, API exposure, merge filtering)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 20:35:14 +01:00
user
c1c92ddc39 fix: upgrade pip in container image (CVE-2026-1703)
All checks were successful
ci / secrets (push) Successful in 10s
ci / test (push) Successful in 21s
ci / build (push) Successful in 23s
Path traversal in malicious wheel extraction, fixed in pip 26.0.
2026-02-21 18:50:35 +01:00
user
a741c0a017 feat: v0.3.0 stabilization -- systemd, tests, API docs
All checks were successful
ci / secrets (push) Successful in 9s
ci / test (push) Successful in 20s
ci / build (push) Successful in 15s
- Bump version 0.1.0 -> 0.3.0
- Add systemd service unit (config/s5p.service) and install-service
  Makefile target
- Add CLI argument parsing tests (tests/test_cli.py, 27 tests)
- Expand protocol tests with SOCKS5/4/HTTP handshake, error, and auth
  coverage (tests/test_proto.py, 30 tests)
- Add full API reference to docs/USAGE.md with response schemas for
  all GET/POST endpoints
- Update INSTALL.md, CHEATSHEET.md with systemd section
- Update ROADMAP.md, TASKS.md for v0.3.0
2026-02-21 18:35:51 +01:00
user
53fdc4527f docs: mark SOCKS5 server auth as done (fa36218)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:23:43 +01:00
user
94e91d9e27 fix: use docker build --push (buildx driver skips local store)
All checks were successful
ci / secrets (push) Successful in 9s
ci / test (push) Successful in 20s
ci / build (push) Successful in 18s
Runner uses buildx with docker-container driver which doesn't
populate the local image store. --push builds and pushes directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:17:04 +01:00
user
e9ed041996 fix: bypass docker login, write auth config directly
Some checks failed
ci / secrets (push) Successful in 9s
ci / test (push) Successful in 20s
ci / build (push) Failing after 12s
Docker credential helper on the runner may interfere with login.
Write base64 credentials to ~/.docker/config.json directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:15:43 +01:00
user
c3a4b07d3a fix: switch CI from docker to podman for build and push
Some checks failed
ci / secrets (push) Failing after 9s
ci / test (push) Successful in 20s
ci / build (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:14:28 +01:00
user
3b5ebbaa2e ci: debug harbor login -- print credential lengths
Some checks failed
ci / secrets (push) Successful in 8s
ci / test (push) Successful in 19s
ci / build (push) Failing after 20s
2026-02-21 18:12:57 +01:00
user
c46a347def ci: retry pipeline
Some checks failed
ci / secrets (push) Successful in 9s
ci / test (push) Successful in 19s
ci / build (push) Failing after 12s
2026-02-21 18:09:02 +01:00
user
802170087a ci: trigger pipeline
Some checks failed
ci / secrets (push) Successful in 9s
ci / test (push) Successful in 20s
ci / build (push) Failing after 11s
2026-02-21 17:55:59 +01:00
user
4cbd157896 fix: use --password-stdin for harbor login
Some checks failed
ci / secrets (push) Successful in 9s
ci / test (push) Successful in 19s
ci / build (push) Failing after 12s
Multiline shell continuation in YAML was causing literal \n in the
command. Single-line pipe also avoids the CLI password warning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:47:26 +01:00
user
56db4d26da fix: run gitleaks via container instead of broken binary download
Some checks failed
ci / secrets (push) Successful in 10s
ci / test (push) Successful in 19s
ci / build (push) Failing after 11s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:41:39 +01:00
user
64f3fedb9f feat: add gitleaks secret scanning to CI pipeline
Some checks failed
ci / secrets (push) Failing after 9s
ci / test (push) Successful in 19s
ci / build (push) Has been skipped
Runs gitleaks detect with full history before the build job.
Both test and secrets jobs must pass to gate image push.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:34:38 +01:00
user
8a909cd79d fix: revert to dedicated s5p harbor project
Some checks failed
ci / test (push) Successful in 19s
ci / build (push) Failing after 12s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:32:06 +01:00
user
c33cdc9216 fix: use harbor library project instead of dedicated s5p project
Some checks failed
ci / test (push) Successful in 19s
ci / build (push) Failing after 11s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:29:53 +01:00
user
41a900037d fix: drop container job — actions/checkout needs node on runner
Some checks failed
ci / test (push) Successful in 1m8s
ci / build (push) Failing after 12s
python:3.13-slim lacks node, which actions/checkout@v4 requires.
Run test job on bare runner with setup-python instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:25:35 +01:00
user
8c99544e34 feat: add Gitea CI workflow and production Containerfile
Some checks failed
ci / test (push) Failing after 18s
ci / build (push) Has been skipped
Bake source into the image (COPY src/) so production containers
run without volume mounts. CI pipeline runs ruff + pytest then
builds and pushes harbor.mymx.me/s5p/s5p:latest on push to main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:18:14 +01:00
user
fa3621806d feat: add per-listener SOCKS5 server authentication (RFC 1929)
Per-listener username/password auth via `auth:` config key. When set,
clients must negotiate method 0x02 and pass RFC 1929 subnegotiation;
no-auth (0x00) is rejected to prevent downgrade. Listeners without
`auth` keep current no-auth behavior.

Includes auth_failures metric, API integration (/status auth flag,
/config auth_users count without exposing passwords), config parsing
with YAML int coercion, integration tests (success, failure, method
rejection, no-auth unchanged), and documentation updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:03:03 +01:00
user
76dac61eb6 fix: add shutdown timeout so cProfile data is written on SIGTERM
srv.wait_closed() blocked indefinitely on active relay connections,
preventing serve() from returning and prof.dump_stats() from running.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 16:32:50 +01:00
user
918d03cc58 feat: skip pool hops for .onion destinations
Onion addresses require Tor to resolve, so pool proxies after Tor
would break connectivity. Detect .onion targets and use the static
chain only (Tor), skipping pool selection and retries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 02:28:34 +01:00
user
c191942712 feat: add bypass rules, weighted pool selection, integration tests
Per-listener bypass rules skip the chain for local/private destinations
(CIDR, exact IP/hostname, domain suffix). Weighted multi-candidate pool
selection biases toward pools with more alive proxies. End-to-end
integration tests validate the full client->s5p->hop->target path using
mock SOCKS5 proxies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 19:58:12 +01:00
user
ef0d8f347b feat: add per-hop pool references in listener chains
Allow listeners to mix named pools in a single chain using pool:name
syntax. Bare "pool" continues to use the listener's default pool.
Replaces pool_hops field with pool_seq list; pool_hops is now a
backward-compatible property. Each hop draws from its own pool and
failure reporting targets the correct source pool.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 17:50:17 +01:00
user
a1c238d4a1 docs: update README.md with named pools and multi-Tor
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:21:54 +01:00
user
5f52c83aca docs: update PROJECT.md with named pools and recent features
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:19:04 +01:00
user
ed9bad9024 docs: update TODO.md with pool-related backlog items
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:07:18 +01:00
user
9ed328ceac docs: update ROADMAP.md with v0.2.0 milestones
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:06:38 +01:00
user
44d61727ab docs: update TASKS.md with recent features
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:39:06 +01:00
user
29b4a36863 feat: named proxy pools with per-listener assignment
Add proxy_pools: top-level config (dict of name -> pool config) so
listeners can draw from different proxy sources. Each pool has
independent sources, health testing, state persistence, and refresh
cycles.

- PoolSourceConfig gains mitm: bool|None for API ?mitm=0/1 filtering
- ListenerConfig gains pool_name for named pool assignment
- ProxyPool gains name param with prefixed log messages and
  per-name state file derivation (pool-{name}.json)
- server.py replaces single proxy_pool with proxy_pools dict,
  validates listener pool references at startup, per-listener closure
- API /pool merges all pools (with pool field on multi-pool entries),
  /status and /config expose per-pool summaries
- Backward compat: singular proxy_pool: registers as "default"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 11:33:53 +01:00
user
288bd95f62 feat: multi-Tor round-robin via tor_nodes config
New top-level tor_nodes list distributes traffic across multiple Tor
SOCKS proxies. First hop is replaced at connection time by round-robin
selection; health tests also rotate across all nodes. FirstHopPools
are created for each node when pool_size > 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:12:58 +01:00
user
b3966c9a9f feat: dynamic health test concurrency
Auto-scale test concurrency to ~10% of proxy count, capped by
test_concurrency config ceiling (default raised from 5 to 25).
Prevents saturating upstream Tor when pool size varies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:09:44 +01:00
user
d4e3638143 feat: per-listener latency tracking
Each listener now tracks chain setup latency independently via a
dict[str, LatencyTracker] on Metrics. The global aggregate stays for
summary output. /status embeds per-listener latency on each listener
entry; /metrics includes a listener_latency map keyed by host:port.
2026-02-18 08:14:09 +01:00
user
b8f7217e43 feat: connection rate and chain latency metrics
Add RateTracker (rolling deque, events/sec) and LatencyTracker (circular
buffer, p50/p95/p99 in ms) to the Metrics class.  Both are recorded in
_handle_client and exposed via summary(), to_dict(), /status, and /metrics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 00:16:46 +01:00
user
e7de479c88 fix: enable cprofile in compose command 2026-02-17 22:37:09 +01:00
user
28c9830f56 docs: reorder listeners -- deepest chain on default port
:1080 = Tor + 2 pool hops, :1081 = Tor + 1, :1082 = Tor only.
2026-02-17 22:06:40 +01:00
user
7dc3926f48 feat: multi-listener with configurable proxy chaining
Each listener binds to its own port with an independent chain.
The "pool" keyword in a chain appends a random alive proxy from
the shared pool; multiple pool entries = multiple hops.

  :1080 -> Tor only (0 pool hops)
  :1081 -> Tor + 1 pool proxy
  :1082 -> Tor + 2 pool proxies

Shared resources (ProxyPool, Tor, metrics, semaphore, API) are
reused across listeners. FirstHopPool is shared per unique first
hop. Backward compatible: old listen/chain format still works.
2026-02-17 22:03:37 +01:00
user
ba60d087c0 fix: mark proxies alive incrementally during health tests
Proxies that pass the TLS handshake are now immediately added to the
alive list instead of waiting for the entire batch to complete. On
cold start with large pools, this means proxies become available
within seconds rather than waiting 30+ minutes.
2026-02-17 19:02:36 +01:00
user
aac69f6a3e fix: always defer health tests to background on startup
Cold start with large pools blocked the server for the entire test
duration. Now start() always defers health testing so the server
listens immediately. Proxies become available as tests complete.
2026-02-17 18:58:06 +01:00
user
6d9a21ac02 docs: update TASKS.md with TLS health check completion 2026-02-17 18:30:02 +01:00
user
e78fc8dc3c feat: replace HTTP health check with TLS handshake
Replace _http_check (HTTP GET to httpbin.org) with _tls_check that
performs a TLS handshake through the proxy chain. Multiple targets
(google, cloudflare, amazon) rotated round-robin eliminate the single
point of failure. Lighter, faster, harder to block than HTTP.

- Add test_targets config field (replaces test_url)
- Backward compat: legacy test_url extracts hostname automatically
- Add ssl.create_default_context() and round-robin index to ProxyPool
- Update docs (example.yaml, USAGE.md, CHEATSHEET.md)
2026-02-17 18:26:21 +01:00
user
3638c607da fix: show exception class name when pool source error message is empty
TimeoutError.__str__() returns '' in Python, causing truncated log
lines like "source ... failed: ". Fall back to the class name.

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