Entries, ids and endpoints¶
Three ideas hold the commands together: an entry is one request, an id names it, and an endpoint is the shape of a request with its variable parts removed.
Entries and ids¶
Each request in the capture is an entry. WireTrail numbers them in the order they appear in the file, starting at zero, and writes the number as e and six digits: e000000,
e000001, e000069.
- An id is stable. It does not change between commands, between runs or with
--filter. Entrye000069is the same request everywhere, so you can find it inerrors, open it withshow-entryand replay it withcurl. - It depends on the file order, not on time. Most tools write entries in start order, so the two agree.
- A different file has different ids. An id from one capture means nothing in another.
show-entry and curl also accept the bare number: 14 and e14 both mean e000014.
Endpoints¶
Looking at every URL separately would hide the pattern. /v1/products/1042/price and /v1/products/1043/price are one thing asked twelve times. So WireTrail
replaces the parts of a path that identify a thing with a placeholder, and calls the result a normalized path.
| A path segment that is… | Becomes | Example |
|---|---|---|
| All digits, with some exceptions below | {id} |
1042 → {id} |
| A UUID | {id} |
550e8400-e29b-41d4-a716-446655440000 → {id} |
| Hexadecimal and at least 16 characters | {id} |
0123456789abcdef0123 → {id} |
| An opaque blob: base64-like or percent-encoded, long | {blob} |
eyJyZWdpb24iOiJubCIs… → {blob} |
| Anything else | kept | v1, products, cart |
A single digit as the first segment is kept, so /3/tv/popular stays as it is. Every other all-digit segment is an id.
A blob is what hides a secret in a path, such as a configuration object encoded into the URL. The test is long: at least 16 characters, not all hex, not a UUID, and
either percent-encoded or in the base64 alphabet with a mix of cases, digits or +/= that slugs do not have. The demo capture has one:
12 GET api.shop.example/v1/products/{id}/price
3 POST o0.ingest.sentry.io/api/{id}/envelope/
2 GET api.shop.example/v1/inventory/{id}
1 PUT api.shop.example/v1/cart/items/{id}
1 GET api.shop.example/v1/receipts/{id}/download
1 GET cdn.shop.example/cfg/{blob}/manifest.json
An endpoint is a method, a host and a normalized path. GET api.shop.example/v1/products/{id}/price is one endpoint, however many products were fetched.
endpoints, compare, pagination and diff group by it, and duplicates and retries use it as part of their key.
The raw path is still there¶
Normalization changes what is shown and what commands group by. It does not change the request. --filter "path:..." matches the raw path as it appeared in the
URL, so to select the twelve price calls you write a * where the id goes:
show-entry prints the raw URL, with opaque path segments masked. See How redaction works.
What normalization gets wrong¶
Normalization is a pattern, and patterns misfire:
- A number that is not an id.
/page/2and/api/2024/itemsbecome/page/{id}and/api/{id}/items. Pagination in the path folds into one endpoint. - An id that is not a number.
/users/annand/users/bobstay as two endpoints, because a name looks like a word. - A slug with a long mix of characters can be mistaken for a blob, and a short token is never mistaken for one.
When a grouping looks wrong, open one entry with show-entry and read the raw path.
Query strings¶
The query is not part of the normalized path. Commands that care about it, such as duplicates, build a fingerprint: method, host, normalized path and the query pairs,
sorted, without the cache-buster keys _, cb, cachebuster, nonce, ts and timestamp. Two calls with the same fingerprint are the same call as far as WireTrail is
concerned. The body is not in the fingerprint.
Related¶
- Duplicates uses the fingerprint.
- Filter entries explains
path:against the raw path. - Hosts, subsystems and endpoints shows the endpoint list.