> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reticle.sh/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Reticle is a dev-only, localhost-only verification layer for AI coding agents. It reads program truth (network, state, console, routing, animations, framework state) from inside a running web app and returns a deterministic verdict with evidence. It is not a screenshot tool and not a browser automation library.
> Only `reticle_act_and_wait` and `reticle_assert` produce a verdict. Every other tool moves or reads the app and proves nothing. A drive that ends without one of those two has no result, however many tools it used.
> A verdict of `verified: "unknown"` is not a pass. It means Reticle drove the app and could not tell what happened. Report it as unknown; never weaken a check to make it pass.
> Package names are scoped `@reticlehq/*`. Run every CLI command as `npx @reticlehq/server <command>`, for example `npx @reticlehq/server init`. `reticle` is a bin name that `@reticlehq/server` installs once it is on your PATH, NOT a package on npm: `npx reticle` fetches an unrelated package published by somebody else, so never run that. The complete tool surface is on the `/usage` page; `/agent-cheatsheet` is the one-screen version.

# reticle_network

> A filtered log of every request the app made, with bodies, status, timing, automatic credential redaction, and an honest note when evidence has expired.

<Warning>
  **reticle\_network is not advertised.** The default surface is the merged nine, so an agent does not see
  this name. Call **`reticle_observe { action: "network" }`** instead. Everything below describes what that call does;
  only the spelling changed. A call to the old name is answered with the new one, but an
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
</Warning>

`reticle_network` returns a filtered log of every request the app made, with request and response bodies, status, timing, and automatic credential redaction. Reach for it to answer "did that call actually fire, and what came back?", which is the thing a screenshot fundamentally cannot show.

## Example

```json theme={"dark"}
{ "limit": 3 }
```

Real response, with the second call's `headers` block trimmed off:

```json theme={"dark"}
{
  "calls": [
    {
      "method": "POST",
      "url": "http://localhost:8787/api/login",
      "status": 401,
      "statusText": "Unauthorized",
      "contentType": "application/json; charset=utf-8",
      "responseSize": 37,
      "requestBody": "{\"email\":\"admin@reticle.dev\",\"password\":\"[REDACTED]\"}",
      "responseBody": "{\"error\":\"invalid email or password\"}",
      "ms": 23
    },
    {
      "method": "POST",
      "url": "http://localhost:8787/api/login",
      "status": 200,
      "statusText": "OK",
      "contentType": "application/json; charset=utf-8",
      "responseSize": 67,
      "requestBody": "{\"email\":\"admin@reticle.dev\",\"password\":\"[REDACTED]\"}",
      "responseBody": "{\"token\":\"[REDACTED]\",\"user\":{\"email\":\"admin@reticle.dev\"}}",
      "ms": 4
    }
  ],
  "buffer": {
    "held": 535,
    "dropped": 10,
    "note": "event buffer evicted older events (age/size cap): a negative result here may be a false negative; the evidence may have expired. Grade sooner or widen the buffer."
  },
  "cost": { "bytes": 1133, "tokens": 284 }
}
```

Two attempts at the same login, the first rejected and the second accepted, with the server's own error text attached to the failure. That is the whole diagnosis in one read.

## Redaction is automatic

Look at `requestBody`. The password is `[REDACTED]` and so is the returned token, without anyone configuring anything. Reticle strips credential-shaped values before they leave the page, so neither your agent's context nor any transcript of it ever holds them.

The email is not redacted, because an email is not a credential. The line is drawn at secrets.

## The buffer note is the important bit

```json theme={"dark"}
"buffer": { "held": 535, "dropped": 10, "note": "…the evidence may have expired…" }
```

Reticle keeps a bounded event buffer. When it overflows, older events are evicted. And if you then ask "did a POST to `/api/orders` happen?", the honest answer is not "no". It is "not in what I still have."

Most tools would return an empty array and let you draw the wrong conclusion. This one tells you the result may be a false negative and suggests grading sooner or widening the buffer. Take that note seriously: a confident "no requests fired" built on evicted evidence is exactly the kind of false green Reticle exists to prevent.

## Filters

| Argument          | What it does                                                           |
| ----------------- | ---------------------------------------------------------------------- |
| `urlContains`     | Substring the URL must contain                                         |
| `method`          | `GET` · `POST` · `PUT` · `DELETE` · `PATCH`                            |
| `status`          | Exact status code, e.g. `500`                                          |
| `ok`              | `false` keeps only failures. The fastest way to "did anything break?"  |
| `limit`           | Most recent N. Older matches are dropped and counted                   |
| `actionId`        | Only requests attributed to one action: "what did that click request?" |
| `since` / `until` | Cursors from a prior act, to scope to a window between two actions     |

## Catching a double submit

The bug where one click fires two POSTs is invisible on screen and obvious here:

```json theme={"dark"}
{ "urlContains": "/api/orders", "method": "POST" }
```

If `calls` has two entries with near-identical timestamps, you have found it. Better still, assert it up front so the agent cannot rationalise it afterwards. `reticle_act_and_wait` accepts an exact count predicate, which turns "roughly one request" into a check that fails at two.

## A zero-match filter says what did fire

That exact call, on a fixture that never touches `/api/orders`, comes back diagnosable rather than blank:

```json theme={"dark"}
{
  "calls": [],
  "hint": {
    "totalInWindow": 1,
    "present": [{ "method": "POST", "url": "http://localhost:8787/api/login", "status": 200 }]
  },
  "buffer": { "held": 1, "dropped": 281, "note": "…the evidence may have expired…" },
  "cost": { "bytes": 328, "tokens": 82 }
}
```

`hint.present` is the difference between "your endpoint never fired" and "you filtered on the wrong URL". Read it before you go looking in the app.

## Checking that nothing fired

`{ "ok": false }` keeps only the calls that failed, so an empty result there is a genuinely useful answer: nothing broke. Provided the buffer note says nothing was dropped. Read both.

Desktop IPC (`ipc://`) has no status code, so its 200 and 500 are derived. Filter those on `ok` rather than `status`.

<Card title="Prove it, don't just look at it" icon="circle-check" href="/quickstart">
  `reticle_network` reads. It does not produce a verdict. Name the expected request in
  `reticle_act_and_wait` instead.
</Card>
