Skip to content

feat(analytics): add restricted audience measurement - #226

Open
francoischalifour wants to merge 4 commits into
mainfrom
fc/audience-measurement
Open

francoischalifour wants to merge 4 commits into
mainfrom
fc/audience-measurement

Conversation

@francoischalifour

Copy link
Copy Markdown
Member

What

Add trackingMode: 'audience' for aggregate usage measurement without persistent client identifiers.

Usage

altertable.init('YOUR_API_KEY', {
  trackingMode: 'audience',
});

altertable.track('Install Copied', { package: 'react' });

Automatic page views and custom events carry processing_mode: 'audience'. React hooks use the shared client without API changes.

Audience mode:

  • Omits user, anonymous, session, and device IDs from outgoing events.
  • Keeps identity, consent, and buffered events in memory, overriding persistence options.
  • Ignores identify(), alias(), and updateTraits().
  • Strips URL credentials, queries, fragments, referrer paths, and viewport data.
  • Omits browser credentials and the HTTP referrer across normal, retry, and exit delivery.

trackingConsent still controls delivery. Pending or dismissed events wait in memory; denied events are discarded. Unsent events are lost on reload.

Search usage can be measured explicitly with events such as Search Performed and reviewed properties such as category and result_count. Search terms require review before collection because they can contain personal data.

Why

Provide a collection mode designed for audience measurement with a simple setup and enforceable event contract. Cookie usage alone does not describe the collection or processing policy.

Design decisions

  • Configure once; classify every event. The client owns the mode and applies its marker automatically, including after caller transforms.
  • Preserve familiar APIs. Applications keep using track(), page(), and transformEvent. Transforms receive TrackPayload; audience delivery uses AudienceTrackPayload.
  • Keep mode changes explicit. configure() excludes trackingMode. Calling init() changes modes and discards buffered events; callers can flush first.
  • Reject adapters. Source SDKs control their own collection, persistence, and identity, so their events cannot satisfy this mode's guarantees.
  • Strip queries by default. This also removes automatic UTM attribution. Campaign configuration is outside this change.
  • Keep transport generic. The client selects request options; the requester forwards them. Audience exit delivery uses keepalive fetch because beacons cannot enforce those options.

Custom event names, properties, and URL paths still require application review. transformEvent can remove sensitive values before transmission.

Remaining backend work

This PR implements the SDK portion. Backend support must:

  1. Recognize each event's processing_mode before enrichment, logging, or queueing. Handle mixed batches per event, reject unknown modes, and reject identity fields on audience events.
  2. Enforce an approved event/property schema. Prevent raw IP addresses, forwarding headers, raw user-agent data, and sensitive payloads from entering retained events, queues, or request/error logs.
  3. Preserve the audience classification downstream, enforce retention and aggregate outputs, and prevent profile creation or joins to identified customer data.
  4. Define short-lived visitor/session semantics if audience metrics or linked funnels require them. The SDK supplies no linkage identifiers.
  5. Verify stored events and logs for malformed requests, identity injection, mixed batches, and sensitive properties. Publish the processing guarantees in the official docs.

Checklist

  • Tests added or updated
  • CHANGELOG.md updated
  • Breaking-change review: product mode remains the default; configure() now explicitly excludes initialization-only trackingMode

Use method-call notation consistently in adapter and dropped-call diagnostics. Format the queue limit for readability.
Add an initialization-only audience tracking mode while preserving the track() and page() APIs. Each event requests audience processing and omits identity fields after caller transforms.

Keep identity, consent, and event buffers in memory; reduce automatic URL context; and omit credentials and referrers across retries and exit delivery. Reject analytics adapters and invalid mode changes before replacing the active client.

Keep analytics policy in the client and pass request options to the transport. Cover behavior through public APIs and validate the exported configuration and payload types from packed packages.
Document audience collection, persistence, consent, transforms, and initialization-only mode changes. Distinguish transform inputs from the restricted audience wire payload and explain the adapter restriction.
Document that Audience mode removes search query parameters from automatic page tracking and show how to track search usage and result counts explicitly. Note that collecting search terms requires reviewing them for personal data.

@albert20260301 albert20260301 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking SDK findings. Audience mode applies the wire contract after transforms, omits identity fields and persistent storage, rejects adapters and mode changes through configure(), and preserves the transport restrictions through retry and exit delivery. Product-mode defaults remain covered.

Local build/types/lint/Knip/format, all 716 unit tests, bundle budgets and packed React 16/current consumers pass. The packed-consumer check required an isolated temporary directory because of the host's shared /tmp TypeScript configuration. Required CI, including integration and both native compatibility lanes, is green.

This verifies the SDK-side opt-in contract only. The backend processing/enrichment/logging work listed in the PR remains required for end-to-end audience-processing guarantees; custom event properties and URL paths remain application-owned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants