@clue-ai/browser-sdk
v0.1.1
Published
Clue Browser SDK for raw telemetry capture and transport
Readme
@clue-ai/browser-sdk
Clue browser ingest SDK.
Minimal integration
After pnpm add @clue-ai/browser-sdk, the intended integration is:
import { ClueInit } from "@clue-ai/browser-sdk";
ClueInit({
endpoint: process.env.NEXT_PUBLIC_CLUE_INGEST_ENDPOINT!,
projectKey: "...",
environment: "production",
});projectKey is the public ingest key. The API resolves authoritative
tenantId, projectId, and environmentId from that key. endpoint must be
injected from the host app and passed to ClueInit.
Responsibility boundary
- SDK captures browser signals and applies local minimization where the signal is obviously high-risk.
- API ingest privacy gate is authoritative for final allow/deny/unmask policy.
- Worker-side sanitize is defense-in-depth only.
- SDK sends
projectKey,environment,schemaVersion,sdkType,sdkVersion, stable keys, and minimized capture payloads. - SDK does not authoritatively stamp
tenant_id,project_id,environment_id, or final archive-safe request/response payloads.
The browser SDK does not own final archive-safe allowlist decisions.
Default privacy/minimization behavior
The browser SDK avoids shipping obvious high-risk plaintext and keeps the MVP default event surface intentionally narrow.
input_change.value-> local value metadata onlyselection_text-> metadata only- raw selector/path -> never serialized; stable key only
Current MVP defaults:
- successful network emits one
request_finished - failed network emits one
request_failed - standard action capture emits
element_clicked,form_submitted,input_committed,toggle_changed,selection_committed,file_selected, anddrag_drop_completed - standard support capture emits only frustration signals
(
dead_click_detected,error_click_detected,rage_click_detected) - normal batching flushes when the queued payload reaches the 48KB send threshold, when no new event is added for 3 minutes, or when the page is leaving
- event type does not change the delivery rule in MVP
Stable key precedence:
data-testiddata-qa- safe
name - safe
aria-label - structural fallback
data-clue-id and data-clue-key are not part of the MVP stable-key
contract. Captured stable keys are best-effort evidence, not authoritative
business meaning.
Sampling and cost guards
sampling.sessionSampleRatecontrols whether a session emits normal capture- oversized events degrade in stages before final drop:
- payload body removed
- metadata/schema only
- shell only
- oversized batches stay below the configured payload hard max
Flush reliability
The SDK flushes on:
visibilitychange->hiddenpagehidebeforeunload
The page-leave flush is best effort. The SDK does not persist unsent events in
localStorage or IndexedDB in MVP.
Beacon contract
- unload / hidden beacon sends only the request body
- ingest must be able to resolve
projectKeyandenvironmentfrom the body - custom auth headers cannot be assumed on the beacon path
Degraded event contract
Downstream ingest must accept these as normal input:
- no raw payload body
- metadata + schema only
- shell-only event
Privacy and PII handling
The SDK applies a layered privacy model:
1. Hard-deny: PII / secrets are stripped before transport
The SDK strips a built-in set of property keys before the event leaves
the browser. These keys cannot be unmasked through allowedValuePaths —
even an explicit body.email allowlist does not bypass the hard-deny.
Hard-deny categories (case- and separator-insensitive — userEmail,
user-email, USER_EMAIL, email_address all match):
- Auth credentials:
authorization,cookie,set-cookie,password,passwd,secret,token,access_token,refresh_token,session,session_token,api_key,apikey,private_key - PII categories (Phase 4 P0):
email,phone,credit_card,ssn
This list is a strict superset of the server-side ingest hard-deny
(@clue/shared INGEST_HARD_DENY_KEYS), enforced by CI parity tests in
all three SDKs.
2. Default-masked: arbitrary values are masked unless allowlisted
Properties that pass the hard-deny gate are still masked by default. The SDK emits a fingerprint or shape-only digest, not the raw value.
To capture a specific field in plaintext, two things must both be true:
- The field path appears in the SDK call's
allowedValuePaths(caller opt-in at SDK init time). - The same property key appears in the project's server-side
observationAnalysisPropertyAllowlist(ProjectEnvironment.observationAnalysisPropertyAllowlist).
If condition 1 holds but condition 2 does not, the value is stored in
the raw archive only — facts_json.analysisProperties.projected will
be empty and the field will not appear in Clue's analysis UI.
3. Operational consequence: configure server allowlist before launch
Customers who launch without setting observationAnalysisPropertyAllowlist
on their ProjectEnvironment will see custom event names land in
observation_type but no projected properties in dashboards. The
canonical event still includes raw_only_keys listing which keys were
seen but masked. Configure the server allowlist via the Clue admin UI
or ProjectEnvironment update API after taking a privacy review of
which property names you want to project for analytics.
