Inspect traffic
Query language
Since 0.4.1 Requires Cloak Proxy 0.4.1 or later.
Filter captured traffic in Cloak Proxy with one typed line such as status:5xx app:ShopApp dur:>500ms -host:cdn.*, with autocomplete and saved queries, and color requests or pull values into columns with highlight and extract rules.
The quick filter is also a query bar. Type what you’re looking for as conditions, such as status:5xx app:ShopApp dur:>500ms -host:cdn.*, and the traffic list shows only the requests that meet all of them. Fields are colored as you type, autocomplete suggests fields and the values in your session, and you can save a query to use again.
Highlight and extract rules use the same language. A rule colors and tags the requests a query picks, and it can copy a value from each one, such as an order ID, into its own column of the traffic list.
A typed query, an extracted Order column and rows colored by highlight rules.
- The query bar, colored as you type
- How many requests match
- A column filled by an extract rule
- A row colored by a highlight rule
Type a query
Section titled “Type a query”-
Press ⌘ F, or click the search field at the right of the filter bar.
-
Type a field name and a colon, then a value. For example
status:5xx.As soon as the text contains a field, the field turns blue and the bar widens. The list updates as you type, or after a short pause when the query reads headers or bodies.
-
Add more conditions, separated by spaces. A request must meet all of them.
For example
status:5xx app:ShopAppshows server errors from ShopApp only. -
Press Esc to close the suggestions, and Esc again or the clear button to remove the query.
Text without any field is a plain search, exactly as before: it searches the URL, or whatever Search in says in the magnifying glass menu. Words next to fields are searched the same way, so status:5xx checkout shows server errors whose URL contains checkout. With Search in set to All or a body field, the Found In column shows where those words were found.
Autocomplete
Section titled “Autocomplete”A list under the bar suggests what can come next:
- Field names as you type a word:
staoffersstatus:,duroffersduration:. - Values after a colon: status classes and codes, methods, the hosts and apps in your session, content types,
is:flags, GraphQL operations, and example durations and sizes. - Header names after
header.,req.header.orresp.header., and your extract columns aftercol.. - Saved queries while the bar is empty.
Values come from your session: here, the apps that sent requests.
| Key | Does |
|---|---|
| ↓ ↑ | Move through the suggestions |
| Tab | Put the selected suggestion in (the first one if none is selected) |
| Return | Put in the suggestion you moved to; otherwise filter now and close the list |
| Esc | Close the list |
When part of the query can’t be read, the list shows why at the bottom, the part is underlined in red, and an orange triangle appears before the bar (hold the pointer over it to read the problem). The rest of the query still filters, so the list doesn’t empty while you finish a term.
Syntax
Section titled “Syntax”| Write | Means |
|---|---|
field:value |
The field contains the value (text), or equals it (numbers) |
a b |
Both (AND). AND and && also work |
a OR b |
Either one. | and || also work. OR is uppercase |
-a, !a, NOT a |
Not a |
( … ) |
A group, such as (status:401 OR status:403) path:/admin* |
field:a,b |
Either value, such as method:PUT,PATCH |
"two words" |
A value or word with spaces. \" is a quote inside it |
AND binds tighter than OR: a OR b c means a OR (b c).
Matching text
Section titled “Matching text”| Write | Matches when the value… |
|---|---|
host:example |
Contains example, ignoring case |
host:*.example.com |
Matches the whole pattern. * stands for any run of characters |
host:=api.example.com |
Is exactly api.example.com, ignoring case |
path:~^/v[0-9]+/ |
Matches the regular expression (ICU syntax, ignoring case). resp.body~error also works |
header.X-Debug:* |
Exists, with any value. -header.X-Debug:* means it’s missing |
method and scheme match exactly by default, so method:GET doesn’t match a custom GETX.
Matching numbers
Section titled “Matching numbers”status, duration, size, req.size, port and id take numbers.
| Write | Matches |
|---|---|
status:404 |
Exactly 404 |
status:5xx |
500 to 599 (1xx to 5xx) |
status:200-299 |
A range |
status:>=400, >, <, <= |
Comparisons. dur>500ms without the colon also works |
dur:>1.5s |
Durations in ms (the default), s or m |
size:>1MB |
Sizes in bytes (the default), KB, MB, GB (1,000) or KiB, MiB, GiB (1,024) |
A request with no response doesn’t match status: or size: comparisons, and one still in progress doesn’t match duration:. Use is:pending and is:failed for those.
Fields
Section titled “Fields”| Field | Also written | Matches |
|---|---|---|
status |
code, sc |
The response status code |
method |
m, verb |
The HTTP method |
host |
domain |
The host name |
path |
The path, with the query string | |
url |
The whole URL | |
param.name |
query.name, qp.name |
A query-string parameter: param.page:2 |
scheme |
http or https |
|
port |
The port | |
version |
proto |
The HTTP version: version:2 |
app |
process |
The app that sent the request (its name or bundle ID) |
device |
Another device, by address, kind (iPad) or name |
|
ip |
client |
The client’s address |
type |
kind |
A content-type tab’s name (json, image, graphql…), or else the Content-Type |
mime |
ctype, content-type |
The Content-Type header |
duration |
dur, latency, took |
How long the request took |
size |
resp.size |
The response body’s size |
req.size |
reqsize |
The request body’s size |
body |
The request or response body, decompressed | |
req.body |
reqbody |
The request body |
resp.body |
res.body, response.body |
The response body |
header |
headers |
Any header as a Name: Value line, or one header’s value: header.X-Cache:HIT |
req.header |
request.header |
A request header: req.header.Authorization:Bearer* |
resp.header |
res.header |
A response header: resp.header.Cache-Control:no-store |
json.path |
A value in the response’s or request’s JSON body: json.$.user.id:42 |
|
req.json.path |
A value in the request’s JSON body | |
resp.json.path |
res.json.path |
A value in the response’s JSON body: resp.json.$.total:>100 |
gql |
graphql, op, operation |
The GraphQL operation’s name or type, or errors |
contract |
What API Contracts found: violation, error, warning, passed |
|
trace |
traceid |
A distributed-tracing ID in the headers: W3C traceparent, B3, AWS X-Ray, Google Cloud, Jaeger, Sentry or Datadog |
tag |
tags |
Tags set by debugging tools and by highlight rules |
rule |
The name of a highlight or extract rule that matched | |
col.Name |
column.Name |
The value an extract rule put in its column: col.Order:ord_* |
id |
The request’s number in the list | |
text |
any, all |
Anywhere: URL, method, status, headers and bodies |
is |
One of the flags below | |
tab |
browser.tab |
The browser tab that made the request, by title or URL, from the browser extension |
JSON paths use the same JSONPath as rule conditions; the leading $. can be left out (resp.json.order.id:7). With >, < and the other comparisons, JSON values and columns compare as numbers.
is: flags
Section titled “is: flags”| Flag | Requests that… |
|---|---|
is:error |
Failed, got a 4xx or 5xx, or got GraphQL errors |
is:failed |
Got no response (refused, timed out, a TLS error) |
is:pending |
Are still waiting for the response |
is:complete |
Have a response |
is:redirect |
Got a 3xx |
is:websocket |
Upgraded to a WebSocket |
is:graphql |
Are GraphQL requests |
is:gqlerror |
Got a 2xx GraphQL response with errors |
is:decrypted |
Are HTTPS that Cloak Proxy decrypted |
is:tunnel |
Are encrypted tunnels that weren’t decrypted |
is:stream |
Were streamed (SSE, JSON lines, chunks) |
is:device |
Came from another device |
is:modified |
Were changed or answered by a debugging tool such as Map Local |
is:highlighted |
Were colored by a highlight rule |
Examples
Section titled “Examples”| To find | Query |
|---|---|
| Server errors from one app, except the CDN | status:5xx app:ShopApp -host:cdn.* |
| Slow checkout calls | path:/checkout* dur:>1s |
| Every failure except analytics | is:error -host:*.example.net |
| One GraphQL mutation that failed | gql:CreateOrder is:error |
| Large responses that weren’t cached | size:>1MB header.X-Cache:MISS |
| Unauthorized or forbidden admin calls | (status:401 OR status:403) path:/admin* |
| Orders over 100 | resp.json.$.order.total:>100 |
| Requests without an auth header | -req.header.Authorization:* |
| Every request of one trace | trace:4bf92f3577b34da6 |
| Timeouts, by regex in the body | resp.body~"time(d )?out" |
| What one browser tab sent | tab:Checkout |
| Requests no tab reported (background traffic) | -tab:* |
Save queries
Section titled “Save queries”- Type the query.
- Click the magnifying glass at the left of the bar and choose Save Query….
- Type a name and click Save. Saving under an existing name replaces that query.
Click the magnifying glass and choose the query under Saved Queries, or click in the empty bar: saved queries are the first suggestions.
Click the magnifying glass and choose Delete Saved Query › the query.
Saved queries are kept between launches. An assistant can save one too, with set_traffic_filter and save_as.
Queries and the Filters panel
Section titled “Queries and the Filters panel”The Multiple Filters panel and the query bar convert into each other:
- In the magnifying glass menu, Open in Filters Panel turns each term of the query into a condition row and opens the panel. Terms the panel has no field for, such as
dur:>500msorapp:ShopApp, become Query rows. - Under its conditions, the panel shows them As a query. Move to Query Bar moves them into the bar as one typed query.
- A Query condition takes a typed query, so any query can be one condition among others, combined with all or any.
Conditions and the same filter as a query.
Add to a query from the list
Section titled “Add to a query from the list”Right-click a request and choose Add to Query to add one of its details as a term: its host, app, method, status, endpoint (path:/v1/orders/*) or GraphQL operation. The same submenu has Exclude versions (-host:cdn.example.com). A term that’s already there isn’t added twice, and adding a term replaces its opposite.
Highlight and extract rules
Section titled “Highlight and extract rules”A rule picks requests with a query and then:
- Colors their rows in the traffic list. A color you set by hand with Highlight wins over a rule’s.
- Tags them, so
tag:slowfinds them. - With a pattern, copies a value from each one into a column named after the rule. You can sort by the column, and filter with
col.Order:ord_*.
Rules apply to the whole session, live, as requests arrive.
An extract rule, with what it finds in the captured traffic.
- Rules, in order: the first matching color wins
- Which requests the rule applies to
- Where the value is, and how to find it
- Color and tag
- What it finds in captured traffic
Create a rule
Section titled “Create a rule”-
Choose Tools › Highlight and Extract Rules… (⇧ ⌘ E).
You can also right-click the traffic list’s column headers and choose Add Extract Column…, right-click a request and choose Highlight › Highlight Similar Requests…, or choose Highlight and Extract Rules… in the query bar’s magnifying glass menu.
-
Click New Rule and choose Extract Column or Highlight Rule.
-
Fill in the rule. Changes apply as you make them; there’s nothing to save.
-
Check In captured traffic at the bottom: how many of the newest requests (up to 20,000) the rule matches, and the first ones, with the values it found.
| Setting | What it does |
|---|---|
| Name | The column’s title, and what col.Name: and rule:Name refer to |
| On | Turns the rule off without deleting it. The checkbox in the list does the same |
| Applies to | A query choosing the requests, in the query bar’s syntax. Empty means every request. It can’t use col:, rule:, tag: or is:highlighted, which depend on the rules themselves |
| From | Where the value is: Response body, Request body, URL, Request headers or Response headers (headers as Name: Value lines) |
| JSONPath or Regex | How to find it. JSONPath reads JSON bodies and takes the first value it finds. A regex takes its first group, or the whole match without one, ignoring case |
| Pattern | The JSONPath ($.order.id) or regex (X-Request-Id: (\S+)). Leave it empty for a rule that only colors and tags |
| Show as a column in the traffic list | Adds the column. Turn it off to use the value only in queries (col.Name:) |
| Color | The row’s tint. When several rules match, the first one with a color in the list wins |
| Tag | Added to matching requests’ tags |
A rule with a pattern only matches requests where the pattern finds a value, so its color shows the requests that have one. Values longer than 500 characters are cut. Right-click a rule in the list to duplicate it, turn it off or delete it.
Large sessions
Section titled “Large sessions”Typed queries run on the same incremental engine as the rest of the traffic list. Changing the query filters every request again on all of your Mac’s cores; new requests are then checked one batch at a time as they arrive. In a test session of 500,000 requests, status:5xx app:ShopApp dur:>500ms -host:cdn.* takes about a third of a second on an M-series Mac, and new traffic is placed in a few milliseconds. Queries that read bodies or JSON take longer the first time, while bodies are decompressed.
Use queries from an AI assistant
Section titled “Use queries from an AI assistant”The MCP server gives assistants the same language, so what an assistant filters is a query you can read and change:
| Tool | Does |
|---|---|
explain_query |
Checks a query and says what it means in words, with any problems. Without a query, lists every field and flag |
set_traffic_filter |
Puts a query in the query bar of the traffic list in front, and returns what matches. save_as also saves it |
list_requests and search_requests |
Take a query argument |
list_extract_rules, save_extract_rule, delete_extract_rule |
Manage highlight and extract rules |
For example, ask “show me ShopApp’s slow checkout calls” and the assistant can set app:ShopApp path:/checkout* dur:>1s in the bar.
Use queries from the command line
Section titled “Use queries from the command line”cloakctl filters a saved session, or the running app’s traffic, with a query. In CI, fail a build when a recording has requests it shouldn’t:
# Exit 1 if any request in the recording was a server error, except analyticscloakctl query 'status:5xx -host:*.example.net' run.har --fail-if-any
# How many slow requests the running app capturedcloakctl query 'app:ShopApp dur:>1s' --count
# Values from extract rules, as JSONcloakctl query 'path:/v1/orders*' run.har --rules rules.json --json
# Check a querycloakctl query --explain 'status:5xx app:ShopApp dur:>500ms'| Option | Does |
|---|---|
FILE |
A .har or .cloaksession file. Without one, the running app’s traffic |
--count |
Prints only how many requests matched |
--json |
Prints the matching requests as JSON |
--limit N |
Prints only the newest N |
--rules FILE |
Applies highlight and extract rules from a JSON array (as list_extract_rules returns them), so col.Name: and rule: work and the values are printed |
--fail-if-any |
Exit code 1 when something matches |
--fail-if-none |
Exit code 1 when nothing matches |
--explain |
Prints the query in words, normalized, with its problems |
The exit code is 2 when the query has a problem; the problem is printed with where it is. cloakctl list --query '…' also takes a query.