> For the complete documentation index, see [llms.txt](https://docs.p0.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.p0.dev/inventory/query-search/query-language-basics.md).

# Query Language Basics

This guide introduces the query language you use to search the Access Inventory. It assumes no prior experience. By the end, you can read and write queries like `identity:->identity:->entitlement:->risk:` and understand exactly what they return.

If you want the complete reference — every operator, search term, and attribute — see the [Search Reference](/inventory/query-search/search-reference.md). For an overview of the search interface itself, see [Query Search](/inventory/query-search.md).

## Your inventory is a graph

P0 models your cloud IAM configuration as a *graph*: a set of items (called **nodes**) connected by relationships (called **edges**).

Each node is one piece of your access setup, such as a user, an access key, or a permission grant. Each edge records how two nodes relate, such as "this identity holds this entitlement."

You query the inventory by describing a pattern of nodes and the connections between them. P0 finds every part of your graph that matches the pattern and shows you the results.

{% hint style="info" %}
You don't need to know graph theory to write queries. Think of each query as a sentence that describes the access you're looking for, reading from left to right.
{% endhint %}

## The core search terms

The most common nodes you search for are the four that follow. Type one of these words to limit your search to that kind of node.

| Search term   | What it represents                                                            | Example                                          |
| ------------- | ----------------------------------------------------------------------------- | ------------------------------------------------ |
| `identity`    | A user, group, machine identity, or role that can authenticate.               | An employee, a service account, an AWS IAM role. |
| `credential`  | A single way to authenticate, such as an access key or a federated SSO login. | An AWS access key, a short-lived token.          |
| `entitlement` | A grant that gives an identity privileges over one or more resources.         | An AWS policy attachment, a GCP role binding.    |
| `risk`        | A security risk that a privilege carries.                                     | A privilege that allows data exfiltration.       |

These four are the terms you use most often, but they aren't the only ones. The inventory also models `resource`, `privilege`, `usage`, and more. For the complete list, see [Search types](/inventory/query-search/search-reference.md#search-types).

## The colon operator: `:`

The colon turns a word into a **node-type filter**. On its own, a term followed by a colon matches every node of that type:

```
identity:
```

This query matches every identity in your inventory.

To narrow the search, add a keyword after the colon. P0 then matches nodes of that type whose name contains the keyword:

```
identity:alice
```

This query matches identities whose name contains `alice`.

Read the colon as "that is a." So `identity:alice` reads as "a node *that is an* identity, named `alice`."

{% hint style="info" %}
A colon with nothing after it (`risk:`) is a presence search: it finds every node of that type. Use it to answer questions like "which entitlements carry any risk at all?" with `entitlement:->risk:`.
{% endhint %}

## The type argument

Many node types come in several varieties. An identity might be a user, a group, or a machine role. A credential might be a static key or a short-lived token. To filter by variety, use the `type` argument.

The `type` argument is an *attribute* of a node, so you write it as a second colon-separated value:

```
identity:type:user
```

This reads as "an identity whose `type` is `user`." The pattern is always:

```
<search term>:type:<value>
```

Here are some common examples:

| Query                           | Matches                                          |
| ------------------------------- | ------------------------------------------------ |
| `identity:type:user`            | Human user identities.                           |
| `identity:type:aws-iam-role`    | AWS IAM roles.                                   |
| `identity:type:service-account` | Machine identities (service accounts).           |
| `credential:type:key`           | Static secret credentials, such as access keys.  |
| `credential:type:short-lived`   | Ephemeral credentials, such as temporary tokens. |
| `usage:type:unused`             | Privileges that have gone unused.                |

{% hint style="info" %}
`type` is one of several attributes you can filter on. Each search term supports its own attributes, such as `identity:status` or `credential:last90`. See [Search attributes](/inventory/query-search/search-reference.md#search-attributes) for the full set and their allowed values.
{% endhint %}

## The inverted operator: `!`

Put a `!` immediately before a keyword to match nodes whose value *does not* contain it. This is the **inverted** (or "NOT") operator.

```
identity:status:!disabled
```

This reads as "an identity whose `status` is *not* `disabled`." It returns every identity that is enabled or whose status is unknown.

The `!` goes after the type and attribute, directly in front of the keyword it inverts. The pattern is:

```
<search term>:<attribute>:!<keyword>
```

{% hint style="info" %}
On its own, an inverted keyword does little, because each identity and grant connects to many values and some won't match anyway. Inverted matches work best combined with a type or attribute, as in `credential:type:!key` (credentials that aren't static keys).
{% endhint %}

The inverted operator (`!`) flips a single keyword. A related operator, the **exclusion** operator (`^`), removes whole results that match a term. For both, see [Inverted matches](/inventory/query-search/search-reference.md#inverted-matches) and [Exclusion matches](/inventory/query-search/search-reference.md#exclusion-matches) in the reference.

## Matching with a regular expression: `/.../`

A plain keyword matches any value that *contains* it. When you need more control — a value that *starts* with something, or matches one of several patterns — wrap a **regular expression** in forward slashes.

```
identity:/^svc-/
```

This reads as "an identity whose name matches the pattern `^svc-`," which finds identities whose name *starts with* `svc-`. The `^` anchors the match to the beginning of the value.

You can use the full standard regular expression syntax, including anchors (`^`, `$`), character classes, and alternation:

```
identity:/prod|staging/
```

This matches identities whose name contains either `prod` or `staging`.

Regex works wherever a keyword does, including after an attribute (`identity:status:/^en/`) and combined with the inverted operator (`identity:type:!/role/`).

{% hint style="info" %}
Regex matches are **case sensitive**, unlike plain keyword matches. To match either case, build it into the pattern — for example, `/[Pp]rod/` rather than `/prod/i`.
{% endhint %}

For escaping rules, limitations, and the equivalent Cypher, see [Regex matches](/inventory/query-search/search-reference.md#regex-matches) in the reference.

## The path operators: `->` and `<-`

A single term finds nodes in isolation. To describe how nodes *connect*, join terms with an arrow. We call this a **path**.

The arrow points along the direction of the graph's edges:

| Operator | Name        | Meaning                                                                  |
| -------- | ----------- | ------------------------------------------------------------------------ |
| `->`     | Child path  | Follow the connection forward, from the left term toward the right term. |
| `<-`     | Parent path | Follow the connection backward, from the left term toward its parent.    |

For example, to find identities that hold an entitlement, follow the connection forward:

```
identity:->entitlement:
```

This reads as "an identity that connects forward to an entitlement." The same relationship viewed from the other side uses the reverse arrow:

```
entitlement:<-identity:
```

Both describe the same connection. You pick the direction that matches how you want to read the query and which nodes you want to start from.

You can chain as many terms as you need, adding one arrow between each pair:

```
identity:->entitlement:->risk:
```

This reads as "an identity that holds an entitlement that carries a risk."

Switch to the graph view to see the matching paths. Each row traces an identity (left) through the entitlement it holds to the risk that entitlement carries (right), with the nodes that match your query highlighted in yellow:

<figure><img src="/files/QUMwzINaCyiXCEXajAbD" alt="Graph view of the query identity arrow entitlement arrow risk, showing identities such as Okta users and AWS IAM roles connected through AWS policy assignments and privileges to critical data-exfiltration risks, with matching nodes highlighted in yellow"><figcaption><p>The graph view for <code>identity:->entitlement:->risk:</code></p></figcaption></figure>

{% hint style="warning" %}
A single path must use one direction throughout. Mixed paths like `a:->b:<-c:` are not supported. Use all `->` or all `<-` within one path.
{% endhint %}

{% hint style="info" %}
Arrows follow the real connections in the graph, so not every combination is valid. For example, `identity:->entitlement:->risk:` works, but `resource:->risk:` doesn't, because resources don't connect directly to risks. The [IAM graph diagram](/inventory/query-search/search-reference.md#iam-graph) shows which connections exist.
{% endhint %}

## Reading a worked example

Now you can read the example query from the top of this guide:

```
identity:->identity:->entitlement:->risk:
```

Walk through it one hop at a time, left to right:

1. `identity:` — Start from an identity.
2. `->identity:` — Follow the connection forward to another identity that the first one is linked to. For example, a user and a group or role that user can act through.
3. `->entitlement:` — Follow the connection forward to an entitlement that this second ("right") identity holds.
4. `->risk:` — Follow the connection forward to a risk that the entitlement carries.

Put together, the query finds **identities that are linked to other identities, the entitlements those linked identities hold, and the risks those entitlements carry**. It's a way to surface indirect risk: access that one identity gains through another, and the danger that access brings.

The graph view makes each hop visible. Here, Okta users reach groups, which hold AWS policy assignments, whose privileges carry critical exfiltration risks — each matching node highlighted in yellow:

<figure><img src="/files/0i56CvmCJV91MkaMpyCC" alt="Graph view of the query identity arrow identity arrow entitlement arrow risk, showing Okta users connected to Okta groups, then to AWS roles and policy assignments, then to privileges that carry critical data-exfiltration and cryptographic-exfiltration risks, with matching nodes highlighted in yellow"><figcaption><p>The graph view for <code>identity:->identity:->entitlement:->risk:</code></p></figcaption></figure>

## More example queries

| Query                                                | What it finds                                     |
| ---------------------------------------------------- | ------------------------------------------------- |
| `identity:`                                          | Every identity in your inventory.                 |
| `identity:type:service-account`                      | Every service account.                            |
| `credential:type:key`                                | Every static access key.                          |
| `identity:->entitlement:`                            | Identities and the entitlements they hold.        |
| `entitlement:->risk:`                                | Entitlements that carry any risk.                 |
| `entitlement:->risk:CRITICAL`                        | Entitlements that carry a critical risk.          |
| `identity:type:service-account->entitlement:->risk:` | Service accounts whose entitlements carry a risk. |

## Build queries visually with the Query builder

If you'd rather not type the syntax by hand, use the **Query builder**. Click the **+** button next to the **where** box, above the table or graph, to open it.

The Query builder is a form that assembles one search term and inserts it into the **where** box for you. In it, you:

1. Choose a connection — **are** (match the displayed node itself) or **can reach** (match a node it connects to).
2. Select a search type, such as **identities** or **entitlements**, and an attribute to filter on.
3. Pick **containing** (substring) or **equal to** (exact), then enter a keyword.
4. Optionally turn on **Invert match?** to apply the `!` operator, **Remove match?** to apply the exclusion operator (`^`), or **Only first node?** to match just the first node in a chain.

A live preview shows the term as you build it. Click **Add term** to drop it into the **where** box, where you can edit it or chain it with other terms.

{% hint style="info" %}
The Query builder produces ordinary query text — the same syntax this guide describes. It's a quick way to discover operators and exact attribute values; once you know the syntax, typing is often faster.
{% endhint %}

## Next steps

* Try a query: open the **Inventory** page, choose what to display in the **show** control, and type a query in the **where** box. See [Access Inventory](/inventory/access-inventory.md) for a tour of the search interface.
* Look up the rest of the operators (exact matches, inverted matches, exclusions, and more) with every search term and attribute in the [Search Reference](/inventory/query-search/search-reference.md).
