proxy
Manage reverse proxies with traffic logging and frontend instrumentation. The proxy sits between the browser and your dev server so the agent can capture frontend errors, inspect the live DOM, run JavaScript in the page, and inject chaos — without touching application code.
Synopsis
proxy {action: "<action>", ...params}
Actions
| Action | Description |
|---|---|
start | Create and start a reverse proxy |
stop | Stop a running proxy |
restart | Restart a running proxy (preserves config) |
status | Get proxy status and statistics |
list | List all running proxies (accepts global: true for all directories) |
exec | Execute JavaScript in connected browsers |
chaos | Configure chaos engineering (network failures, latency) |
toast | Display toast notifications in the browser |
start
Create and start a reverse proxy.
proxy {
action: "start",
id: "app",
target_url: "http://localhost:3000"
}
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | - | Unique proxy identifier |
target_url | string | Yes | - | Backend server URL |
port | integer | No | hash-based | Listen port. Only specify if you need a specific port. |
max_log_size | integer | No | 1000 | Maximum log entries |
bind_address | string | No | 127.0.0.1 | Bind address: 127.0.0.1 (localhost only) or 0.0.0.0 (all interfaces for tunnel/mobile testing) |
allow_external | boolean | No | false | Required to bind to a non-localhost address (0.0.0.0 or ::). Acknowledges network exposure risk. |
skip_tls_verify | boolean | No | false | Skip TLS certificate verification (for self-signed/expired certs in dev). |
public_url | string | No | - | Public URL for tunnel services (e.g., https://abc123.trycloudflare.com). Used for URL rewriting. |
tunnel | string | No | - | Tunnel provider to launch alongside the proxy: ngrok, cloudflared, tailscale, or custom. |
tunnel_args | string[] | No | - | Additional arguments for the tunnel command. |
tunnel_token | string | No | - | Authentication token for the tunnel (e.g., ngrok authtoken). |
tunnel_region | string | No | - | Tunnel region. |
tunnel_command | string | No | - | Custom tunnel command (when tunnel: "custom"). Use {{PORT}} as placeholder. |
Response:
{
"id": "app",
"status": "running",
"target_url": "http://localhost:3000",
"listen_addr": "127.0.0.1:45849",
"message": "Proxy started"
}
Port Selection
By default, the proxy assigns a stable port based on a hash of the target URL. This ensures:
- The same target URL always gets the same port (consistent across restarts)
- Different URLs get different ports (avoids conflicts)
- Ports are in the range 10000-60000 (avoids well-known and ephemeral ports)
Recommended: Let the proxy choose the port automatically. Only specify port if you need a specific port number.
Request a specific port:
proxy {action: "start", id: "app", target_url: "http://localhost:3000", port: 9000}
→ {listen_addr: ":9000"}
If the requested port is busy, the proxy finds an available one:
proxy {action: "start", id: "app", target_url: "http://localhost:3000", port: 9000}
→ {
"listen_addr": ":45123",
"message": "Port 9000 was busy, using 45123"
}
stop
Stop a running proxy.
proxy {action: "stop", id: "app"}
Response:
{
"id": "app",
"message": "Proxy stopped"
}
restart
Restart a running proxy, preserving its configuration (target URL, port, bind address, tunnel).
proxy {action: "restart", id: "app"}
Response:
{
"id": "app",
"success": true,
"message": "Proxy restarted"
}
status
Get proxy status and statistics.
proxy {action: "status", id: "app"}
Response:
{
"id": "app",
"status": "running",
"target_url": "http://localhost:3000",
"listen_addr": ":8080",
"uptime": "15m32s",
"total_requests": 1542,
"log_stats": {
"http_entries": 1000,
"error_entries": 3,
"performance_entries": 45,
"dropped": 542
},
"restart_count": 0,
"last_error": null
}
list
List all running proxies.
proxy {action: "list"}
Response:
{
"proxies": [
{
"id": "frontend",
"status": "running",
"target_url": "http://localhost:3000",
"listen_addr": ":8080"
},
{
"id": "api",
"status": "running",
"target_url": "http://localhost:4000",
"listen_addr": ":8081"
}
],
"active_count": 2
}
exec
Execute JavaScript in connected browser clients.
proxy {action: "exec", id: "app", code: "document.title"}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Proxy ID |
code | string | Yes | JavaScript code to execute |
Response:
{
"result": "My App - Dashboard",
"clients": 1
}
Examples
// Get page title
proxy {action: "exec", id: "app", code: "document.title"}
// Get current URL
proxy {action: "exec", id: "app", code: "window.location.href"}
// Access __devtool API
proxy {action: "exec", id: "app", code: "window.__devtool.inspect('#header')"}
// Take screenshot
proxy {action: "exec", id: "app", code: "window.__devtool.screenshot('debug')"}
// Highlight element
proxy {action: "exec", id: "app", code: "window.__devtool.highlight('.button')"}
Timeout
Execution has a 30-second timeout. For async operations:
// Interactive element selection (waits for user click)
proxy {action: "exec", id: "app", code: "window.__devtool.selectElement()"}
// Ask user a question
proxy {action: "exec", id: "app", code: "window.__devtool.ask('OK?', ['Yes', 'No'])"}
chaos
Configure chaos engineering to simulate network failures, latency, and API errors.
proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "flaky-api"}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Proxy ID |
chaos_operation | string | Yes | One of enable, disable, status, set, preset, add_rule, remove_rule, list_rules, stats, clear |
chaos_preset | string | For preset | Built-in preset name (see below) |
chaos_rules | array | - | Legacy field; for set, pass rules inside chaos_config.rules |
chaos_rule | object | For add_rule | A single rule to add |
chaos_rule_id | string | For remove_rule | ID of the rule to remove |
chaos_config | object | For set | Full chaos configuration (enabled, rules, global_odds, seed, logging_mode) |
Operations
chaos_operation | Effect |
|---|---|
enable / disable | Turn chaos on or off for the proxy |
preset | Apply a built-in preset (chaos_preset) |
set | Replace the full rule set — pass rules inside chaos_config |
add_rule | Add a single rule (chaos_rule) |
remove_rule | Remove a rule by id (chaos_rule_id) |
list_rules | List the active rules |
status | Get current chaos state |
stats | Get injection statistics |
clear | Remove all rules |
Built-in Presets
| Preset | Description |
|---|---|
mobile-3g | 200-2000ms latency, 2% packet loss |
mobile-4g | 50-500ms latency, 0.5% packet loss |
flaky-api | Random 500s, timeouts, variable latency |
race-condition | Out-of-order responses, high variance delays |
stale-tab | 3-hour delays (test token expiry) |
slow-connection | 5KB/s bandwidth throttling |
connection-drops | 10% mid-response disconnects |
rate-limited | 20% 429 errors |
Examples
// Apply preset
proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "mobile-3g"}
// Custom rules (set replaces the full rule set; rules go inside chaos_config)
proxy {
action: "chaos",
id: "app",
chaos_operation: "set",
chaos_config: {
"enabled": true,
"rules": [
{
"id": "api-latency",
"type": "latency",
"enabled": true,
"url_pattern": "/api/.*",
"min_latency_ms": 500,
"max_latency_ms": 2000,
"probability": 0.3
}
]
}
}
// Check status
proxy {action: "chaos", id: "app", chaos_operation: "status"}
→ {enabled: true, preset: "flaky-api", stats: {affected_count: 38, errors_injected: 7}}
// Disable chaos
proxy {action: "chaos", id: "app", chaos_operation: "disable"}
// Clear all rules
proxy {action: "chaos", id: "app", chaos_operation: "clear"}
See Chaos Engineering for complete documentation.
toast
Display toast notifications in connected browsers.
proxy {action: "toast", id: "app", toast_message: "Build complete!"}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Proxy ID |
toast_message | string | Yes | Notification message |
toast_type | string | No | success, error, warning, info (default: info) |
toast_title | string | No | Optional title |
toast_duration | integer | No | Duration in milliseconds (default: 5000) |
Examples
// Simple notification
proxy {action: "toast", id: "app", toast_message: "Saved!"}
// Success with title
proxy {action: "toast", id: "app", toast_message: "All tests passed", toast_type: "success", toast_title: "Tests"}
// Error that stays longer
proxy {action: "toast", id: "app", toast_message: "Build failed", toast_type: "error", toast_duration: 10000}
// Warning
proxy {action: "toast", id: "app", toast_message: "Slow response detected", toast_type: "warning"}
Features
What the Proxy Does
- Forwards HTTP Traffic - Transparent to the application
- Logs Requests/Responses - Captured in circular buffer
- Injects JavaScript - Adds
window.__devtoolto HTML pages - Captures Errors - Frontend JavaScript errors with stack traces
- Collects Metrics - Page load timing, paint metrics
- Tracks Pages - Groups requests by page session
WebSocket Support
WebSocket connections (e.g., HMR) are proxied transparently:
Browser ←→ Proxy (:8080) ←→ Dev Server (:3000)
│
└── /__devtool_metrics (reserved for metrics WebSocket)
Auto-Restart
Proxies auto-restart on crash (max 5 restarts per minute):
proxy {action: "status", id: "app"}
→ {
"restart_count": 2,
"last_error": "bind: address already in use"
}
Error Responses
Proxy Not Found
{
"error": "proxy not found",
"id": "nonexistent"
}
ID Already Exists
{
"error": "proxy already exists",
"id": "app"
}
Invalid Target URL
{
"error": "invalid target URL",
"target_url": "not-a-url"
}
No Connected Clients
{
"error": "no connected clients",
"id": "app"
}
Real-World Patterns
Development Setup
// Start dev server
run {script_name: "dev"}
// Wait for it to be ready
proc {action: "output", process_id: "dev", grep: "ready", tail: 5}
// Start proxy (port auto-assigned based on target URL)
proxy {action: "start", id: "app", target_url: "http://localhost:3000"}
// Check the listen_addr in the response, then browse to that address
Multiple Environments
// Local dev (each gets a unique port based on URL hash)
proxy {action: "start", id: "local", target_url: "http://localhost:3000"}
// Staging
proxy {action: "start", id: "staging", target_url: "https://staging.example.com"}
// Compare traffic
proxylog {proxy_id: "local", types: ["http"], url_pattern: "/api"}
proxylog {proxy_id: "staging", types: ["http"], url_pattern: "/api"}
Debugging Session
// Start proxy
proxy {action: "start", id: "debug", target_url: "http://localhost:3000"}
// User navigates to problem page...
// Check for errors
proxylog {proxy_id: "debug", types: ["error"]}
// Inspect problem element
proxy {action: "exec", id: "debug", code: "window.__devtool.inspect('.broken-component')"}
// Take screenshot
proxy {action: "exec", id: "debug", code: "window.__devtool.screenshot('bug')"}
Mobile Testing with Tunnels
For testing on real mobile devices, you need to expose your proxy publicly. agnt supports this via the bind_address and public_url options, combined with the tunnel tool.
Quick Setup
// 1. Start proxy on all interfaces
proxy {
action: "start",
id: "app",
target_url: "http://localhost:3000",
bind_address: "0.0.0.0"
}
// 2. Start a Cloudflare tunnel pointing to the proxy
tunnel {
action: "start",
id: "app",
provider: "cloudflare",
local_port: 45849,
proxy_id: "app"
}
The tunnel automatically configures the proxy's public_url, enabling proper URL rewriting for HTTPS.
Manual Configuration
If you're using an external tunnel service:
proxy {
action: "start",
id: "app",
target_url: "http://localhost:3000",
bind_address: "0.0.0.0",
public_url: "https://your-tunnel-url.trycloudflare.com"
}
Security Note
By default, proxies bind to 127.0.0.1 (localhost only) for security. Only use 0.0.0.0 when you need external access (tunnels, mobile testing).
See Also
- Chaos Engineering - Complete chaos testing documentation
- tunnel - Manage Cloudflare/ngrok tunnels
- proxylog - Query proxy traffic logs
- currentpage - View page sessions
- Mobile Testing Guide - Complete mobile testing workflow
- Reverse Proxy Feature
- Frontend Diagnostics