# Query language

> 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.

Source: https://docs.subcloak.com/cloak-proxy/inspect/query-language/

Added in Cloak Proxy 0.4.1.

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.

*[Screenshot: The traffic list filtered by the query host:*.shop.example -host:cdn.* dur:>150ms, typed in the quick filter with the field names in blue and the negation in orange. An Order column shows order IDs such as ord_4821, server errors are tinted red and slow checkouts yellow. A typed query, an extracted Order column and rows colored by highlight rules.]*

## Type a query

1. Press <kbd>⌘</kbd> <kbd>F</kbd>, or click the search field at the right of the filter bar.

2. 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.

3. Add more conditions, separated by spaces. A request must meet all of them.

   For example `status:5xx app:ShopApp` shows server errors from ShopApp only.

4. Press <kbd>Esc</kbd> to close the suggestions, and <kbd>Esc</kbd> 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](https://docs.subcloak.com/cloak-proxy/inspect/filtering/#the-found-in-column) shows where those words were found.

> **Tip:**
> To search for text that looks like a field, such as `id:42` in a body, put it in quotes: `"id:42"`. Choosing a match other than **Contains** in the magnifying glass menu (such as **Regex**) also takes the text literally.

### Autocomplete

A list under the bar suggests what can come next:

- **Field names** as you type a word: `sta` offers `status:`, `dur` offers `duration:`.
- **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.` or `resp.header.`, and your extract columns after `col.`.
- **Saved queries** while the bar is empty.

*[Screenshot: The query bar with status:>=400 app: typed and a list of the session's apps under it: curl, Google Chrome, MyApp, Safari, ShopApp, Slack and SyncAgent. Values come from your session: here, the apps that sent requests.]*

| Key | Does |
|---|---|
| <kbd>↓</kbd> <kbd>↑</kbd> | Move through the suggestions |
| <kbd>Tab</kbd> | Put the selected suggestion in (the first one if none is selected) |
| <kbd>Return</kbd> | Put in the suggestion you moved to; otherwise filter now and close the list |
| <kbd>Esc</kbd> | 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

| 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

| 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

`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

| 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](https://docs.subcloak.com/cloak-proxy/inspect/websocket-and-graphql/) operation's name or type, or `errors` |
| `contract` | | What [API Contracts](https://docs.subcloak.com/cloak-proxy/testing/contract-checking/) found: `violation`, `error`, `warning`, `passed` |
| `trace` | `traceid` | A [distributed-tracing](https://docs.subcloak.com/cloak-proxy/inspect/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](https://docs.subcloak.com/cloak-proxy/devices/browser-extension/) |

JSON paths use the same [JSONPath](https://docs.subcloak.com/cloak-proxy/tools/conditions-and-graphql/) 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

| 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](https://docs.subcloak.com/cloak-proxy/inspect/streams/) (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

| 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

  **Save a query**

    1. Type the query.
    2. Click the magnifying glass at the left of the bar and choose **Save Query…**.
    3. Type a name and click **Save**. Saving under an existing name replaces that query.

  **Use a saved query**

    Click the magnifying glass and choose the query under **Saved Queries**, or click in the empty bar: saved queries are the first suggestions.

  **Delete one**

    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

The [Multiple Filters panel](https://docs.subcloak.com/cloak-proxy/inspect/filtering/#combine-several-conditions) 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:>500ms` or `app: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**.

*[Screenshot: The Multiple Filters panel with four conditions: Host ends with .shop.example, Response Body not contains timeout, Status Code starts with 5, and a Query row with duration:>500ms. Below them: As a query host:*.shop.example -resp.body:timeout status:5xx duration:>500ms, and a Move to Query Bar button. Conditions and the same filter as a query.]*

## 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

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:slow` finds 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.

*[Screenshot: The Highlight and Extract Rules window. A list of four rules on the left: Order, Server errors in red, Slow in yellow and Request ID. The Order rule is selected: it applies to host:api.shop.example, extracts $.order.id from the response body by JSONPath and shows as a column. Below, the captured requests it matches with their order IDs. An extract rule, with what it finds in the captured traffic.]*

### Create a rule

1. Choose **Tools › Highlight and Extract Rules…** (<kbd>⇧</kbd> <kbd>⌘</kbd> <kbd>E</kbd>).

   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.

2. Click **New Rule** and choose **Extract Column** or **Highlight Rule**.

3. Fill in the rule. Changes apply as you make them; there's nothing to save.

4. 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.

> **Note:**
> JSONPath reads decompressed bodies of up to 2 MB; a regex reads the first 2 MB of a body. Values are worked out when a row is shown or a query asks for them, and are kept until the request changes, so rules cost nothing for rows you don't look at.

## 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

The [MCP server](https://docs.subcloak.com/cloak-proxy/automation/mcp-server/) gives assistants the same language, so what an assistant filters is a query you can read and change:

| Tool | Does |
|---|---|
| [`explain_query`](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#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`](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#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`](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#list_requests) and [`search_requests`](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#search_requests) | Take a `query` argument |
| [`list_extract_rules`](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#list_extract_rules), [`save_extract_rule`](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#save_extract_rule), [`delete_extract_rule`](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/#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

[`cloakctl`](https://docs.subcloak.com/cloak-proxy/automation/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:

```sh
# Exit 1 if any request in the recording was a server error, except analytics
cloakctl query 'status:5xx -host:*.example.net' run.har --fail-if-any

# How many slow requests the running app captured
cloakctl query 'app:ShopApp dur:>1s' --count

# Values from extract rules, as JSON
cloakctl query 'path:/v1/orders*' run.har --rules rules.json --json

# Check a query
cloakctl 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.

## Related

  - [Filtering](https://docs.subcloak.com/cloak-proxy/inspect/filtering/): The quick filter, content tabs, Multiple Filters and presets.
  - [Traffic list](https://docs.subcloak.com/cloak-proxy/inspect/traffic-list/): Columns, sorting, highlights and comments.
  - [Rule conditions](https://docs.subcloak.com/cloak-proxy/tools/conditions-and-graphql/): The same JSONPath and GraphQL matching for debugging tools.
  - [MCP tools reference](https://docs.subcloak.com/cloak-proxy/automation/mcp-tools-reference/): Every tool's arguments, including the query tools.
