tinyreplay.
Architecture

Event Flow

A session event's journey from the browser to SQLite, step by step.

One direction, five parts. Events are captured in the browser, batched, posted to the ingest API, validated, and written to SQLite. The dashboard reads it back.

01Browser SDKrrweb capture + masking
02Batch Transportbuffer · flush · beacon
03Ingest APIPOST /api/ingest
04SQLitesessions · events (WAL)
05Replay Dashboardscrub · inspect

1 · Capture (Browser SDK)

rrweb records the DOM and interactions; the SDK layers on console, network metadata, error, and route custom events. Masking is applied here, at capture time. Events land in an in-memory buffer.

2 · Batch (Transport)

A timer flushes the buffer every flushInterval (5 s), up to 500 events per request. Batches are numbered with a monotonic seq; seq: 0 carries session metadata. On page unload, the buffer is drained via sendBeacon in small chunks.

3 · Ingest (API)

POST /api/ingest runs each request through an ordered gauntlet:

Content-Type must be application/json, or 400.

Rate limit - over RATE_LIMIT_PER_MIN per IP returns 429.

Size - Content-Length and the real byte length are both checked against MAX_PAYLOAD_BYTES; over it is 413.

Validation - a zod schema checks the shape: projectId (1–64), sessionId (uuid), seq (≥0), and events (1–500). seq: 0 must also carry startedAt, url, and viewport. Failures return 400.

Token - when INGEST_TOKEN is set, the body token or a Bearer header must match (constant-time), or 401.

Persist - the batch is written and the session row upserted. 200.

4 · Store (SQLite)

The raw events are stored as a JSON batch in the events table, keyed by session_id and seq. The session row is updated with derived counters - event_count, page_count (from route events), and error_count (from error events) - computed server-side from the batch. See the SQLite schema.

5 · Replay (Dashboard)

The dashboard loads a session's batches in seq order, reassembles the event stream, and drives the replay engine.

No outbound calls

The ingest route is annotated NO TELEMETRY - it only writes to local SQLite and never makes a request of its own.