Get started

// Product

What gets captured

The buffers, the timeline, the screenshot, the masking — exactly what a report contains, and what can never leave the page.

Every report carries the evidence an engineer needs to reproduce the problem — collected in the browser, bounded by construction, and masked before it ever enters memory. This page is the complete inventory.

The four buffers

Capture is a set of small, fixed-size rolling buffers fed by passive listeners. Memory use doesn't grow with session length, and nothing leaves the page until a member submits a report.

BufferSizeWhat's in it
User actionslast 50Clicks, typing (masked), form submits, key presses, navigations. Typing is debounced (250 ms), so one entry per burst.
Consolelast 50console.error and console.warn output, uncaught exceptions, unhandled promise rejections.
Networklast 50URL, method, status, duration, and whether it was fetch or XHR. Rebase's own API traffic is excluded.
Device snapshotonceTaken at submit time only — never continuously.

The causal timeline

Actions, errors and failed requests are merged into one time-ordered story: Clicked "Save" → POST /orders returned 500 → TypeError follows. That's what your team — and the AI — reads, instead of four separate log dumps.

The timeline also flags what logs can't show:

  • Dead clicks — a click that produced no navigation, no request and no visible change within a second is flagged. "The button does nothing" arrives with proof.
  • Rage clicks — repeated clicks on the same target in a short window are collapsed and labelled.
  • Reload survival — the tail of the session (last 15 actions + last 10 errors) persists across page reloads, with a page-load boundary in the timeline. If the crash caused the reload, the evidence isn't lost.

Network capture, in detail

Failed requests are classified by failure kind, not just status code: HTTP error, timeout, aborted, offline, or blocked (ad-blocker/CORS).

Rebase is GraphQL-aware: requests to GraphQL endpoints are named by operation (query or mutation + name), and responses that return HTTP 200 but carry an errors array are caught as failures — the classic invisible GraphQL bug.

What never travels:

  • Response bodies — never. There is no configuration that captures them.
  • Request bodies — opt-in only, via data-capture-bodies="true" on the script tag.
  • Captured URLs lose their query strings and credentials.
  • Console and network entries are swept for known secret shapes before submission — bearer tokens, sk_ and pk_ keys, GitHub/Slack/Google/npm tokens, JWTs, AWS key ids, password= and token= pairs — and the API sweeps again server-side.

The element, in depth

For the clicked element, Rebase records: tag, id, classes, attributes, ARIA role and label, visible text (truncated to 500 characters, masked), a serialized snippet of its markup (truncated to 4 KB, masked), its size and position, computed styles (display, position, color, background, font, z-index, overflow, opacity, transform, visibility), and an ancestor breadcrumb up to 8 levels deep.

Where possible, the element is also attributed to a framework component and a file:line — see Source attribution.

The screenshot

A viewport-only PNG is taken at the moment of the click — no browser extension, no screen-share permission; it's just the script tag. The widget's own UI is filtered out of the shot, and masked fields are masked in the screenshot too.

Server-side, screenshots are re-encoded (stripping anything that isn't pixels), capped at 5 MB, served via short-lived signed URLs (10 minutes), and deleted after 90 days.

The device snapshot

Captured once, at submit: browser and OS, viewport, screen size and pixel density, language, timezone, connection quality, device memory and cores, page URL and referrer.

Pins that survive change

Reports are pinned to the element they're about, not a coordinate. Each pin stores four selector strategies (test id, element id, CSS path, structural path) plus a fractional offset inside the element and an absolute-coordinate fallback. On resize, scroll or layout shift the pin follows its element; if the DOM has changed so much that no selector matches, the pin degrades to a ghost marker at the original spot instead of lying.

Route groups

Pins match by route group, with query and hash ignored. Obviously-dynamic path segments — pure numbers, UUIDs, long hex ids, ULIDs — are collapsed, so a ticket filed on /orders/123 pins /orders/456 too. Static segments (slugs, words) are never collapsed; the grouping is deliberately conservative and runs server-side. The ticket's stored pathname stays the exact page it was filed on, so deep links are unaffected.

Masking & redaction

Everything typed is masked by default. Input values are replaced with characters before they enter the capture buffer — they don't exist anywhere to leak. You opt fields out of masking, not into it.

Some fields can never be captured, with no configuration that overrides it: password inputs, credit-card fields (autocomplete containing cc-), and current-password, new-password and one-time-code autocomplete fields.

Your controls, from broadest to narrowest:

ControlWhereEffect
data-mask=".secret, [data-acct]"script tagMasks everything matching the selector list.
data-rebase-maskany elementMasks that element's value and text.
data-rebase-blockany elementDrops the entire subtree from capture.
data-rebase-unmaskany elementRe-enables a safe field inside a masked subtree.

Rebase also honors the masking you've already set up for other tools: Hotjar (data-hj-suppress), FullStory (fs-mask / fs-unmask), Sentry (data-sentry-block) and PostHog markers are respected automatically.

Defense in depth: captured payloads are scrubbed a second time on ingest, and AI triage only ever sees a size-budgeted, scrubbed slice — never the raw payload.

Who sees capture data

Capture context (console, network, steps, environment) is visible to owner and team roles only. Commenters can file and discuss; viewers are read-only. Capture runs on every visitor session of a set-up site, but filing a report requires sign-in.