Skip to content

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.

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

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.

Arrays of 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) count from it.

HAR 1.2 files, { "log": { "entries": [...] } }, with these additions to each entry:

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.

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

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.