Traffic Inspector
Live Tail
View inbound requests in real time with searchable payloads. The local inspector ships with the agent: while a tunnel is running it serves a browser dashboard at http://127.0.0.1:<http_port> (default 9090) plus a terminal UI, and the ngris inspect / replay / export commands talk to that same running agent over loopback. It reads the agent's own captured requests, so no entitlement is required.
Click any request for a detail panel: method, path, status, and timing, plus the request headers, cookies, and body — and, while the dashboard is open, the response headers and body too. Each request also has Replay and Edit & Replay (see Replay).
The hosted web dashboard offers the same view over edge traffic as a paid feature, gated by the traffic_inspector entitlement, alongside Security Analytics.
- Dimensions:
host,path,method,source_ip,status,protocol,asn,country,ua(user-agent),browser,os. - Operators:
equals,not_equals,contains,starts_with,ends_with,in,not_in. The substring operators (contains/starts_with/ends_with) apply to the free-text dimensions (host,path,ua); the enumerable dimensions takeequals/not_equals/in/not_in.in/not_intake a comma-separated value list.
# List captured requests (newest first)
ngris inspect
# Search method/path/headers/body, only failures, and live-tail
ngris inspect --search "payment_intent" --status 4xx,5xx --follow
# Machine-readable output → pipe into jq
ngris inspect --json | jq '.[].path'
# Point at a non-default inspector port
ngris inspect --port 9091 --limit 200
Security Analytics
The Security Analytics page (/account/security/analytics) is a security-framed lens over the same traffic data the metrics page reads. Where the metrics page answers “how is my service performing,” this one answers “who is attacking me and what stopped them.” It is a paid feature gated by the security_analytics entitlement; without it the page renders an upgrade prompt instead of data.
What “mitigated” means
A request counts as mitigated when it was blocked by a firewall policy or a rate-limit rule at the edge. Everything else is allowed. The page splits mitigations into two sources so you can tell which control is doing the work:
- Mitigated (firewall) — a firewall policy returned a block.
- Mitigated (rate limit) — a rate-limit rule returned a block (and no firewall rule already blocked it — firewall takes precedence, so the two never double-count).
The top KPIs show total requests, allowed, mitigated-by-firewall, mitigated-by-rate-limit, and the overall mitigation rate (mitigated ÷ total). A stacked time series plots allowed vs the two mitigation sources across the window.
Attacker fingerprints & top dimensions
Each panel ranks the top values of a dimension by mitigated requests first, then total — so the noisiest attackers float to the top, not just the busiest legitimate clients. Every row shows total, mitigated, and the per-value mitigation rate:
| Panel | What it ranks |
|---|---|
| Top source IPs | The client IPs sending the most mitigated traffic. |
| Top ASNs | Networks by ASN, labelled AS<n> <org> (resolved from request geo data). |
| Top countries | Source country (rows with no geo data are excluded). |
| Top user agents | The raw User-Agent strings. |
| Top paths / hosts | The request paths and hostnames being targeted. |
| Top browsers / OS | Browser and operating-system families classified from the User-Agent (Chrome, Firefox, Safari, Edge, Opera, curl, Bot, … / Windows, macOS, iOS, Android, Linux, ChromeOS, …). |
| Top firing rules | Which firewall policies and rate-limit rules are actually catching traffic, by hit count. |
A recently mitigated table lists the latest blocked requests with timestamp, source IP, country, host, path, method, status code, and the rule that stopped them.
Drill through to a firewall rule
Every top-firing-rule row links straight to the rule that produced it — firewall rows open the matching firewall policy, rate-limit rows open the matching rate-limit rule. You can also pivot from any top-dimension value (an IP, ASN, country, path…) into a pre-filtered view, so investigating “everything from AS<n>” is one click. The same pivot links work into the Traffic Inspector.
Filters & the shared condition builder
Scope the whole page with a time range (30m, 1h, 6h, 12h, 24h, 7d, 30d, or a custom from/to range), an optional per-endpoint filter, and the condition builder — the same one the Traffic Inspector uses. Each condition combines a dimension, an operator, and a value, and the filters apply server-side across the whole window:
- Dimensions:
host,path,method,source_ip,status,protocol,asn,country,ua(user-agent),browser,os. - Operators: the free-text dimensions (
host,path,ua) acceptequals,not_equals,contains,starts_with,ends_with,in,not_in. The enumerable dimensions (method,source_ip,asn,country,browser,os) acceptequals,not_equals,in,not_in.in/not_intake a comma-separated list (capped at 50 values). A missing operator defaults toequals, which is why bare drill-through links (e.g.?source_ip=…) keep working.
security_analytics entitlement; the Traffic Inspector requires the traffic_inspector entitlement. They share the same member-level read permission (traffic_inspector), since both are read views over the same traffic data.Replay
Re-send a captured request to your tunnel's local backend — verbatim, or after editing the method, path, headers, or body — then read the response. Replay bypasses the public tunnel/edge and does not follow redirects, so you see exactly what your own backend returns.
From the local dashboard or TUI, open a request and use Replay (re-send verbatim) or Edit & Replay (change method / path / headers / body first). Replays land back in the list as new rows badged replay. Arbitrary-target replay (--to <url>), --dry-run, and batch replay are not available — replay always targets the tunnel's own local backend.
# Find the request ID
ngris inspect --status 5xx
# Replay it verbatim to the local backend
ngris replay --id <req_id>
# Edit the method, headers, and body, then replay
ngris replay --id <req_id> --method POST \
--set-header "X-Debug: 1" \
--remove-header Cookie \
--body '{"test": true}'