vs mitmproxy
If you have used mitmproxy Python addons, this page compares RelayCore scripting hooks, data models, and tooling. See Scripting API for reference and Scripting Guide for patterns.
Capability comparison
| Capability | RelayCore | mitmproxy | Notes |
|---|---|---|---|
| HTTP request/response hooks | ✅ 4 hooks | ✅ 4 hooks | Feature parity |
| WebSocket message frames | ✅ | ✅ | Feature parity |
| WebSocket lifecycle | ✅ start/end/error | ✅ | Feature parity |
| Connection hooks | ✅ onConnect/onDisconnect | ✅ clientconnect/clientdisconnect | RelayCore supports connection drop |
| Error callback | ✅ onError | ✅ error | RelayCore includes stage info |
| Cross-request state | ✅ sharedState | ✅ Python globals | Different model: per-isolate vs process-global |
| Environment variables | ✅ relay.env + whitelist | ✅ os.environ | RelayCore requires --script-env-allow |
| Logging | ✅ console.* → tracing | ✅ logging | Feature parity |
| Execution metrics | ✅ Prometheus | ✅ addon trace | RelayCore via /api/v1/metrics |
| Built-in utilities | ✅ relay.* | ✅ Python stdlib | Language ecosystem difference |
| Sub-requests | ✅ relay.fetch (optional) | ✅ requests/httpx | mitmproxy HTTP client is more flexible |
| Rule match context | ✅ matched_rules / rule_variables | — | Scripts can read rule results |
| npm / pip packages | ✅ esbuild bundle | ✅ pip | Different packaging model |
| DNS/TCP/UDP layer hooks | — | ✅ | mitmproxy covers lower layers |
| TLS clienthello hook | — | ✅ | mitmproxy is more direct for SNI-style use |
| Streaming body chunk edits | — | ✅ | RelayCore currently reads/writes full bodies |
Migration examples
Modify request headers
# mitmproxy
def request(flow):
flow.request.headers["X-Custom"] = "value"
# RelayCore
globalThis.onRequestHeaders = (_ctx, flow) => {
flow.layer.data.request.headers.push(["X-Custom", "value"]);
return flow;
}; Mock responses
mitmproxy can synthesize responses in the response hook. In RelayCore 0.5.x, prefer rule MockResponse for static mocks; use scripts for dynamic logic. See the Rule Guide.
# mitmproxy
def response(flow):
if flow.response.status_code == 404:
flow.response = http.Response.make(200, b"OK", {"Content-Type": "text/plain"})
# RelayCore — rule example (JSON)
{
"filter": { "type": "Url", "config": { "mode": "Contains", "value": "/missing" } },
"actions": [{ "type": "MockResponse", "config": { "status": 200, "body": "OK" } }]
} Key differences
- Language: TypeScript (Deno/V8) vs Python
- Headers: RelayCore uses
[key, value][]arrays (duplicate headers preserved); mitmproxy uses dicts - Body: RelayCore
body.contentis base64 — decode withatob(), encode withbtoa() - Flow shape: HTTP data lives under
flow.layer.data.request/response(see Scripting API) - Global state: RelayCore
sharedState.get/setvs mitmproxy module-level Python variables - Loading:
relay-core-cli run --script, HTTPPOST /api/v1/script, Tauriload_script
When to use which
| Scenario | Better fit |
|---|---|
| Daily HTTP/HTTPS capture and header/body edits | Either |
| Python ecosystem and pip dependencies | mitmproxy |
| TypeScript / npm with a Rust proxy stack | RelayCore |
| Rule + script coordination (match context in scripts) | RelayCore |
| Transparent proxy TCP/UDP/DNS scripting | mitmproxy |
| Same runtime across CLI, HTTP API, MCP, desktop embed | RelayCore |