CloudClawer/CloudClawerDocs
BlogSign InSign In
FeaturesRecordings
Features

Browser Recordings

rrweb session replay for any browser an agent drives via Playwright. Capture the DOM, mouse, network, and console — then scrub the replay in your dashboard.

cloudclawer.com/recordings/1747000000-3a8f9c
0:00 — page loads, rrweb takes a full snapshot
1 events
1/8 · 1 rrweb events

Overview

Recordings are browser sessions, not agent tool-call traces. Whenever your agent uses Playwright (for E2E tests, scraping, or debugging a frontend), wrap the page with CloudClawer's recording helper and you'll get a frame-accurate replay backed by rrweb.

The recording captures the DOM mutations, mouse moves, input events, scroll positions, and network responses that rrweb supports. Replay it in the Recordings dashboardexactly as if you were watching the agent's browser live.

Capturing a recording

Install the hooks package (bundled with the cloudclawer CLI) and import the recording helper into your Playwright script:

import { chromium } from 'playwright';
import { startRecording } from 'cloudclawer-hooks/record.mjs';

const browser = await chromium.launch();
const page    = await browser.newPage();

const rec = await startRecording(page, { title: 'Login flow E2E' });

await page.goto('http://localhost:3000');
await page.getByRole('button', { name: 'Sign in' }).click();
// … exercise the feature …

const { filePath, eventCount, recordingId } = await rec.stop();
console.log(`Recorded ${eventCount} events → ${filePath}`);

await browser.close();

startRecording injects the rrweb client into the page from cdn.jsdelivr.net (pin version rrweb@2.0.0-alpha.4 to match the replay player), re-injects after navigation, and buffers events in the page context until you call stop(). The local file lands at ~/.cloudclawer-recordings/<id>.json.

The recorder defaults inlineImages: true and inlineFonts: true so recordings of localhoststill play back on other devices that can't reach your machine. Pass recordCanvas: trueif you're recording a WebGL canvas — it requires preserveDrawingBuffer: true on the WebGL context.

Uploading

After rec.stop() writes the JSON to disk, upload it with the bundled CLI:

cloudclawer-hooks record-upload ~/.cloudclawer-recordings/1747000000-3a8f9c.json

The CLI requests a presigned S3 URL from CloudClawer, streams the file directly to S3, and returns both the recording ID and the dashboard play URL. The recording is then listed on cloudclawer.com/recordings immediately.

Watching the replay

Open Recordings for a list of every upload, then click any item to launch the rrweb player. The player gives you:

  • Timeline scrubber — drag to any moment in the session.
  • Speed control — 0.5×, 1×, 2×, or 4× playback.
  • Skip inactive periods — auto-fast-forwards quiet stretches.
  • Full DOM inspection — open devtools on the replay iframe; the recorded DOM is real.

Storage & limits

Recordings are stored in S3 under your account namespace at users/{username}/recordings/{recordingId}.json. Hard limits:

  • 500 recordings per account. Oldest are evicted as you upload new ones.
  • 90-day TTL. Recordings older than 90 days are deleted by S3 lifecycle policy.
  • List, fetch, and delete endpoints accept your CloudClawer API key as X-API-Key.
GET    /u/dkr/recordings              # list
GET    /u/dkr/recordings/{id}         # fetch metadata + presigned download URL
DELETE /u/dkr/recordings/{id}         # delete now (also removes the S3 object)

Privacy & what's captured

rrweb captures only the page context it was injected into — it does not see your clipboard, other browser tabs, or anything outside the Playwright-controlled page. Specifically:

  • Captured: DOM snapshots, attribute and text mutations, input values, mouse moves, scroll, viewport size, network metadata (URLs + timing) when enabled.
  • Not captured by default: canvas pixel data (opt in with recordCanvas), cross-origin iframe contents, response bodies.
Heads up: Form input values are captured by default — including password fields. Mask sensitive inputs by adding the data-rr-block attribute, or call rrweb with maskInputOptions in your own integration.
© NeuralAccel 2026