# Read Query Logs

> The logs operation on whisper.agents: the kind argument, the time forms from and to accept, and every column a log row can carry.

*Source: https://www.whisper.security/docs/control-plane/operations/logs*

---
`logs` returns what the plane recorded. It needs `dns:logs:read`.

## logs

```whisper-call
CALL whisper.agents({op: 'logs', args: {agent: 'checkout-bot', from: '-1h', kind: 'dns'}})
```

### Arguments

| Argument | Contract |
|---|---|
| `agent` | The agent to read. Accepts an identifier or an address. |
| `kind` | `dns` for lookups, `conn` for connections, `alloc` for allocation events. |
| `from` | Start of the window. |
| `to` | End of the window. Omit it to read up to now. |
| `limit` | How many rows to return. |

`from` and `to` each accept three forms: epoch milliseconds, an RFC 3339 timestamp, or a relative offset such as `-1h`.

### Returns

One row per event. Which columns carry a value depends on `kind`.

| Column | Holds |
|---|---|
| `ts` | When the event happened. |
| `kind` | Which kind of event this row is. |
| `agent` | The agent it belongs to. |
| `qname` | The name that was looked up. |
| `qtype` | The record type asked for. |
| `rcode` | The DNS response code returned. |
| `decision` | What policy did: allowed, blocked, or no opinion. |
| `reason` | Why that decision was reached. |
| `answer` | What the lookup resolved to. |
| `source` | Which channel the query arrived on. |
| `client_src` | The address the query came from. |
| `latency_ms` | How long the lookup took. |
| `peer` | The other end of a connection. |
| `bytes_up`, `bytes_down` | Bytes out and in on that connection. |
| `packets_up`, `packets_down` | Packets out and in. |
| `duration_ms` | How long the connection lasted. |
| `parent_agent` | The identity this one egressed behind. |
| `nested` | Whether the event came from a nested identity. |

## Reading blocked lookups

Filter on `decision` once the rows are in hand. On the command line the same read is one pipe:

```bash
whisper logs --agent checkout-bot --from -1h --kind dns | jq '.[] | select(.decision == "blocked")'
```

## Errors

| Code | Status | Meaning |
|---|---|---|
| `BAD_ARGS` | 400 | `kind` is not one of the three values, or a time argument is in none of the accepted forms. |
| `NOT_FOUND` | 404 | No such agent under this account. |
| `FORBIDDEN_SCOPE` | 403 | The key does not carry `dns:logs:read`. |
| `RESULT_TOO_LARGE` | 413 | The window returns more than one answer can hold. Narrow `from` and `to`, or lower `limit`. |
| `READ_BUSY` | 503 | The store could not answer the read. Retry after the interval in `retry_after`. |

What is kept, and for how long, follows the `retention` setting described in [Set policy](/docs/control-plane/operations/policy).
