Traffic and metrics
Every connection HLE runs is measured, and every one of them is measured the same way: a chart of traffic over time, and three numbers above it. Whether you are looking at a tunnel, a webhook receiver, an agent, or a firepuncher forward, the widget is the same and the axes mean the same thing.
Where to find it
| You want | Open |
|---|---|
| Everything on the account | Dashboard → Overview |
| One tunnel or webhook receiver | its page, from the connections list |
| One agent, summed over its endpoints | Agents → the agent |
| An agent’s TCP forwards | Firepuncher, on the agent |
Each view offers 24 hours, 7 days and 30 days.
What the three numbers mean
The metric strip changes with what you are looking at, because the honest answer is different for each. Nothing is averaged across things that cannot be meaningfully averaged.
| View | Metrics |
|---|---|
| Account | Requests · Traffic · WS streams |
| Tunnel | Requests · Traffic · Latency |
| Webhook receiver | Deliveries · Traffic · Latency |
| Firepuncher | Forwards · Traffic · Median length |
Two deliberate omissions:
- The account view has no latency. Latency is a property of a connection. Averaging it across every tunnel on an account produces a number that describes nothing.
- Firepuncher counts forwards, not requests. A forward is a TCP session, so its useful second dimension is how long it lasted, not how quickly it replied.
A forward that is still open counts, and its traffic is included as the agent relays it — an SSH session held open for hours would otherwise show as nothing at all until you closed it. When something is open you get a fourth number, Open now, which is what explains a traffic figure that keeps climbing while the forward count and the median sit still. The median covers finished forwards only: an open session’s age is not its length.
For a forward, in means bytes travelling toward the target service and out
means bytes coming back — the same direction convention as a tunnel’s request
and response bodies, so bars point the same way on every chart.
Webhook outcomes
A webhook receiver’s chart splits deliveries by what happened to them: delivered, failed, rate limited, rejected. This is the fastest way to tell “the provider stopped sending” from “we turned it away”.
Outcomes are recorded hourly. On the 30-day view they are absent rather than approximated, because the coarser table that backs long ranges does not record them — an empty outcome breakdown at 30 days is not a sign that nothing failed.
Resolution and retention
| Range | Bucket | Source |
|---|---|---|
| 24 hours | 1 hour | hourly rows |
| 7 days | 6 hours | hourly rows |
| 30 days | 24 hours | daily rollups |
Per-tunnel hourly rows are purged after two weeks, so ranges longer than 7 days are served from the rollups. The account’s own hourly table is never purged, which is why the account chart stays hour-resolution at every range.
Reading it from a script
The dashboard has no privileged path — it calls the same endpoint you can.
curl -s -H "Authorization: Bearer $HLE_API_KEY" \ "https://hle.world/api/metrics/series?kind=account&range=7d"GET /api/metrics/series kind=account | tunnel | agent | firepuncher (required) subdomain=<subdomain> (required for kind=tunnel) agent_id=<id> (required for agent, firepuncher) range=24h | 7d | 30d (default 24h)The response is one shape for every kind:
{ "kind": "tunnel", "range": "24h", "bucket_hours": 1, "hourly": true, "points": [ { "t": "2026-08-13T09:00:00Z", "requests": 412, "ws_streams": 3, "bytes_in": 91233, "bytes_out": 8812301, "latency_ms": 47, "delivered": 0, "failed": 0, "rate_limited": 0, "rejected": 0 } ], "metrics": [ { "key": "requests", "label": "Requests", "unit": "count", "value": 9812 }, { "key": "traffic", "label": "Traffic", "unit": "bytes", "value": 40122333 }, { "key": "latency", "label": "Latency", "unit": "ms", "value": 47 } ]}metrics is what the strip shows, already labelled — render it rather than
hard-coding names, so a view that gains a metric does not need your script
changed. A value of null means “no data in this range”, not zero.
The connection log
The chart says how much; the log says who. Every request that met a tunnel’s
access rules — and every one that did not — is recorded with the client
address, which rule matched, and the outcome. Filter it by event type, tunnel
and date range from Activity, or read it from /api/logs.
Retention follows your plan: 7 days on Free, up to 30 on PAYG.
When you need the bodies
Metrics deliberately contain no payloads. When a specific tunnel is misbehaving in a way counters cannot explain, HLE has a per-tunnel capture mode that records full requests and responses for a bounded window.
It is administrator-initiated and off by default — capture is a
privacy-sensitive operation, not something a chart turns on. If you are
debugging a tunnel on hle.world and need it, ask; if you run your own relay, see
debug_capture.py.
Related
- Agent — one process, many endpoints
- Firepuncher — TCP forwards, and their forward log
- Webhooks — receivers, rate limits and delivery outcomes