feat: v0.3.0 stabilization -- systemd, tests, API docs
- 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
This commit is contained in:
297
docs/USAGE.md
297
docs/USAGE.md
@@ -523,56 +523,297 @@ api_listen: 127.0.0.1:1081
|
||||
s5p --api 127.0.0.1:1081 -c config/s5p.yaml
|
||||
```
|
||||
|
||||
### Read endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `GET` | `/status` | Combined summary: uptime, metrics, pool stats, chain |
|
||||
| `GET` | `/metrics` | Full metrics counters (connections, bytes, rate, latency) |
|
||||
| `GET` | `/pool` | All proxies with per-entry state |
|
||||
| `GET` | `/pool/alive` | Alive proxies only |
|
||||
| `GET` | `/config` | Current runtime config (sanitized) |
|
||||
|
||||
### Write endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `POST` | `/reload` | Re-read config file (replaces SIGHUP) |
|
||||
| `POST` | `/pool/test` | Trigger immediate health test cycle |
|
||||
| `POST` | `/pool/refresh` | Trigger immediate source re-fetch |
|
||||
|
||||
All responses are `application/json`. Errors return `{"error": "message"}` with
|
||||
appropriate status code (400, 404, 405, 500).
|
||||
|
||||
### Examples
|
||||
Settings that require a restart: `listen`, `chain`, `pool_size`, `pool_max_idle`, `api_listen`.
|
||||
|
||||
### API Reference
|
||||
|
||||
#### `GET /status`
|
||||
|
||||
Combined runtime summary: uptime, metrics, pool stats, listeners.
|
||||
|
||||
```bash
|
||||
# Runtime status
|
||||
curl -s http://127.0.0.1:1081/status | jq .
|
||||
```
|
||||
|
||||
# Full metrics
|
||||
```json
|
||||
{
|
||||
"uptime": 3661.2,
|
||||
"connections": 1842,
|
||||
"success": 1790,
|
||||
"failed": 52,
|
||||
"active": 3,
|
||||
"bytes_in": 52428800,
|
||||
"bytes_out": 1073741824,
|
||||
"rate": 4.72,
|
||||
"latency": {"count": 1000, "min": 45.2, "max": 2841.7, "avg": 312.4, "p50": 198.3, "p95": 890.1, "p99": 1523.6},
|
||||
"pool": {"alive": 42, "total": 65},
|
||||
"pools": {
|
||||
"clean": {"alive": 30, "total": 45},
|
||||
"mitm": {"alive": 12, "total": 20}
|
||||
},
|
||||
"tor_nodes": ["socks5://10.200.1.1:9050", "socks5://10.200.1.254:9050"],
|
||||
"listeners": [
|
||||
{
|
||||
"listen": "0.0.0.0:1080",
|
||||
"chain": ["socks5://10.200.1.13:9050"],
|
||||
"pool_hops": 2,
|
||||
"pool": "clean",
|
||||
"auth": true,
|
||||
"latency": {"count": 500, "p50": 1800.2, "p95": 8200.1, "p99": 10500.3, "...": "..."}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `uptime` | float | Seconds since server start |
|
||||
| `connections` | int | Total incoming connections |
|
||||
| `success` | int | Successfully relayed |
|
||||
| `failed` | int | All retries exhausted |
|
||||
| `active` | int | Currently relaying |
|
||||
| `bytes_in` | int | Bytes client -> remote |
|
||||
| `bytes_out` | int | Bytes remote -> client |
|
||||
| `rate` | float | Connections/sec (rolling window) |
|
||||
| `latency` | object/null | Aggregate chain setup latency (ms), null if no samples |
|
||||
| `pool` | object | Aggregate pool counts (present when pool active) |
|
||||
| `pools` | object | Per-pool counts (present when multiple pools) |
|
||||
| `tor_nodes` | array | Tor node URLs (present when configured) |
|
||||
| `listeners` | array | Per-listener state with chain, pool, latency |
|
||||
| `listeners[].auth` | bool | Present and `true` when auth is enabled |
|
||||
|
||||
#### `GET /metrics`
|
||||
|
||||
Full metrics counters with rate, latency percentiles, and per-listener breakdown.
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1081/metrics | jq .
|
||||
```
|
||||
|
||||
# Pool state (all proxies)
|
||||
```json
|
||||
{
|
||||
"connections": 1842,
|
||||
"success": 1790,
|
||||
"failed": 52,
|
||||
"retries": 67,
|
||||
"auth_failures": 0,
|
||||
"active": 3,
|
||||
"bytes_in": 52428800,
|
||||
"bytes_out": 1073741824,
|
||||
"uptime": 3661.2,
|
||||
"rate": 4.72,
|
||||
"latency": {
|
||||
"count": 1000, "min": 45.2, "max": 2841.7,
|
||||
"avg": 312.4, "p50": 198.3, "p95": 890.1, "p99": 1523.6
|
||||
},
|
||||
"listener_latency": {
|
||||
"0.0.0.0:1080": {"count": 500, "min": 800.1, "max": 12400.3, "avg": 2100.5, "p50": 1800.2, "p95": 8200.1, "p99": 10500.3},
|
||||
"0.0.0.0:1081": {"count": 300, "min": 400.5, "max": 5200.1, "avg": 1200.3, "p50": 1000.1, "p95": 3500.2, "p99": 4800.7}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `retries` | int | Total retry attempts |
|
||||
| `auth_failures` | int | SOCKS5 auth failures |
|
||||
| `latency` | object/null | Aggregate latency stats (ms), null if no samples |
|
||||
| `latency.count` | int | Number of samples in buffer (max 1000) |
|
||||
| `latency.p50/p95/p99` | float | Percentile latency (ms) |
|
||||
| `listener_latency` | object | Per-listener latency, keyed by `host:port` |
|
||||
|
||||
#### `GET /pool`
|
||||
|
||||
All proxies with per-entry state.
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1081/pool | jq .
|
||||
```
|
||||
|
||||
# Alive proxies only
|
||||
```json
|
||||
{
|
||||
"alive": 42,
|
||||
"total": 65,
|
||||
"pools": {
|
||||
"clean": {"alive": 30, "total": 45},
|
||||
"mitm": {"alive": 12, "total": 20}
|
||||
},
|
||||
"proxies": {
|
||||
"socks5://1.2.3.4:1080": {
|
||||
"alive": true,
|
||||
"fails": 0,
|
||||
"tests": 12,
|
||||
"last_ok": 1708012345.6,
|
||||
"last_test": 1708012345.6,
|
||||
"last_seen": 1708012300.0,
|
||||
"pool": "clean"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `alive` | int | Total alive proxies across all pools |
|
||||
| `total` | int | Total proxies across all pools |
|
||||
| `pools` | object | Per-pool counts (present when multiple pools) |
|
||||
| `proxies` | object | Keyed by proxy URL |
|
||||
| `proxies[].alive` | bool | Currently passing health tests |
|
||||
| `proxies[].fails` | int | Consecutive failures |
|
||||
| `proxies[].tests` | int | Total health tests performed |
|
||||
| `proxies[].last_ok` | float | Unix timestamp of last successful test |
|
||||
| `proxies[].last_test` | float | Unix timestamp of last test (pass or fail) |
|
||||
| `proxies[].last_seen` | float | Unix timestamp of last source refresh that included this proxy |
|
||||
| `proxies[].pool` | string | Pool name (present when multiple pools) |
|
||||
|
||||
#### `GET /pool/alive`
|
||||
|
||||
Same schema as `/pool`, filtered to alive proxies only.
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1081/pool/alive | jq '.proxies | length'
|
||||
```
|
||||
|
||||
# Current config
|
||||
#### `GET /config`
|
||||
|
||||
Current runtime config (sanitized -- passwords are never exposed).
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1081/config | jq .
|
||||
```
|
||||
|
||||
# Reload config (like SIGHUP)
|
||||
```json
|
||||
{
|
||||
"timeout": 10,
|
||||
"retries": 3,
|
||||
"log_level": "info",
|
||||
"max_connections": 256,
|
||||
"pool_size": 0,
|
||||
"listeners": [
|
||||
{
|
||||
"listen": "0.0.0.0:1080",
|
||||
"chain": ["socks5://10.200.1.13:9050"],
|
||||
"pool_hops": 2,
|
||||
"pool": "clean",
|
||||
"auth_users": 2
|
||||
}
|
||||
],
|
||||
"tor_nodes": ["socks5://10.200.1.1:9050"],
|
||||
"proxy_pools": {
|
||||
"clean": {
|
||||
"sources": [{"url": "http://10.200.1.250:8081/proxies/all", "mitm": false}],
|
||||
"refresh": 300,
|
||||
"test_interval": 120,
|
||||
"max_fails": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `timeout` | float | Per-hop connection timeout (seconds) |
|
||||
| `retries` | int | Max connection attempts per request |
|
||||
| `log_level` | string | Current log level |
|
||||
| `max_connections` | int | Concurrent connection cap |
|
||||
| `pool_size` | int | Pre-warmed TCP connections to first hop |
|
||||
| `listeners` | array | Listener configs |
|
||||
| `listeners[].auth_users` | int | Number of auth users (present when auth enabled) |
|
||||
| `tor_nodes` | array | Tor node URLs (present when configured) |
|
||||
| `proxy_pools` | object | Pool configs (present when pools configured) |
|
||||
|
||||
#### `GET /tor`
|
||||
|
||||
Tor controller status.
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1081/tor | jq .
|
||||
```
|
||||
|
||||
```json
|
||||
{"enabled": true, "connected": true, "last_newnym": 45.2, "newnym_interval": 60}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `enabled` | bool | Whether Tor control is configured |
|
||||
| `connected` | bool | Whether connected to Tor control port |
|
||||
| `last_newnym` | float/null | Seconds since last NEWNYM signal |
|
||||
| `newnym_interval` | int | Auto-rotation interval (0 = manual) |
|
||||
|
||||
Returns `{"enabled": false}` when Tor control is not configured.
|
||||
|
||||
#### `POST /reload`
|
||||
|
||||
Re-read config file (equivalent to SIGHUP).
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1081/reload | jq .
|
||||
```
|
||||
|
||||
# Trigger health tests now
|
||||
```json
|
||||
{"ok": true}
|
||||
```
|
||||
|
||||
Returns `{"error": "..."}` (500) on failure.
|
||||
|
||||
#### `POST /pool/test`
|
||||
|
||||
Trigger immediate health test cycle for all pools.
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1081/pool/test | jq .
|
||||
```
|
||||
|
||||
# Re-fetch proxy sources now
|
||||
```json
|
||||
{"ok": true}
|
||||
```
|
||||
|
||||
Returns `{"error": "no proxy pool configured"}` (400) when no pool is active.
|
||||
|
||||
#### `POST /pool/refresh`
|
||||
|
||||
Trigger immediate source re-fetch for all pools.
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1081/pool/refresh | jq .
|
||||
```
|
||||
|
||||
Settings that require a restart: `listen`, `chain`, `pool_size`, `pool_max_idle`, `api_listen`.
|
||||
```json
|
||||
{"ok": true}
|
||||
```
|
||||
|
||||
Returns `{"error": "no proxy pool configured"}` (400) when no pool is active.
|
||||
|
||||
#### `POST /tor/newnym`
|
||||
|
||||
Request new Tor circuit (NEWNYM signal).
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1081/tor/newnym | jq .
|
||||
```
|
||||
|
||||
```json
|
||||
{"ok": true}
|
||||
```
|
||||
|
||||
Returns `{"ok": false, "reason": "rate-limited or not connected"}` when the
|
||||
signal cannot be sent. Returns `{"error": "tor control not configured"}` (400)
|
||||
when Tor control is not configured.
|
||||
|
||||
#### Error responses
|
||||
|
||||
All endpoints return errors as JSON with appropriate HTTP status codes:
|
||||
|
||||
| Status | Meaning | Example |
|
||||
|--------|---------|---------|
|
||||
| 400 | Bad request | `{"error": "no proxy pool configured"}` |
|
||||
| 404 | Unknown path | `{"error": "not found"}` |
|
||||
| 405 | Wrong method | `{"error": "use GET for /status"}` |
|
||||
| 500 | Server error | `{"error": "reload not available"}` |
|
||||
|
||||
## Tor Control Port
|
||||
|
||||
|
||||
Reference in New Issue
Block a user