Skip to content
Console

Security & isolation

What is redacted before upload, how tenants are kept apart, how media links expire, and what an AI agent can and cannot reach.

You are about to record your users’ sessions — network requests with bodies, console output, clicks — and hand the recordings to a third party. The two questions that matter are what leaves the page and who can ever see it. This page answers both, and every claim on it is enforced in code, not policy.

The SDK keeps a capped, in-memory ring buffer and sends it only when report() is called — by your user, your code, or an auto-report trigger you enabled. There is no background trickle. data-mode="always" exists for the teams that want every session, but it is opt-in, never the default.

Redaction runs inside the browser, before anything is uploaded — so the sensitive value never crosses the wire at all, rather than being scrubbed server-side after we already received it:

  • Headers: Authorization, Cookie, Set-Cookie and friends are replaced with [redacted], and any header whose name contains secret or token gets the same treatment even if it is not on the exact list.
  • URLs: query parameters that carry credentials (token, key, secret, …) are redacted inside recorded URLs.
  • JSON bodies: fields like password, access_token and refresh_token are redacted inside request and response bodies — logging in while being recorded must not turn the recording into a credential.

The exact rules live in @espejo/core’s redaction module and run for every capture path — network, console and navigation alike.

Every recording belongs to a project, and every project to exactly one tenant (your account). The walls between tenants are structural:

  • Every query filters through the tenant relation. There is no code path that looks up a session by id alone: the tenant scope is part of the query itself, never a list of ids that could go stale.
  • A miss is indistinguishable from a non-existent recording. Asking for another tenant’s session returns the same 404 as asking for one that was never created — in the console API and in the MCP tools alike. No 403 ever confirms that an id exists.
  • Support access does not impersonate. The internal operations key is refused wherever access is granted on a person’s behalf — it cannot be used to walk into a tenant’s account.

Recording artifacts (events, DOM replay, video) are never served from guessable URLs. When a viewer — or an AI agent over MCP — asks for one, the API mints a link signed with HMAC that expires after 10 minutes. A leaked link goes stale before it goes far, and the signature covers the session, the artifact and the expiry, so none of them can be swapped.

The MCP server uses OAuth 2.1 with PKCE, and the scope of a connection is fixed once, at the moment you approve it — from the account you were signed into, never from anything the agent sends later. All MCP tools are read-only, and every one of them goes through the same tenant-scoped queries as the console. Revoking the connection cuts access on the next call.

Recordings are deleted automatically when they age past your project’s retention window or when the count/minutes caps evict the oldest — the sweep runs continuously on the server, and deleting a session cascades to every stored artifact. What the plan says is what the disk does.