Overview
Syncline records a browser session and the backend trace behind it, then draws them against one clock. The interesting part is not either half — both exist already — but the seam between them, and how it is made reliable.
The problem
Session replay tools show you the browser. Tracing tools show you the backend. When someone reports that checkout was slow, you watch a video, guess a timestamp, and go looking in a second tool whose clock does not agree with the first one. Most of the work is correlation, by hand, under time pressure.
The mechanism
The browser SDK mints a W3C traceparent for every request it is allowed to touch, and writes that trace id into the rrweb stream itself as a custom event, at the frame the request fired. Your backend reads the header with ordinary OpenTelemetry instrumentation and continues the trace. The viewer then resolves player time to a trace id to a span tree.
The consequence worth understanding: the join is by identifier, not by time. Clock skew between a laptop and a server changes where a span is drawn; it can never change which request a span belongs to. Skew is a rendering concern, and it is measured and shown rather than hidden.
The pieces
| Component | What it does |
|---|---|
syncline-browser | Records with rrweb, patches fetch and XHR, uploads chunks |
apps/api | Authenticates, stores the raw body, enqueues. Parses nothing |
apps/worker | Validates, normalizes OTLP, writes rows |
apps/web | The recordings list and the viewer |
Where to go next
- Quickstart — running locally in about five minutes.
- Browser SDK — options, and the two rules the SDK will not break.
- Backend tracing — the two environment variables, and the CORS header everyone forgets.
- Privacy and masking — read this before recording anything real.