@rvanbaalen/roxyproxy 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +658 -0
- package/dist/cli/banner.d.ts +6 -0
- package/dist/cli/banner.js +30 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/commands/clear.d.ts +2 -0
- package/dist/cli/commands/clear.js +35 -0
- package/dist/cli/commands/clear.js.map +1 -0
- package/dist/cli/commands/proxy-off.d.ts +2 -0
- package/dist/cli/commands/proxy-off.js +19 -0
- package/dist/cli/commands/proxy-off.js.map +1 -0
- package/dist/cli/commands/proxy-on.d.ts +2 -0
- package/dist/cli/commands/proxy-on.js +20 -0
- package/dist/cli/commands/proxy-on.js.map +1 -0
- package/dist/cli/commands/request.d.ts +2 -0
- package/dist/cli/commands/request.js +23 -0
- package/dist/cli/commands/request.js.map +1 -0
- package/dist/cli/commands/requests.d.ts +2 -0
- package/dist/cli/commands/requests.js +46 -0
- package/dist/cli/commands/requests.js.map +1 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.js +41 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +44 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +2 -0
- package/dist/cli/commands/stop.js +52 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/trust-ca.d.ts +2 -0
- package/dist/cli/commands/trust-ca.js +134 -0
- package/dist/cli/commands/trust-ca.js.map +1 -0
- package/dist/cli/format.d.ts +3 -0
- package/dist/cli/format.js +112 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +35 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/interactive.d.ts +1 -0
- package/dist/cli/interactive.js +408 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/system-proxy.d.ts +14 -0
- package/dist/cli/system-proxy.js +115 -0
- package/dist/cli/system-proxy.js.map +1 -0
- package/dist/server/api.d.ts +10 -0
- package/dist/server/api.js +109 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/config.d.ts +6 -0
- package/dist/server/config.js +66 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/events.d.ts +20 -0
- package/dist/server/events.js +49 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/index.d.ts +20 -0
- package/dist/server/index.js +91 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/proxy.d.ts +27 -0
- package/dist/server/proxy.js +245 -0
- package/dist/server/proxy.js.map +1 -0
- package/dist/server/ssl.d.ts +17 -0
- package/dist/server/ssl.js +88 -0
- package/dist/server/ssl.js.map +1 -0
- package/dist/shared/types.d.ts +53 -0
- package/dist/shared/types.js +10 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/storage/cleanup.d.ts +11 -0
- package/dist/storage/cleanup.js +32 -0
- package/dist/storage/cleanup.js.map +1 -0
- package/dist/storage/db.d.ts +17 -0
- package/dist/storage/db.js +152 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/ui/assets/index-C8qts74N.js +9 -0
- package/dist/ui/assets/index-CrfXQK5U.css +2 -0
- package/dist/ui/index.html +13 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Robin van Baalen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
# RoxyProxy
|
|
2
|
+
|
|
3
|
+
HTTP/HTTPS intercepting proxy with a CLI and web UI. Captures traffic, stores it in SQLite, and makes it queryable -- by humans and LLMs alike.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Interactive Mode](#interactive-mode)
|
|
10
|
+
- [CLI Commands](#cli-commands)
|
|
11
|
+
- [start](#start)
|
|
12
|
+
- [stop](#stop)
|
|
13
|
+
- [status](#status)
|
|
14
|
+
- [requests](#requests)
|
|
15
|
+
- [request](#request)
|
|
16
|
+
- [clear](#clear)
|
|
17
|
+
- [trust-ca](#trust-ca)
|
|
18
|
+
- [proxy-on](#proxy-on-macos)
|
|
19
|
+
- [proxy-off](#proxy-off-macos)
|
|
20
|
+
- [Web UI](#web-ui)
|
|
21
|
+
- [HTTPS Interception](#https-interception)
|
|
22
|
+
- [System Proxy](#system-proxy-macos)
|
|
23
|
+
- [Configuration](#configuration)
|
|
24
|
+
- [REST API](#rest-api)
|
|
25
|
+
- [Architecture](#architecture)
|
|
26
|
+
- [Development](#development)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone <repo-url>
|
|
34
|
+
cd roxyproxy
|
|
35
|
+
npm install
|
|
36
|
+
npm run build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To make the `roxyproxy` command available globally:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm link
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Start the proxy (default: proxy on :8080, web UI on :8081)
|
|
49
|
+
roxyproxy start
|
|
50
|
+
|
|
51
|
+
# Route traffic through it
|
|
52
|
+
curl -x http://127.0.0.1:8080 http://httpbin.org/get
|
|
53
|
+
|
|
54
|
+
# View captured traffic
|
|
55
|
+
roxyproxy requests --format table
|
|
56
|
+
|
|
57
|
+
# Open the web UI
|
|
58
|
+
open http://127.0.0.1:8081
|
|
59
|
+
|
|
60
|
+
# Stop
|
|
61
|
+
roxyproxy stop
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For HTTPS interception, trust the CA certificate first:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
roxyproxy start
|
|
68
|
+
roxyproxy trust-ca
|
|
69
|
+
curl -x http://127.0.0.1:8080 https://api.example.com/endpoint
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Interactive Mode
|
|
73
|
+
|
|
74
|
+
Running `roxyproxy` with no arguments launches an interactive terminal menu:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
roxyproxy
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The interactive menu provides access to all features:
|
|
81
|
+
|
|
82
|
+
- **Start/Stop proxy** -- toggle the proxy server on and off
|
|
83
|
+
- **Status** -- view proxy stats (port, request count, DB size)
|
|
84
|
+
- **View requests** -- browse captured traffic in the terminal
|
|
85
|
+
- **Clear traffic** -- delete all captured requests
|
|
86
|
+
- **Open web UI** -- opens the dashboard in your browser (auto-starts the proxy if needed)
|
|
87
|
+
- **Trust CA certificate** -- install the CA cert for HTTPS interception
|
|
88
|
+
- **Enable/Disable system proxy** -- route all macOS traffic through RoxyProxy
|
|
89
|
+
- **Quit** -- stop the proxy and exit
|
|
90
|
+
|
|
91
|
+
Use arrow keys to navigate and Enter to select. Press `q` or Ctrl+C to quit.
|
|
92
|
+
|
|
93
|
+
The interactive mode stays in sync with the web UI -- if you stop the proxy from the web dashboard, the CLI menu updates within a second, and vice versa.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## CLI Commands
|
|
98
|
+
|
|
99
|
+
### start
|
|
100
|
+
|
|
101
|
+
Start the proxy server in the foreground.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
roxyproxy start [options]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Option | Default | Description |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `--port <number>` | `8080` | Proxy listening port |
|
|
110
|
+
| `--ui-port <number>` | `8081` | Web UI and API port |
|
|
111
|
+
| `--db-path <path>` | `~/.roxyproxy/data.db` | SQLite database location |
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Default ports
|
|
115
|
+
roxyproxy start
|
|
116
|
+
|
|
117
|
+
# Custom ports
|
|
118
|
+
roxyproxy start --port 9000 --ui-port 9001
|
|
119
|
+
|
|
120
|
+
# Custom database location
|
|
121
|
+
roxyproxy start --db-path /tmp/proxy.db
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The process writes its PID to `~/.roxyproxy/pid` and responds to SIGINT/SIGTERM for graceful shutdown.
|
|
125
|
+
|
|
126
|
+
### stop
|
|
127
|
+
|
|
128
|
+
Stop the running proxy server.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
roxyproxy stop [options]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Option | Default | Description |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `--ui-port <number>` | `8081` | API port to send shutdown request to |
|
|
137
|
+
|
|
138
|
+
Sends a graceful shutdown request via the API. Falls back to SIGTERM via the PID file if the API is unreachable.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
roxyproxy stop
|
|
142
|
+
roxyproxy stop --ui-port 9001
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### status
|
|
146
|
+
|
|
147
|
+
Show proxy status.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
roxyproxy status [options]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Option | Default | Description |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `--ui-port <number>` | `8081` | API port to query |
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
roxyproxy status
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Output:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
Status Running
|
|
165
|
+
Proxy port 8080
|
|
166
|
+
Requests 142
|
|
167
|
+
DB Size 3.2MB
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### requests
|
|
171
|
+
|
|
172
|
+
Query captured requests from the database.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
roxyproxy requests [options]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Option | Default | Description |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| `--host <pattern>` | | Filter by hostname (substring match) |
|
|
181
|
+
| `--status <code>` | | Filter by HTTP status code |
|
|
182
|
+
| `--method <method>` | | Filter by HTTP method |
|
|
183
|
+
| `--search <pattern>` | | Search URLs (substring match) |
|
|
184
|
+
| `--since <time>` | | After this time (Unix ms or ISO date) |
|
|
185
|
+
| `--until <time>` | | Before this time (Unix ms or ISO date) |
|
|
186
|
+
| `--limit <n>` | `100` | Maximum number of results |
|
|
187
|
+
| `--format <format>` | `json` | Output format: `json` or `table` |
|
|
188
|
+
| `--db-path <path>` | `~/.roxyproxy/data.db` | Database location |
|
|
189
|
+
|
|
190
|
+
The default JSON output is designed for piping to `jq` or feeding to LLMs:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# All 500 errors
|
|
194
|
+
roxyproxy requests --status 500
|
|
195
|
+
|
|
196
|
+
# POST requests to a specific host
|
|
197
|
+
roxyproxy requests --host api.example.com --method POST
|
|
198
|
+
|
|
199
|
+
# Search URLs
|
|
200
|
+
roxyproxy requests --search "/api/v2"
|
|
201
|
+
|
|
202
|
+
# Human-readable table
|
|
203
|
+
roxyproxy requests --format table --limit 20
|
|
204
|
+
|
|
205
|
+
# Time-bounded query
|
|
206
|
+
roxyproxy requests --since "2024-01-15T00:00:00Z" --until "2024-01-16T00:00:00Z"
|
|
207
|
+
|
|
208
|
+
# Pipe to jq
|
|
209
|
+
roxyproxy requests --host stripe.com | jq '.data[].url'
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### request
|
|
213
|
+
|
|
214
|
+
Show full details of a single captured request, including headers and bodies.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
roxyproxy request <id> [options]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
| Option | Default | Description |
|
|
221
|
+
|---|---|---|
|
|
222
|
+
| `--format <format>` | `json` | Output format: `json` or `table` |
|
|
223
|
+
| `--db-path <path>` | `~/.roxyproxy/data.db` | Database location |
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
roxyproxy request a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
227
|
+
roxyproxy request a1b2c3d4-e5f6-7890-abcd-ef1234567890 --format table
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### clear
|
|
231
|
+
|
|
232
|
+
Delete all captured traffic from the database.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
roxyproxy clear [options]
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
| Option | Default | Description |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| `--ui-port <number>` | `8081` | API port |
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
roxyproxy clear
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### trust-ca
|
|
247
|
+
|
|
248
|
+
Install and trust the RoxyProxy CA certificate for HTTPS interception.
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
roxyproxy trust-ca [options]
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| Option | Description |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `--no-interactive` | Skip prompts; print cert path and manual instructions |
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Interactive (prompts for sudo password)
|
|
260
|
+
roxyproxy trust-ca
|
|
261
|
+
|
|
262
|
+
# Non-interactive (CI, scripts)
|
|
263
|
+
roxyproxy trust-ca --no-interactive
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
See [HTTPS Interception](#https-interception) for details.
|
|
267
|
+
|
|
268
|
+
### proxy-on (macOS)
|
|
269
|
+
|
|
270
|
+
Configure RoxyProxy as the system-wide HTTP/HTTPS proxy.
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
roxyproxy proxy-on [options]
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
| Option | Default | Description |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| `--port <number>` | `8080` | Proxy port |
|
|
279
|
+
| `--service <name>` | auto-detected | Network service (e.g., "Wi-Fi", "Ethernet") |
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
roxyproxy proxy-on
|
|
283
|
+
roxyproxy proxy-on --port 9000 --service "Wi-Fi"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### proxy-off (macOS)
|
|
287
|
+
|
|
288
|
+
Remove RoxyProxy from system proxy settings.
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
roxyproxy proxy-off [options]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
| Option | Default | Description |
|
|
295
|
+
|---|---|---|
|
|
296
|
+
| `--service <name>` | auto-detected | Network service |
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
roxyproxy proxy-off
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Web UI
|
|
305
|
+
|
|
306
|
+
Available at `http://127.0.0.1:8081` when the proxy is running.
|
|
307
|
+
|
|
308
|
+
### Features
|
|
309
|
+
|
|
310
|
+
- **Live traffic stream** -- requests appear in real-time via Server-Sent Events
|
|
311
|
+
- **Historical traffic** -- previously captured requests load on page open
|
|
312
|
+
- **Sortable columns** -- click any column header to sort (Time, Method, Status, Host, Path, Duration, Size)
|
|
313
|
+
- **Resizable columns** -- drag column borders to adjust widths
|
|
314
|
+
- **Request detail panel** -- click any row to inspect headers and response body in a resizable side panel
|
|
315
|
+
- **Filters** -- filter by host, status code, HTTP method, or URL search
|
|
316
|
+
- **Proxy controls** -- start/stop the proxy and clear traffic directly from the UI
|
|
317
|
+
- **Live sync** -- start/stop state is synchronized between the web UI and CLI in real-time
|
|
318
|
+
|
|
319
|
+
### Keyboard Shortcuts
|
|
320
|
+
|
|
321
|
+
The filter bar is always accessible. Type in any filter field to narrow results instantly.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## HTTPS Interception
|
|
326
|
+
|
|
327
|
+
RoxyProxy performs HTTPS interception via a local Certificate Authority (CA).
|
|
328
|
+
|
|
329
|
+
### How it works
|
|
330
|
+
|
|
331
|
+
1. On first startup, RoxyProxy generates a root CA certificate and private key at `~/.roxyproxy/ca/`
|
|
332
|
+
2. When a client sends a CONNECT request (HTTPS), RoxyProxy:
|
|
333
|
+
- Accepts the tunnel
|
|
334
|
+
- Generates a per-domain certificate signed by the CA on the fly
|
|
335
|
+
- Terminates TLS with the client using the generated cert
|
|
336
|
+
- Opens a separate TLS connection to the real server
|
|
337
|
+
- Forwards traffic in both directions, capturing it along the way
|
|
338
|
+
|
|
339
|
+
### Setup
|
|
340
|
+
|
|
341
|
+
**Step 1: Start the proxy** (generates the CA if it doesn't exist)
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
roxyproxy start
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Step 2: Trust the CA certificate**
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
roxyproxy trust-ca
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
This runs the platform-specific trust command:
|
|
354
|
+
|
|
355
|
+
| Platform | What happens |
|
|
356
|
+
|---|---|
|
|
357
|
+
| **macOS** | Adds to System Keychain via `security add-trusted-cert` (requires sudo) |
|
|
358
|
+
| **Linux** | Copies to `/usr/local/share/ca-certificates/` and runs `update-ca-certificates` (requires sudo) |
|
|
359
|
+
| **Firefox** | Must be done manually: Settings > Privacy & Security > Certificates > View Certificates > Import `~/.roxyproxy/ca/ca.crt` |
|
|
360
|
+
|
|
361
|
+
**Step 3: Route HTTPS traffic through the proxy**
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Via explicit proxy flag
|
|
365
|
+
curl -x http://127.0.0.1:8080 https://api.example.com/data
|
|
366
|
+
|
|
367
|
+
# Or enable system-wide proxy (macOS)
|
|
368
|
+
roxyproxy proxy-on
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Certificate Details
|
|
372
|
+
|
|
373
|
+
| Property | Value |
|
|
374
|
+
|---|---|
|
|
375
|
+
| CA location | `~/.roxyproxy/ca/ca.crt` and `ca.key` |
|
|
376
|
+
| CA validity | 10 years |
|
|
377
|
+
| CA subject | "RoxyProxy CA" |
|
|
378
|
+
| Per-domain cert validity | 1 year |
|
|
379
|
+
| Key size | 2048-bit RSA |
|
|
380
|
+
| Signature algorithm | SHA-256 |
|
|
381
|
+
| Domain cert cache | LRU, default 500 entries (configurable) |
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## System Proxy (macOS)
|
|
386
|
+
|
|
387
|
+
On macOS, RoxyProxy can configure itself as the system-wide HTTP/HTTPS proxy. This routes all traffic from most applications through the proxy without needing per-app configuration.
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
# Enable
|
|
391
|
+
roxyproxy proxy-on
|
|
392
|
+
|
|
393
|
+
# Disable
|
|
394
|
+
roxyproxy proxy-off
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
This uses `networksetup` to set the proxy on your active network service (auto-detects Wi-Fi, Ethernet, or the first available interface).
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## Configuration
|
|
402
|
+
|
|
403
|
+
Configuration is loaded from (highest priority first):
|
|
404
|
+
|
|
405
|
+
1. CLI flags
|
|
406
|
+
2. `~/.roxyproxy/config.json`
|
|
407
|
+
3. Built-in defaults
|
|
408
|
+
|
|
409
|
+
### Config file
|
|
410
|
+
|
|
411
|
+
Create `~/.roxyproxy/config.json`:
|
|
412
|
+
|
|
413
|
+
```json
|
|
414
|
+
{
|
|
415
|
+
"proxyPort": 8080,
|
|
416
|
+
"uiPort": 8081,
|
|
417
|
+
"dbPath": "~/.roxyproxy/data.db",
|
|
418
|
+
"maxAge": "7d",
|
|
419
|
+
"maxDbSize": "500MB",
|
|
420
|
+
"maxBodySize": "1MB",
|
|
421
|
+
"certCacheSize": 500
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Options
|
|
426
|
+
|
|
427
|
+
| Field | Default | Description |
|
|
428
|
+
|---|---|---|
|
|
429
|
+
| `proxyPort` | `8080` | Proxy listening port |
|
|
430
|
+
| `uiPort` | `8081` | Web UI and REST API port |
|
|
431
|
+
| `dbPath` | `~/.roxyproxy/data.db` | SQLite database file path (supports `~`) |
|
|
432
|
+
| `maxAge` | `7d` | Auto-delete requests older than this |
|
|
433
|
+
| `maxDbSize` | `500MB` | Auto-delete oldest requests when DB exceeds this size |
|
|
434
|
+
| `maxBodySize` | `1MB` | Truncate request/response bodies larger than this |
|
|
435
|
+
| `certCacheSize` | `500` | Max per-domain SSL certificates cached in memory |
|
|
436
|
+
|
|
437
|
+
### Size and duration formats
|
|
438
|
+
|
|
439
|
+
Sizes accept: raw bytes (`1048576`), or human units (`1KB`, `10MB`, `1GB`).
|
|
440
|
+
|
|
441
|
+
Durations accept: raw milliseconds (`86400000`), or human units (`1s`, `5m`, `1h`, `7d`).
|
|
442
|
+
|
|
443
|
+
### Auto-cleanup
|
|
444
|
+
|
|
445
|
+
A background job runs every 5 minutes to enforce `maxAge` and `maxDbSize`:
|
|
446
|
+
|
|
447
|
+
- Deletes requests older than `maxAge`
|
|
448
|
+
- If the database still exceeds `maxDbSize`, deletes the oldest requests in batches
|
|
449
|
+
- Runs incremental vacuum to reclaim disk space
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## REST API
|
|
454
|
+
|
|
455
|
+
The API is available at `http://127.0.0.1:8081/api` when the proxy is running.
|
|
456
|
+
|
|
457
|
+
### Endpoints
|
|
458
|
+
|
|
459
|
+
#### `GET /api/requests`
|
|
460
|
+
|
|
461
|
+
Query captured requests. Returns paginated results.
|
|
462
|
+
|
|
463
|
+
Query parameters match the CLI `requests` command: `host`, `status`, `method`, `content_type`, `search`, `since`, `until`, `limit`, `offset`.
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
# All requests
|
|
467
|
+
curl http://127.0.0.1:8081/api/requests
|
|
468
|
+
|
|
469
|
+
# Filtered
|
|
470
|
+
curl "http://127.0.0.1:8081/api/requests?host=example.com&status=200&limit=50"
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Response:
|
|
474
|
+
|
|
475
|
+
```json
|
|
476
|
+
{
|
|
477
|
+
"data": [ { "id": "...", "timestamp": 1700000000000, "method": "GET", ... } ],
|
|
478
|
+
"total": 142,
|
|
479
|
+
"limit": 100,
|
|
480
|
+
"offset": 0
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
#### `GET /api/requests/:id`
|
|
485
|
+
|
|
486
|
+
Get full details for a single request, including headers and base64-encoded bodies.
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
curl http://127.0.0.1:8081/api/requests/a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
#### `DELETE /api/requests`
|
|
493
|
+
|
|
494
|
+
Delete all captured traffic.
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
curl -X DELETE http://127.0.0.1:8081/api/requests
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
#### `GET /api/status`
|
|
501
|
+
|
|
502
|
+
Get proxy status.
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
curl http://127.0.0.1:8081/api/status
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Response:
|
|
509
|
+
|
|
510
|
+
```json
|
|
511
|
+
{
|
|
512
|
+
"running": true,
|
|
513
|
+
"proxyPort": 8080,
|
|
514
|
+
"requestCount": 142,
|
|
515
|
+
"dbSizeBytes": 3358720
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
#### `POST /api/proxy/start`
|
|
520
|
+
|
|
521
|
+
Start the proxy server.
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
curl -X POST http://127.0.0.1:8081/api/proxy/start
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
#### `POST /api/proxy/stop`
|
|
528
|
+
|
|
529
|
+
Stop the proxy server (the API remains available).
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
curl -X POST http://127.0.0.1:8081/api/proxy/stop
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
#### `POST /api/shutdown`
|
|
536
|
+
|
|
537
|
+
Shut down the entire process (proxy + API + web UI).
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
curl -X POST http://127.0.0.1:8081/api/shutdown
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
#### `GET /api/events`
|
|
544
|
+
|
|
545
|
+
Server-Sent Events stream for real-time updates.
|
|
546
|
+
|
|
547
|
+
```bash
|
|
548
|
+
curl -N http://127.0.0.1:8081/api/events
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Events are named:
|
|
552
|
+
|
|
553
|
+
- `event: request` -- new captured request (data is the request record as JSON)
|
|
554
|
+
- `event: status` -- proxy state change (data is `{"running": true/false, "proxyPort": 8080}`)
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Architecture
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
┌─────────────────────────────────────────┐
|
|
562
|
+
│ RoxyProxy │
|
|
563
|
+
│ │
|
|
564
|
+
HTTP/S traffic │ ┌──────────────┐ ┌──────────────┐ │
|
|
565
|
+
─────────────────► │ │ Proxy Server │───►│ EventManager │ │
|
|
566
|
+
│ │ :8080 │ │ (pub/sub) │──┼──► SSE to Web UI
|
|
567
|
+
│ └──────┬───────┘ └──────────────┘ │
|
|
568
|
+
│ │ │
|
|
569
|
+
│ ▼ │
|
|
570
|
+
│ ┌──────────────┐ ┌──────────────┐ │
|
|
571
|
+
│ │ SQLite DB │◄───│ Cleanup │ │
|
|
572
|
+
│ │ (batched) │ │ (5 min) │ │
|
|
573
|
+
│ └──────┬───────┘ └──────────────┘ │
|
|
574
|
+
│ │ │
|
|
575
|
+
│ ▼ │
|
|
576
|
+
│ ┌──────────────┐ ┌──────────────┐ │
|
|
577
|
+
│ │ REST API │ │ Web UI │ │
|
|
578
|
+
│ │ /api/* │ │ (React) │ │
|
|
579
|
+
│ │ :8081 │ │ :8081 │ │
|
|
580
|
+
│ └──────────────┘ └──────────────┘ │
|
|
581
|
+
└─────────────────────────────────────────┘
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Key design decisions
|
|
585
|
+
|
|
586
|
+
- **SQLite with WAL mode** -- high write throughput with concurrent reads
|
|
587
|
+
- **Batched writes** -- requests are queued in memory and flushed every 100ms to reduce I/O
|
|
588
|
+
- **Event batching** -- SSE events are buffered for 100ms before flushing to connected clients
|
|
589
|
+
- **Response decompression** -- gzip, deflate, and brotli responses are automatically decompressed before storage
|
|
590
|
+
- **Body truncation** -- configurable max body size prevents storage bloat; a `truncated` flag is set on affected records
|
|
591
|
+
- **Per-domain cert caching** -- LRU cache avoids regenerating SSL certificates for frequently accessed domains
|
|
592
|
+
|
|
593
|
+
### Data storage
|
|
594
|
+
|
|
595
|
+
All data is stored in `~/.roxyproxy/data.db` (SQLite). The `requests` table has indexes on `timestamp`, `host`, `status`, `path`, and `content_type` for fast querying.
|
|
596
|
+
|
|
597
|
+
Request and response bodies are stored as binary blobs. In the API and SSE stream, they are base64-encoded.
|
|
598
|
+
|
|
599
|
+
### Files
|
|
600
|
+
|
|
601
|
+
| Path | Purpose |
|
|
602
|
+
|---|---|
|
|
603
|
+
| `~/.roxyproxy/data.db` | SQLite database |
|
|
604
|
+
| `~/.roxyproxy/config.json` | Configuration file (optional) |
|
|
605
|
+
| `~/.roxyproxy/ca/ca.crt` | Root CA certificate |
|
|
606
|
+
| `~/.roxyproxy/ca/ca.key` | Root CA private key |
|
|
607
|
+
| `~/.roxyproxy/pid` | Process ID file |
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Development
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
# Run all tests
|
|
615
|
+
npm test
|
|
616
|
+
|
|
617
|
+
# Watch mode
|
|
618
|
+
npm run test:watch
|
|
619
|
+
|
|
620
|
+
# Build server (TypeScript)
|
|
621
|
+
npm run build:server
|
|
622
|
+
|
|
623
|
+
# Build web UI (Vite + React)
|
|
624
|
+
npm run build:ui
|
|
625
|
+
|
|
626
|
+
# Build everything
|
|
627
|
+
npm run build
|
|
628
|
+
|
|
629
|
+
# Vite dev server with API proxy to :8081
|
|
630
|
+
npm run dev:ui
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Project structure
|
|
634
|
+
|
|
635
|
+
```
|
|
636
|
+
src/
|
|
637
|
+
├── cli/ # CLI entry point, commands, interactive mode
|
|
638
|
+
│ ├── index.ts # Command registration (Commander.js)
|
|
639
|
+
│ ├── interactive.tsx # Interactive terminal menu (Ink/React)
|
|
640
|
+
│ ├── commands/ # Individual CLI commands
|
|
641
|
+
│ └── system-proxy.ts # macOS system proxy & CA management
|
|
642
|
+
├── server/ # Proxy server, API, SSL, events
|
|
643
|
+
│ ├── index.ts # RoxyProxyServer orchestrator
|
|
644
|
+
│ ├── proxy.ts # HTTP/HTTPS intercepting proxy
|
|
645
|
+
│ ├── api.ts # Express REST API + SSE
|
|
646
|
+
│ ├── ssl.ts # CA generation & per-domain cert caching
|
|
647
|
+
│ ├── events.ts # Pub/sub event manager
|
|
648
|
+
│ └── config.ts # Config loading and merging
|
|
649
|
+
├── storage/ # Database and cleanup
|
|
650
|
+
│ ├── db.ts # SQLite operations (better-sqlite3)
|
|
651
|
+
│ └── cleanup.ts # Auto-cleanup job
|
|
652
|
+
├── shared/ # Shared TypeScript types
|
|
653
|
+
│ └── types.ts # Config, RequestRecord, RequestFilter
|
|
654
|
+
└── ui/ # React web UI (Vite)
|
|
655
|
+
├── App.tsx # Main app component
|
|
656
|
+
├── api.ts # API client + SSE hook
|
|
657
|
+
└── components/ # UI components
|
|
658
|
+
```
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function printBanner(): void;
|
|
2
|
+
export declare function printStartInfo(proxyPort: number, uiPort: number): void;
|
|
3
|
+
export declare function printSuccess(msg: string): void;
|
|
4
|
+
export declare function printError(msg: string): void;
|
|
5
|
+
export declare function printInfo(msg: string): void;
|
|
6
|
+
export declare function printWarn(msg: string): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
export function printBanner() {
|
|
3
|
+
console.log('');
|
|
4
|
+
console.log(pc.cyan(' ___ ___ '));
|
|
5
|
+
console.log(pc.cyan(' | _ \\___ __ ___ _ | _ \\_ _ _____ ___ _ '));
|
|
6
|
+
console.log(pc.cyan(' | / _ \\\\ \\ / || || ___/ \'_/ _ \\ \\ /| || |'));
|
|
7
|
+
console.log(pc.cyan(' |_|_\\___//_\\_\\\\_, ||_| |_| \\___/_\\_\\ \\_, |'));
|
|
8
|
+
console.log(pc.cyan(' |__/ |__/ '));
|
|
9
|
+
console.log('');
|
|
10
|
+
}
|
|
11
|
+
export function printStartInfo(proxyPort, uiPort) {
|
|
12
|
+
console.log(` ${pc.green('●')} Proxy ${pc.cyan(`http://127.0.0.1:${proxyPort}`)}`);
|
|
13
|
+
console.log(` ${pc.green('●')} Web UI ${pc.cyan(`http://127.0.0.1:${uiPort}`)}`);
|
|
14
|
+
console.log('');
|
|
15
|
+
console.log(pc.dim(' Ctrl+C to stop'));
|
|
16
|
+
console.log('');
|
|
17
|
+
}
|
|
18
|
+
export function printSuccess(msg) {
|
|
19
|
+
console.log(` ${pc.green('✔')} ${msg}`);
|
|
20
|
+
}
|
|
21
|
+
export function printError(msg) {
|
|
22
|
+
console.log(` ${pc.red('✖')} ${msg}`);
|
|
23
|
+
}
|
|
24
|
+
export function printInfo(msg) {
|
|
25
|
+
console.log(` ${pc.cyan('›')} ${msg}`);
|
|
26
|
+
}
|
|
27
|
+
export function printWarn(msg) {
|
|
28
|
+
console.log(` ${pc.yellow('!')} ${msg}`);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=banner.js.map
|