# Recording format

Each recording is a folder of the project's data files. Read them over MCP with `readGadgetFiles`.
The files can be large: read one at a time.

```text
data/recordings/<id>/
  recording.json     what the recording is (written last)
  rrweb/NNNN.json    the page, as rrweb events, in order
  network/NNNN.json  the page's requests, as HAR 1.2
  assets/NNNN.json   the page's images, fonts and stylesheets
```

`NNNN` counts up from `0000`. To get all of one kind, concatenate its files' arrays in order (for
`network/`, each file's `log.entries`).

## recording.json

```ts
interface Recording {
  id: string;
  title: string;
  url: string;               // the page the recording started on ("" if unknown)
  startedAt: number;         // milliseconds since the epoch (UTC)
  endedAt: number;
  eventCount: number;        // rrweb events, all rrweb/ files together
  rrwebShardCount: number;   // how many rrweb/ files
  networkEntryCount: number; // requests, all network/ files together
  networkShardCount: number;
  assetCount: number;
  assetShardCount: number;
  createdAt: number;         // when it was saved
  source: string;            // the extension and its version
}
```

`recording.json` is written after all the other files, so if it is there, the recording is complete.

## rrweb/NNNN.json

Arrays of [rrweb](https://github.com/rrweb-io/rrweb) events. Every event has a `timestamp` in
milliseconds since the epoch.

| `type` | Event |
|---|---|
| `4` Meta | The page's address (`data.href`). A new `href` means a new page. |
| `2` FullSnapshot | The whole page as a tree of nodes (`data.node`), each with an `id`. |
| `3` IncrementalSnapshot | A change after it. `data.source`: `0` DOM change, `2` mouse, `3` scroll, `5` input. |

**Time zero** of a recording is the first event's `timestamp`. Requests and pins (a pin's `atMs`,
[MCP server](/docs/mcp/#runcode-and-the-studio-methods)) count from it.

## network/NNNN.json

[HAR 1.2](http://www.softwareishard.com/blog/har-12-spec/) files, `{ "log": { "entries": [...] } }`,
with these additions to each entry:

```ts
interface HarEntryExtras {
  _atMs: number | null;    // milliseconds from time zero; null before the first rrweb event
  _resourceType: string;   // "XHR", "Fetch", "Document", "Script", "Image", …
  _error?: string;         // set for a failed request
}
// On request.postData and response.content, a body over the size limit has no text,
// and instead: _truncated: true
```

- `request.postData.text` is what the page sent. `response.content.text` is the body, for API calls,
  documents and event streams whose body is text.
- A body is at most 256 KiB, and all bodies in a recording at most 8 MiB; past that, `_truncated`.
- What's redacted: [Recording](/docs/recording/#what-a-recording-keeps).

To find the requests around a pin, compare `_atMs` with the pin's `atMs`.

## assets/NNNN.json

```ts
type Asset =
  | { url: string; kind: "file"; type: string; data: string } // data: base64
  | { url: string; kind: "css"; css: string };
```

The page's look as recorded; replays use these copies. A file is kept if it's at most 4 MB and the
recording's assets stay under 60 MB; otherwise only its address is in the rrweb events.