wiretrail is a fast, deterministic, agent-friendly HAR (HTTP Archive) analyzer
for the command line. It answers narrow, repeatable questions about a network
capture in a single command — storms, duplicates, retries, errors, auth flows,
slow calls, what varies between repeated requests, ranked root-cause diagnosis,
body search/extraction, and regression diff against a baseline — with structured
terminal output and a stable --json schema, and no GUI.
It is HeapTrail for network captures. It reuses heaptrail's design philosophy: agentic/LLM-driven investigation, deterministic output that diffs cleanly, JSON for machine consumers, and fixed single-command answers instead of an interactive load-and-explore session.
Forked from mandrean/har-rs, which
contributes the HAR 1.2/1.3 struct definitions; this fork replaces the parse path
with an mmap single-pass loader and adds the analysis CLI documented below.
Each log.entries[] item in a HAR already exposes the request, response, content,
timings, cache metadata, headers, cookies, URL, method, status, and bodies — more
than enough to reconstruct the kind of manual "what happened during this capture?"
analysis a debugger does by hand. wiretrail turns that into single-command
answers.
Sanitization is treated as core, not optional: HARs routinely contain cookies,
auth headers, tokens, request/response bodies, and user data. wiretrail redacts
by default across every output — including secret-bearing blobs hidden in URL
path segments — and only reveals raw values with an explicit
--unsafe-include-secrets flag.
- Agentic / LLM-driven investigation. Structured terminal output (with
--json) lets an agent run a command, read the result, and decide the next probe. Every command prints a "next useful commands" footer. - Headless / CI. Single static binary, deterministic output, defined exit codes
(
0clean,1findings,2invalid HAR).compare --fail-on <severity>turns a regression diff against a baseline HAR into a strict pass/fail gate. - Large captures. mmap single-pass parse: a 143 MB capture (2237 entries) loads in ~0.5 s using ~2× the file size in RAM.
- Safe to share. Redact-by-default makes
report,curl, andshow-entryoutput safe to paste into a ticket;--unsafe-include-secretswhen you actually need to replay a call. - Narrow, repeatable questions. "What are the request storms?", "Which calls are wasteful duplicates vs retries?", "What's the auth/refresh story?", "What differs between these 27 identical-looking POSTs?" — one command each.
- Live, interactive inspection while reproducing a flow — Chrome DevTools, Charles, Proxyman, HTTP Toolkit stay in their column.
- Editing and re-sending requests interactively. wiretrail emits sanitized
curlfor replay but isn't an interactive client.
The tools complement each other: capture in a proxy/DevTools, then run wiretrail
over the exported .har for fast, scriptable, agent-friendly post-mortem analysis.
cargo install wiretrailOr build from git:
git clone https://github.com/johnneerdael/wiretrail
cd wiretrail
cargo build --release # ./target/release/wiretrailPre-built binaries for Linux/macOS/Windows are attached to each GitHub release.
wiretrail <FILE> [COMMAND] [OPTIONS]
<FILE> is a HAR (1.2 or 1.3) export. With no command, summary runs.
wiretrail capture.har # executive summary (default)
wiretrail capture.har auto # smart one-shot: summary + auto-drill the findings
wiretrail capture.har duplicates # repeated calls, grouped
wiretrail capture.har errors --json # 4xx/5xx grouped, as JSON
wiretrail capture.har show-entry e000123 # full redacted detail for one entry
wiretrail capture.har curl e000123 --unsafe-include-secrets # replayable cURLStart with auto for an unfamiliar capture: it prints the summary, ranks the
likely problems, and inlines the relevant deeper analysis (errors, retries, auth,
…) scoped to exactly where the trouble is — one command, no guessing what to run
next. summary itself now ends with a ranked recommended next steps section,
so even the default command tells you precisely which follow-ups matter.
| Option | Effect |
|---|---|
--json |
Emit the stable JSON envelope instead of terminal text. |
--top N |
Bound list sizes (default 10). |
--filter "<expr>" |
Restrict to matching entries (repeatable). |
--config <path> |
Path to wiretrail.yaml (default: ./wiretrail.yaml if present). |
--unsafe-include-secrets |
Show raw auth headers, tokens, bodies, and URL secrets instead of redacting. |
The filter language: host:api.foo.com status:>=400 method:POST path:*login* time:>1000ms has:req.header.authorization.
Overview & inventory
| Command | Answers |
|---|---|
summary (default) |
Capture meta, time range, status/resource breakdown, top hosts, top duplicates, slowest, biggest payloads, root-cause hints. |
hosts |
Per-host: count, methods, status distribution, p50/p95/max latency, bytes, time window, duplicate count. |
subsystems |
Group hosts into named integrations (built-in vendor heuristics + wiretrail.yaml ownership map). |
endpoints |
Normalized endpoint inventory (host, method, {id} path, statuses, content types, sample query keys). |
timeline |
Chronological per-request view with DUP/RETRY markers. |
Wasteful traffic
| Command | Answers |
|---|---|
duplicates |
Repeated method + normalized-path + query fingerprint, grouped; marks retries. |
retries |
Repeats that follow a failed attempt (5xx/429/network), with backoff gaps. |
storms |
Bursts of many calls to one host/endpoint within a window (--window-ms, --min-count). |
pagination |
Pagination loops + N+1 fan-out (--max-pages, --fanout-min, --window-ms). |
rate-limit |
429 events, Retry-After, X-RateLimit-*, cooldown violations. |
Failures & timing
| Command | Answers |
|---|---|
errors |
4xx/5xx grouped by endpoint+status, with parsed message/code, correlation IDs, body snippet. |
redirects |
Redirect chains/storms, cross-host hops. |
transitions |
Status sequences: 401→200, 429→429, 5xx→2xx. |
slowest |
Top-N slow calls with timing-phase breakdown + bottleneck classifier. |
Auth
| Command | Answers |
|---|---|
jwt |
Find and decode JWTs (header/claims, exp/skew) — hashed sub, never the signature or raw token by default. |
auth |
401/403 patterns, inconsistent auth, token rotation, and token-refresh flows (old-token-reuse, concurrent, failed). |
handoff |
Backend trace-handoff blocks for failed + slowest requests (template, correlation IDs, server IP, sanitized cURL). |
Inspection, diff & export
| Command | Answers |
|---|---|
show-entry <id> |
Full normalized request/response/timings for one entry, redacted. |
diff |
What varies across repeated calls to one endpoint (query/header/body verdict: identical / volatile-only / meaningful). |
search <pattern> |
Grep request/response bodies (--regex, --ignore-case); redaction-safe context snippets. |
extract <jsonpath> |
Pull a JSON-path value from bodies ($.errors[0].code, [*] wildcard); --target req|resp; opaque values masked. |
export |
Flatten entries to NDJSON or CSV (--format ndjson|csv) — metadata only, no raw bodies. |
report |
A dossier-style markdown report composed from the analyses. |
curl [id] |
Sanitized, safety-labeled curl replay for one entry or all filtered entries. |
Diagnosis & quality
| Command | Answers |
|---|---|
auto |
Smart one-shot: prints the summary, then drills the top-ranked recommendations (HIGH+MED by default) inline, each scoped by its own filter. --all / --min-severity widen/narrow. |
diagnose |
Ranked root-cause findings synthesized from all analyses (severity-sorted, with evidence IDs + a suggested follow-up command). |
validate |
Capture quality + analysis sufficiency (timings/bodies/auth coverage, sanitized?, anomalies). |
startup |
Boot/startup profile: max concurrency, critical path, slow dependencies (--window-ms). |
cascade |
Earliest failure and the downstream failures it triggered (--window-ms, --min-downstream). |
Regression & rules
| Command | Answers |
|---|---|
compare <baseline.har> |
Diff this capture against a baseline: new/removed hosts & endpoints, new errors, latency regressions, payload growth — severity-scored. --fail-on <severity> gates CI. |
rules |
Evaluate wiretrail.yaml rules + built-in packs (--pack auth,caching,payments,security,rest,graphql). |
Run wiretrail <file> <command> --help for per-command options.
$ wiretrail capture.har summary
== wiretrail summary ==
entries: 2237 total, 2237 after filter
duration (first start to last response): 1354.1s
status classes:
2xx: 2040
4xx: 57
5xx: 2
top hosts (by request count):
785 api.themoviedb.org
131 yjyuomfgkqwmjvnoxurn.supabase.co
top duplicate calls:
29x POST youtubei.googleapis.com /youtubei/v1/visitor_id ...
hints:
- 29x duplicate calls: POST youtubei.googleapis.com /youtubei/v1/visitor_id ...
- 59 error responses (4xx/5xx/failed)
next useful commands: duplicates · errors · slowest
An optional wiretrail.yaml (in the working directory, or via --config) maps
hosts/paths to named subsystems and declares required-header rules:
ownership:
- name: Payments API
host: "payments.*.foo.com"
owner: payments-team
criticality: high
required_headers:
- host: "api.foo.com"
headers: ["Authorization", "X-App-Version", "Accept"]
rules:
- name: "API calls need auth"
host: "api.foo.com" # globs; optional method/path/status matchers
require_headers: ["Authorization"]
max_latency_ms: 2000
- name: "no staging hosts in prod capture"
host: "*.staging.foo.com"
forbid: true # any match is a violationrules evaluates this list plus any built-in --packs; subsystems falls back to
built-in vendor heuristics then raw host when no ownership rule matches.
Every command redacts by default: auth/cookie headers, sensitive query params,
JSON body keys (password/token/secret/…), JWT signatures, and opaque
secret-bearing blobs embedded in URL path segments (collapsed to {blob} in
aggregate views, <redacted> in show-entry/curl). Pass
--unsafe-include-secrets to reveal raw values for replay. curl labels each
command SAFE/UNSAFE based on method and payment/order keywords.
mmap + a single typed serde_json::from_slice over the mapped bytes — no
intermediate serde_json::Value DOM. Measured on a 143 MB capture (2237 entries):
parse + summary in ~0.5 s, ~314 MB peak RSS (~2.2× file size). Every command runs
in well under a second on that input. Release builds use lto = "fat".
- HAR 1.2 and 1.3 (parsed through a unified permissive model; unknown fields ignored).
- Reads JSON HAR exports from Chrome/Edge DevTools, Charles, Proxyman, HTTP Toolkit, mitmproxy, and others.
HAR cannot prove low-level packet loss, full TLS certificate chains, client call stacks, or proxy/service-worker internals unless captured in custom fields. JWT analysis is structural only (no signature verification). N+1 detection is a best-effort heuristic. See USERGUIDE.md for details and worked examples.
This repo ships a Claude Code plugin that teaches the assistant to drive wiretrail.
It auto-triggers on "analyse/debug HAR", "wiretrail", or a .har file in the
conversation, and adds an /analysing-har slash command.
# One-time: add the marketplace
/plugin marketplace add johnneerdael/wiretrail
# Install the plugin
/plugin install analysing-har@analysing-har
# Use it
/analysing-har path/to/capture.har
# …or just say "analyse this HAR" / "debug this HAR" and the skill triggers.
The plugin wraps the wiretrail CLI (auto-installs via cargo install wiretrail
if missing) and follows the triage workflow in USERGUIDE.md.
MIT. See LICENSE.