Files
s5p/docs/CHEATSHEET.md
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

9.6 KiB

s5p -- Cheatsheet

CLI

s5p                                         # direct, listen :1080
s5p -C socks5://127.0.0.1:9050             # through Tor
s5p -C socks5://tor:9050,http://px:8080    # Tor + HTTP proxy
s5p -c config/s5p.yaml                      # from config file
s5p -l 0.0.0.0:9999                         # custom listen address
s5p -t 30                                   # 30s per-hop timeout
s5p -v                                      # debug logging
s5p -q                                      # errors only
s5p -S http://api:8081/proxies              # proxy source API
s5p -r 5                                   # retry up to 5 proxies
s5p -m 512                                 # max concurrent connections
s5p --api 127.0.0.1:1081                   # enable control API
s5p --cprofile                              # profile to s5p.prof
s5p --cprofile out.prof                     # profile to custom file
s5p --tracemalloc                           # memory profile (top 10)
s5p --tracemalloc 20                        # memory profile (top 20)

Systemd

make install-service                            # install unit + reload
sudo systemctl enable --now s5p                 # enable + start
sudo systemctl status s5p                       # check status
sudo systemctl restart s5p                      # restart
sudo systemctl stop s5p                         # stop
journalctl -u s5p -f                            # follow logs
journalctl -u s5p --since "5 min ago"           # recent logs

Container

make build   # podman-compose build
make up      # podman-compose up -d
make logs    # podman-compose logs -f
make down    # podman-compose down

Volumes: ./config/s5p.yaml (ro), ~/.cache/s5p/data (pool state + profiles) Dev override: compose.yaml mounts ./src (ro) over the baked-in source.

CI

Gitea Actions runs on push to main:

  1. ruff check + pytest (test)
  2. gitleaks detect (secrets scan)
  3. Build + push harbor.mymx.me/s5p/s5p:latest

Secrets: HARBOR_USER / HARBOR_PASS (configured in Gitea repo settings).

Config

cp config/example.yaml config/s5p.yaml     # create live config (gitignored)

Multi-Listener (config)

listeners:
  - listen: 0.0.0.0:1080
    pool: clean                              # default for bare "pool"
    chain:
      - socks5://127.0.0.1:9050
      - pool                                 # Tor + 2 clean hops
      - pool
  - listen: 0.0.0.0:1081
    pool: clean
    chain:
      - socks5://127.0.0.1:9050
      - pool:clean                           # per-hop: explicit clean
      - pool:mitm                            # per-hop: explicit mitm
  - listen: 0.0.0.0:1082
    chain:
      - socks5://127.0.0.1:9050             # Tor only
  - listen: 0.0.0.0:1083
    chain:
      - socks5://127.0.0.1:9050
      - [pool:clean, pool:mitm]              # random choice per connection
      - [pool:clean, pool:mitm]              # independent random choice

Per-hop pool: pool = listener default, pool:name = explicit pool, [pool:a, pool:b] = random choice from candidates.

Bypass Rules (config)

listeners:
  - listen: 0.0.0.0:1080
    bypass:
      - 127.0.0.0/8        # CIDR
      - 10.0.0.0/8         # CIDR
      - 192.168.0.0/16     # CIDR
      - localhost           # exact hostname
      - .local             # domain suffix
    chain:
      - socks5://127.0.0.1:9050
      - pool
Pattern Type Matches
10.0.0.0/8 CIDR IPs in network
127.0.0.1 Exact IP That IP only
localhost Exact host String equal
.local Suffix *.local and local

Listener Authentication (config)

listeners:
  - listen: 0.0.0.0:1080
    auth:
      alice: s3cret
      bob: hunter2
    chain:
      - socks5://127.0.0.1:9050
      - pool
curl -x socks5h://alice:s3cret@127.0.0.1:1080 https://example.com

No auth: key = no authentication required (default).

Listener Retry Override (config)

listeners:
  - listen: 0.0.0.0:1080
    retries: 5                               # override global retries
    chain:
      - socks5://127.0.0.1:9050
      - pool
  - listen: 0.0.0.0:1082
    chain:
      - socks5://127.0.0.1:9050             # 0 = use global default

Per-listener retries overrides the global retries setting. Set to 0 (or omit) to inherit the global value.

Pool Protocol Filter (config)

proxy_pools:
  socks_only:
    allowed_protos: [socks5]                 # reject http proxies
    sources:
      - url: http://api:8081/proxies/all

When set, proxies not matching allowed_protos are silently dropped during merge. Useful when a source returns mixed protocols but the pool should only serve a specific type.

Multi-Tor Round-Robin (config)

tor_nodes:                              # overrides first hop in all listeners
  - socks5://10.200.1.1:9050
  - socks5://10.200.1.254:9050
  - socks5://10.200.1.250:9050
  - socks5://10.200.1.13:9050

Performance Tuning (config)

max_connections: 256   # concurrent connection cap
pool_size: 8           # pre-warmed TCP conns to first hop (0 = off)
pool_max_idle: 30      # evict idle pooled conns (seconds)

Connection Pool (config)

pool_size: 8           # pre-warmed TCP connections per first hop (0 = off)
pool_max_idle: 30      # evict idle connections after N seconds

Pre-warms TCP connections to the first hop in the chain. Only the raw TCP connection is pooled -- SOCKS/HTTP negotiation consumes it. One pool is created per unique first hop (shared across listeners). Requires at least one hop in chain.

Setting Default Notes
pool_size 0 (off) Connections per first hop
pool_max_idle 30 Idle eviction in seconds

Named Proxy Pools (config)

proxy_pools:
  clean:
    sources:
      - url: http://10.200.1.250:8081/proxies/all
        mitm: false                  # adds ?mitm=0
    state_file: /data/pool-clean.json
    refresh: 300
    test_interval: 120
    max_fails: 3
  mitm:
    sources:
      - url: http://10.200.1.250:8081/proxies/all
        mitm: true                   # adds ?mitm=1
    state_file: /data/pool-mitm.json
    refresh: 300
    test_interval: 120
    max_fails: 3

Singular proxy_pool: still works (becomes pool "default").

Source Filters (proxy_pool sources)

Filter Values Query param
proto socks5/socks4/http ?proto=...
country ISO alpha-2 ?country=...
limit integer ?limit=...
mitm true/false ?mitm=1 / ?mitm=0

Tor Control Port (config)

tor:
  control_port: 9051
  password: ""               # or cookie_file: /path/to/cookie
  newnym_interval: 60        # auto-rotate every 60s (0 = manual)
curl -s http://127.0.0.1:1081/tor | jq .                  # status
curl -s -X POST http://127.0.0.1:1081/tor/newnym | jq .   # new circuit

Hot Reload

kill -HUP $(pidof s5p)                    # reload config
podman kill --signal HUP s5p              # container reload

Proxy File Format

# one proxy URL per line
socks5://1.2.3.4:1080
socks5://user:pass@5.6.7.8:1080
http://proxy.example.com:8080

Proxy URLs

socks5://host:port
socks5://user:pass@host:port
socks4://host:port
http://host:port
http://user:pass@host:port

Control API

s5p --api 127.0.0.1:1081 -c config/s5p.yaml    # enable API

curl -s http://127.0.0.1:1081/status | jq .     # runtime status
curl -s http://127.0.0.1:1081/metrics           # prometheus metrics
curl -s http://127.0.0.1:1081/pool | jq .       # all proxies
curl -s http://127.0.0.1:1081/pool/alive | jq . # alive only
curl -s http://127.0.0.1:1081/config | jq .     # current config
curl -s -X POST http://127.0.0.1:1081/reload    # reload config
curl -s -X POST http://127.0.0.1:1081/pool/test # health test now

Testing

# Check exit IP
curl -x socks5h://127.0.0.1:1080 https://httpbin.org/ip

# Tor check
curl -x socks5h://127.0.0.1:1080 https://check.torproject.org/api/ip

# Verbose curl
curl -v -x socks5h://127.0.0.1:1080 https://example.com

# With timeout
curl --max-time 30 -x socks5h://127.0.0.1:1080 https://example.com

Profiling

# Enable in compose.yaml: uncomment the command line
python -m pstats s5p.prof                   # interactive stats viewer
python -m pstats ~/.cache/s5p/s5p.prof      # container profile output

Metrics Log

metrics: conn=1842 ok=1790 fail=52 retries=67 active=3 in=50.0M out=1.0G rate=4.72/s p50=198.3ms p95=890.1ms up=1h01m01s pool=42/65

Prometheus Metrics (/metrics)

curl -s http://127.0.0.1:1081/metrics
# TYPE s5p_connections counter
s5p_connections_total 1842
# TYPE s5p_active_connections gauge
s5p_active_connections 3
# TYPE s5p_pool_proxies_alive gauge
s5p_pool_proxies_alive{pool="clean"} 30
# TYPE s5p_chain_latency_seconds summary
s5p_chain_latency_seconds{quantile="0.5"} 0.198300
s5p_chain_latency_seconds{quantile="0.95"} 0.890100
# EOF

OpenMetrics format. Use /status for JSON equivalent.

Troubleshooting

Symptom Check
Connection refused Is Tor running? ss -tlnp | grep 9050
Timeout Increase -t, check proxy reachability
DNS leak Use socks5h:// (not socks5://) in client
Auth failed Verify credentials in proxy URL
Port in use fuser -k 1080/tcp to free the port
Proxy keeps failing Backoff penalizes for 60s; check pool= in metrics
"static chain unreachable" Tor/upstream hop is down; pool tests skipped
Slow startup Normal on cold start; warm restarts serve instantly from state