netshape --docs
Complete Documentation
NetShape is a local throttling proxy for simulating degraded network conditions. It wraps any app or command, injecting latency, bandwidth limits, packet loss, and jitter so you can test how your software behaves on slow, flaky, or unreliable connections.
How It Works
NetShape starts a local HTTP/HTTPS forward proxy on your machine and launches your app as a child process. The child process inherits three environment variables:
HTTP_PROXY=http://127.0.0.1:8090
HTTPS_PROXY=http://127.0.0.1:8090
ALL_PROXY=http://127.0.0.1:8090Any standard HTTP client that respects these variables will automatically send all traffic through the proxy. NetShape then applies the configured throttle settings — delaying packets, capping throughput, randomly dropping data — before forwarding to the real destination.
Python / LiteLLM users:
ALL_PROXY is intentionally set to an http:// URL (not socks5://). This means Python libraries like httpx, LiteLLM, and openai work without installing any extra packages (socksio is not required).
Your App ──→ NetShape Proxy (127.0.0.1:8090) ──→ Internet
↑ bandwidth cap
↑ latency injection
↑ packet loss
↑ jitterA second port (8091 by default) hosts the control API and the web dashboard.
Installation
Requirements
- • Python 3.10 or later
- • pip
Global install (recommended for CLI use)
pip install netshapeInside a virtual environment
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install netshapeIf installed in a virtual environment, the netshape command is only available while that environment is activated. Install globally if you want it available in any terminal.
Verify the installation
netshape --versionFirst-Time Setup
After installing, run the interactive setup wizard once to choose your preferences:
netshape setupThe wizard presents two selection menus:
Feature selection
┌─────────────────────────────────────────────────────────────┐
│ 1 Core CLI only Terminal commands, no browser UI │
│ 2 CLI + Web Dashboard Visual controls, live graphs, ... │
└─────────────────────────────────────────────────────────────┘- • Core CLI only — installs the lightest footprint. All terminal commands work fully; the dashboard URL is simply not served.
- • CLI + Web Dashboard — enables the browser UI at
http://127.0.0.1:8091/dashboardwhenever a session is active.
You also pick a default throttle profile. Your choices are saved to ~/.netshape/config.json. Re-run netshape setup at any time to change them.
Quick Start
# 1. (First time only) run the setup wizard
netshape setup
# 2. Run your app through a 3G profile
netshape run --profile 3g -- python app.py
# 3. In a separate terminal, check what's happening
netshape status --watch
# 4. Change conditions live (no restart needed)
netshape adjust --profile satellite
# 5. Stop the session
netshape stopBuilt-in Profiles
Profiles are named presets covering common real-world network conditions.
| Profile | Bandwidth | Latency | Loss | Jitter | Description |
|---|---|---|---|---|---|
| 2g | 50 kbps | 500 ms | 2% | 150 ms | Typical 2G mobile |
| 3g | 780 kbps | 200 ms | 1% | 60 ms | Typical 3G mobile |
| 4g | 4 Mbps | 80 ms | 0.3% | 25 ms | Typical 4G LTE |
| 5g | 50 Mbps | 30 ms | 0.1% | 10 ms | Fast mobile |
| edge | 240 kbps | 400 ms | 3% | 120 ms | Very slow mobile EDGE |
| wifi | 30 Mbps | 25 ms | 0.1% | 8 ms | Common home Wi-Fi |
| dsl | 6 Mbps | 60 ms | 0.2% | 20 ms | Older DSL broadband |
| cable | 100 Mbps | 20 ms | 0.05% | 5 ms | Residential cable |
| fiber | 1 Gbps | 5 ms | 0% | 2 ms | Fast wired fiber |
| satellite | 12 Mbps | 650 ms | 0.5% | 80 ms | High-latency satellite |
| congested | 1.5 Mbps | 180 ms | 2.5% | 140 ms | Busy shared network |
| offline | 0 | 0 ms | 100% | 0 ms | All traffic dropped |
List them at any time:
netshape profilesCLI Reference
netshape run
Starts a proxy session and launches your app as a child process. Everything after -- is the command to run.
| Option | Short | Description | Default |
|---|---|---|---|
| --profile | -p | Built-in profile name | — |
| --bandwidth | -b | Bandwidth limit (1mbps, 500kbps) | — |
| --latency | -l | Added latency (200ms, 1s) | — |
| --loss | Packet loss (2%, 0.5%) | — | |
| --jitter | -j | Latency variance (50ms) | — |
| --timeout | -t | Auto-stop after duration. Units: s (seconds), m (minutes), h (hours). E.g. 30m, 1h, 90s | — |
| --port | Proxy traffic port | 8090 | |
| --log-file | Write JSON logs to file (rotating, 10 MB) | — |
# Use a profile
netshape run --profile 3g -- python app.py
netshape run --profile 4g -- node server.js
netshape run --profile satellite -- npx electron .
# Custom settings
netshape run --bandwidth 1mbps --latency 200ms --loss 1% -- python app.py
# Profile with one override
netshape run --profile 3g --latency 500ms -- python app.py
# Auto-stop after 30 minutes
netshape run --profile 3g --timeout 30m -- python app.py
# Save logs to file
netshape run --profile 3g --log-file proxy.log -- python app.pynetshape adjust
Applies new throttle settings to the currently running session live, with no restart needed.
| Option | Short | Description |
|---|---|---|
| --profile | -p | Switch to a built-in profile |
| --bandwidth | -b | New bandwidth limit |
| --latency | -l | New latency |
| --loss | New packet loss | |
| --jitter | -j | New jitter |
# Switch profile
netshape adjust --profile satellite
# Adjust individual values
netshape adjust --bandwidth 500kbps
netshape adjust --latency 800ms
netshape adjust --loss 5%
netshape adjust --jitter 100ms
# Remove all throttling
netshape adjust --bandwidth 0 --latency 0 --loss 0 --jitter 0netshape status
Shows the current session status.
| Option | Description |
|---|---|
| --watch, -w | Live-updating table, refreshes every second |
| --json | Output raw JSON |
netshape stop
Stops the active session and terminates the child process.
netshape stopnetshape test
Verifies the proxy is correctly intercepting traffic by running a local speed test.
netshape test # quick verification
netshape test --profile 3g # test under 3G conditionsnetshape profiles
Lists all built-in profiles with their parameters.
netshape profilesnetshape metrics
Shows detailed proxy metrics.
| Option | Description |
|---|---|
| --prometheus, -p | Output in Prometheus text exposition format |
netshape rule
Manages per-endpoint throttle rules. Rules let you apply different throttle settings to specific domains or URL patterns.
# Add a rule
netshape rule add stripe\.com --bandwidth 1mbps --latency 200ms --comment "payment API"
# List rules
netshape rule list
netshape rule list --json
# Enable/disable
netshape rule enable "payment API"
netshape rule disable "payment API"
# Remove
netshape rule remove "payment API"netshape scenario
Runs a sequence of network condition phases automatically over time.
| Option | Description |
|---|---|
| --no-wait | Submit the scenario and return immediately (useful in CI scripts) |
# List scenarios
netshape scenario list
# Run a built-in scenario
netshape scenario run --builtin subway
# Start scenario without blocking the terminal
netshape scenario run --builtin subway --no-wait
# Run from a YAML file
netshape scenario run ./my-scenario.yaml
# Check status / stop
netshape scenario status
netshape scenario stopnetshape setup
Interactive first-time setup wizard. Configures which features are enabled and your default throttle profile.
netshape setupConfig file at ~/.netshape/config.json:
{
"dashboard": true,
"default_profile": "3g"
}Web Dashboard
The dashboard is an optional live web UI built into the proxy. It gives full visual control over all settings.
Enabling the Dashboard
netshape setup
# → choose option 2: "CLI + Web Dashboard"Open it at http://127.0.0.1:8091/dashboard
Dashboard Sections
- • Live Metrics — Real-time download/upload speed graphs, active throttle readings, connection status
- • Controls — Sliders for bandwidth, latency, loss, jitter; profile dropdown; apply button
- • Per-Endpoint Rules — Add, toggle, and remove domain-specific rules
- • Scenarios — Run built-in or custom scenarios; build custom ones
- • Logs — Live proxy activity log
Throttle Parameters
Bandwidth
Caps the maximum throughput in each direction using a token bucket algorithm. Short bursts above the limit are absorbed, sustained transfer is capped.
Latency
Adds a fixed delay to each packet in both directions. Key insight: Latency multiplies across round trips. At 300ms, a TLS handshake (3 RTTs) costs ~900ms before a byte of data transfers.
Loss
Randomly drops a percentage of packets. TCP will retransmit dropped packets, causing stalls. Even 5% loss with 200ms latency causes TCP retransmit storms and drastic throughput collapse.
Jitter
Varies the latency randomly ± the jitter value on each packet. Causes unpredictable response times, streaming stutter, and WebSocket lag spikes.
Per-Endpoint Rules
Per-endpoint rules let you apply different throttle settings to specific hosts or URL patterns, overriding the global session settings for matched traffic.
How Matching Works
The pattern field is a regular expression matched against the target host (for CONNECT tunnels) or the full URL (for direct HTTP). Matching is case-insensitive.
# Match exact domain
netshape rule add "stripe\.com"
# Match any subdomain
netshape rule add "\.supabase\.co"
# Match multiple services
netshape rule add "openai\.com|anthropic\.com"Persistence
Rules are saved to ~/.netshape/rules.json automatically. When you start a new session, all saved rules are restored — in the disabled state by default.
Scenarios
Scenarios automate a sequence of network conditions over time.
Writing a Custom Scenario File
name: "Flakey Mobile Connection"
description: "Starts decent, degrades, goes offline, recovers."
phases:
- name: "Good Start"
duration: "30s"
profile: "4g"
- name: "Degrading"
duration: "20s"
bandwidth: "500kbps"
latency: "300ms"
loss: "3%"
jitter: "80ms"
- name: "Offline"
duration: "10s"
bandwidth: 0
loss: "100%"
- name: "Slow Recovery"
duration: "20s"
profile: "edge"
- name: "Recovered"
duration: "30s"
profile: "3g"Save the file to ~/.netshape/scenarios/ to make it available in the CLI and dashboard.
Built-in Scenarios
| Name | Description |
|---|---|
| subway | Platform (4G) → tunnel (dead zone) → re-emergence (2G) → platform (4G) |
| coffee-shop-wifi | Quiet café (fast) → lunch rush (congested) → peak (packet-loss hell) → afternoon (recovering) |
| satellite | High-bandwidth but very high latency, simulating a satellite link |
| flight-mode | Normal → airplane mode (offline) → reconnect |
Compatibility Guide
What Gets Throttled
NetShape throttles HTTP and HTTPS traffic routed through the proxy. This covers the vast majority of modern app traffic.
Python Apps
All standard HTTP libraries respect HTTP_PROXY automatically — no code changes needed.
| Library | Throttled? |
|---|---|
| requests | Yes |
| httpx | Yes |
| aiohttp | Yes |
| urllib / urllib2 | Yes |
| openai SDK | Yes |
| anthropic SDK | Yes |
| supabase-py | Yes |
| boto3 (AWS) | Yes |
Node.js Apps
| Library / Runtime | Throttled? | Notes |
|---|---|---|
| axios | Yes | |
| node-fetch / fetch (Node 18+) | Yes | |
| got | Yes | |
| undici | Yes | |
| OpenAI Node SDK | Yes | |
| http.get() / https.get() (native) | No | Requires http-proxy-agent (HTTP) or https-proxy-agent (HTTPS) npm packages |
| supabase-js (Node server-side) | Yes |
Electron Apps
Electron has two traffic paths. The renderer process uses Chromium's network stack and ignores HTTP_PROXY — you must configure it via session.setProxy().
import { app, BrowserWindow, session } from 'electron';
async function applyProxySettings() {
const proxyUrl = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
if (proxyUrl) {
const { host } = new URL(proxyUrl);
await session.defaultSession.setProxy({ proxyRules: host });
}
}
app.whenReady().then(async () => {
await applyProxySettings();
createWindow();
});Common mistake — wrong order
// ❌ Wrong — window opens before proxy is configured
app.whenReady().then(() => {
createWindow(); // renderer starts without proxy
applyProxySettings(); // too late, renderer ignores this
});
// ✅ Correct — proxy configured before window opens
app.whenReady().then(async () => {
await applyProxySettings(); // proxy first
createWindow();
});Do not use npm run dev (Vite dev server). Build first and launch Electron directly:
npm run build
netshape run --profile 3g -- npx electron .Verify the proxy is intercepting renderer traffic
After launching with netshape run, open the NetShape dashboard or run netshape status --watch in another terminal. Then trigger a network request from the renderer. You should see Requests handled increment immediately.
// Quick smoke test — paste in Electron DevTools (Ctrl+Shift+I)
fetch('https://httpbin.org/get')
.then(r => r.json())
.then(data => {
console.log('✓ Request succeeded — check NetShape dashboard for traffic');
console.log(data);
})
.catch(err => console.error('✗ Request failed:', err));- • Dashboard at
http://127.0.0.1:8091/dashboard→ "Requests handled" should increment - •
netshape status --watch→ "Connections active" should be > 0 during a request - • If "Requests handled" stays at 0 →
applyProxySettings()is not running beforecreateWindow() - • If requests fail entirely → check that
session.defaultSession.setProxy()resolved before the request was made
Multi-Service Apps (Electron + Python backend)
Launch each process through its own netshape run in separate terminals.
# Terminal 1 — Electron frontend
netshape run --profile 3g -- npx electron .
# Terminal 2 — Python backend
netshape run --profile 3g -- python -m uvicorn app.main:appWhat Doesn't Work
| Traffic type | Example | Why |
|---|---|---|
| Raw TCP sockets | Self-hosted Redis, MongoDB driver | Not HTTP |
| UDP | DNS, VoIP, game servers, QUIC | Not TCP/HTTP |
| WebRTC | Video calls, peer-to-peer | UDP/DTLS |
| gRPC (some configs) | gRPC over HTTP/2 works | Depends on transport |
| Browser-side traffic | Chrome/Firefox without system proxy | Browser ignores env vars |
Data & Logs
Log Files
netshape run --profile 3g --log-file proxy.log -- python app.pyEach line is a JSON object. Log files rotate at 10 MB, keeping 3 backup files.
Persistence
Rules
Automatically saved to ~/.netshape/rules.json. Restored on new sessions — always in disabled state.
User Scenarios
Scenario files saved to ~/.netshape/scenarios/ are automatically discovered.
FAQ
Do I need to change my app's code?
For most apps — no. Python, Node.js, Go, Ruby, and PHP HTTP clients all respect HTTP_PROXY env vars automatically. Electron apps require a one-time code change.
Will NetShape throttle LLM streaming responses?
Yes. Streaming token responses are especially interesting to throttle — tokens trickle in slowly under low bandwidth, and high latency delays the first token. No code changes needed for any Python or Node.js OpenAI/Anthropic SDK.
Can I run multiple sessions at the same time?
Yes — one per service, each in its own terminal. Each session auto-selects its own proxy port (8090, 8092, 8094, ...) and control port (8091, 8093, 8095, ...).
Can I use NetShape in CI?
Yes. Run your test command through NetShape — netshape run --profile 3g -- pytest. The session exits automatically when the test finishes.
NetShape v1.0.1 — MIT License