Product analytics should link an anonymous visitor to an authenticated user only after the product has explicit evidence that both identifiers belong to the same actor. The link should be scoped, auditable, reversible, and applied when queries run. Historical events should retain the identifier originally sent.
This design preserves pre-signup journeys without pretending that every browser, session, account, or email belongs to the same person. It also lets a team correct a bad identity link without rewriting an event archive.
The practical sequence is:
- Assign a persistent, opaque first-party identifier before authentication.
- Use the product’s stable internal user ID after authentication.
- Create an explicit anonymous-to-identified link at a trusted transition.
- Resolve the canonical actor when calculating users, funnels, retention, or experiments.
- Preserve the original identifiers for audit, deletion, and debugging.
- Return
unknownorambiguouswhen the available evidence cannot support a stronger identity claim.
What is anonymous-to-identified identity resolution?
Anonymous-to-identified identity resolution connects activity collected before login with the stable product actor created or recovered during authentication.
Suppose a visitor receives anon_7f2a when opening a product. They view documentation, start setup, and then sign in as internal user usr_1842. The product can record this directed relationship:
anon_7f2a -> usr_1842
The anonymous identifier remains on the events that originally contained it. Queries may resolve both raw identifiers to usr_1842 when counting unique actors or reconstructing the journey.
This is different from replacing anon_7f2a in every stored row. A link is an interpretation supported by provenance. Rewriting makes the interpretation look like an original fact.
The distinction becomes important when the link is wrong, the environment changes, a shared browser switches accounts, or a deletion request targets one raw identifier.
Why does identity quality change product metrics?
Unique-user metrics are identity calculations. Funnels, retention, lifecycle analysis, frequency, and experiments also depend on knowing whether two events belong to the same actor.
| Identity failure | Data symptom | Likely product mistake |
|---|---|---|
| New random ID for every event | User counts inflate sharply | Engagement appears broader than it is |
| Session ID used as the user ID | Returning users appear new | Retention looks lower |
| One placeholder ID shared by everyone | Users collapse into one actor | Funnels and frequency become meaningless |
| Anonymous and authenticated IDs never linked | One journey becomes two users | Pre-signup conversion is understated |
| Two real users linked together | Separate histories merge | Segments, retention, and experiments are corrupted |
| Link crosses environments | Test and production identities mix | Production decisions use non-production evidence |
Google’s current User-ID guidance similarly requires a unique, persistent identifier for each user and warns that assigning the same ID to multiple people skews reporting. It also advises against identifiers containing information that lets a third party determine the user’s identity. Google Analytics User-ID documentation
A chart cannot repair identity after aggregation. The identity contract must exist below the metric.
What identifier should be used before sign-in?
Use an opaque, first-party identifier that persists for the intended anonymous measurement period. It should contain no email address, phone number, account name, route, campaign value, or device fingerprint.
For a browser product, the identifier may be stored in an origin-scoped first-party storage mechanism. The current WHATWG HTML standard states that each site has a separate storage area and that localStorage spans beyond the current session. It also notes that storage can fail or be unavailable when browser policy prevents persistence. WHATWG HTML Living Standard
That gives the identifier useful but limited meaning:
- It represents one observable browser storage context.
- It does not prove that one human owns the device.
- It does not automatically work across browsers or devices.
- It can disappear when storage is cleared or restricted.
- It must not be described as permanent.
- It must not be reconstructed through fingerprinting when unavailable.
A new identifier after storage deletion is a new observable actor until another trusted event links it. Quietly guessing that two anonymous IDs belong to the same person upgrades probability into identity.
For apps or server-side flows, the persistence mechanism will differ. The same rule remains: the identifier must have a documented scope and lifecycle.
What identifier should be used after authentication?
Use the product’s stable internal actor ID.
Good identifiers are:
- Unique within the declared product scope.
- Stable across sessions.
- Independent of editable profile fields.
- Opaque outside the product.
- Available consistently to the code that emits events.
- Different between production and development when environments are isolated.
An internal immutable user UUID usually fits better than an email address. Emails can change, be reassigned, reveal personal information, or represent a shared mailbox. A hashed email is still derived from an email and should not be treated as automatically anonymous or safe.
The GDPR explicitly includes online identifiers among the information that may be associated with a person. An opaque analytics ID should therefore be treated as potentially personal data, with an appropriate purpose, notice, retention policy, access boundary, and deletion process. This article is an engineering design guide, not jurisdiction-specific legal advice. Regulation (EU) 2016/679
Do not use "anonymous", "guest", zero, an empty string, or another shared placeholder as a real actor ID. Shared placeholders merge unrelated activity.
When should the product create the identity link?
Create the link at a trusted product transition where the application possesses both identifiers.
Typical moments include:
- Successful account creation.
- Successful login.
- Completion of a verified magic-link flow.
- A server-confirmed session exchange.
- An authenticated migration from an old internal ID to a new stable ID.
The direction should move from temporary or superseded identity toward the stable actor:
temporary_or_old_id -> stable_actor_id
Do not create a link merely because two events share an IP address, user-agent string, campaign parameter, or similar timestamp. Those values can support diagnostics, but they do not prove identity.
The server is usually the strongest place to authorize the link because it can verify the authenticated account and receive the current anonymous identifier from the trusted product flow. The browser may carry the anonymous value, but it should not have permission to merge arbitrary users.
The link operation should be idempotent. Retrying the same confirmed relationship must not create several competing links.
Why should historical events keep their original identifier?
The identifier sent with an event is part of the original evidence. Replacing it destroys information about what the product knew when the event occurred.
Keeping raw identity provides four advantages.
First, a mistaken link can be revoked. Future queries can separate the actors again without reconstructing old rows from backups.
Second, reviewers can distinguish received evidence from later interpretation. An event containing anon_7f2a remains visibly anonymous at capture time even if later analysis resolves it to usr_1842.
Third, privacy operations can target an exact raw identifier. Expanding every request through the current identity graph may delete more data than requested, especially after an incorrect merge.
Fourth, identity rules can improve without running destructive backfills. The query layer can apply the current reviewed graph while the immutable event layer remains stable.
The cost is that queries must resolve identity consistently. A system that resolves links in funnels but not retention will produce contradictory user counts. Identity resolution belongs in a shared storage or query boundary, not in individual dashboard formulas.
How should query-time identity resolution work?
Query-time resolution follows active directed links until it reaches the canonical actor.
Given:
anon_browser_1 -> temporary_account_9
temporary_account_9 -> user_42
all three raw identifiers can resolve to user_42. The stored events retain their respective raw values.
A safe resolver needs several constraints:
| Constraint | Why it matters |
|---|---|
| Project scope | Prevents identity from leaking between tenants |
| Environment scope | Keeps development and production separate |
| One active destination per source | Prevents contradictory canonical actors |
| Cycle rejection | Prevents A -> B -> A from having no truthful endpoint |
| Bounded output | Prevents person views from returning unlimited identifiers or links |
| Audit history | Shows who created or revoked the relationship |
| Shared query implementation | Keeps user counts consistent across analysis types |
The resolved actor should be used by unique-actor trends, funnels, retention, lifecycle, stickiness, experiment outcomes, and actor summaries. Raw-event inspection should continue exposing the original identifier.
Revoking a link changes subsequent query results. That is expected: the interpretation has changed. The audit should preserve the previous link and the operator responsible for the correction.
What happens on logout or account switching?
Logout ends the use of the authenticated actor for subsequent anonymous activity. It does not mean that every later browser event should remain attached to the previous account.
A safe client transition is:
- Send authenticated events with the stable actor while the authenticated session is valid.
- Clear the authenticated identity when logout succeeds.
- Rotate or restore an anonymous browser identifier according to the product’s documented policy.
- Do not automatically link post-logout anonymous activity back to the former user.
- Create a new explicit link only after another trusted login.
Shared devices make this boundary essential. If Alice signs out and Bob uses the same browser, continuing to emit Alice’s stable ID merges two people.
Whether to rotate the anonymous identifier at logout is a product and privacy decision. Rotation reduces accidental account association but creates a new anonymous journey. Reusing the pre-login identifier preserves continuity but can join activity across different users on a shared device. The chosen behavior should be explicit and tested.
How should ambiguous or unknown identity be reported?
Return uncertainty as data.
An unlinked identifier is not necessarily anonymous. It could be a stable authenticated ID, an anonymous browser ID, an imported legacy ID, or incorrect instrumentation. Inferring its class from a prefix such as anon_ is convenient but weak unless the server owns and validates that provenance.
Useful states include:
linked: active server-owned links or multiple observed raw IDs establish a canonical population.unknown: no supported evidence establishes a more specific identity state.ambiguous: the graph or imported data contains a contradiction that prevents a reliable conclusion.
A product interface should not relabel unknown as “anonymous user.” That would claim knowledge the system does not have.
Queries should also expose identity coverage: the share of relevant evidence carrying a usable actor identifier. Coverage does not prove that every identifier is correct, but low coverage is a direct blocker for unique-user analysis.
How should identity links interact with deletion?
Identity resolution and deletion should not silently use the same expansion rule.
Analytics queries commonly need canonical resolution: several raw IDs may represent one analytical actor. An administrative deletion request may identify only one exact raw ID. Automatically expanding that request through every current and historical link increases the deletion scope.
A safer contract states which operation is occurring:
| Operation | Recommended identity behavior |
|---|---|
| Unique-user query | Resolve active links |
| Funnel or retention query | Resolve active links consistently |
| Raw-event debugging | Show original identifier |
| Link correction | Revoke or replace reviewed link |
| Exact raw-ID deletion | Delete evidence for that raw identifier only |
| Full person erasure | Use a separate, explicitly authorized canonical expansion workflow |
The product must document which deletion semantics it supports. A label such as identity_scope: exact_raw_distinct_id makes the boundary reviewable.
What does Poolstatis implement today?
As of August 21, 2026, current Poolstatis Core source implements explicit actor links with these properties:
- Directed
source_distinct_id -> target_distinct_idlinks. - Isolation by project and environment.
- Active-source conflict detection.
- Cycle rejection.
- Revocation instead of destructive removal.
- Append-only creation and revocation audit.
- Query-time canonical resolution without rewriting events.
- Resolved identity in actor-based analytical queries.
- Bounded canonical people and activity reads.
linked,unknown, andambiguousidentity states.- Exact raw-identifier event deletion without automatic canonical expansion.
- MCP tools for creating, listing, and revoking links, plus bounded actor and person reads.
The implementation and limitations are inspectable in the current Poolstatis data-model documentation and identity service.
This review verifies the source tree at that commit. It does not claim that every capability has been independently exercised against the hosted Cloud deployment.
What should an identity implementation checklist include?
Identifier design
- Authenticated actors use stable internal IDs.
- Anonymous IDs are opaque and first-party.
- No shared placeholder represents multiple users.
- Emails, phone numbers, raw URLs, and fingerprints are not actor IDs.
- Browser-storage limitations are documented.
- Project and environment scopes are explicit.
Link creation
- The application possesses both identifiers at a trusted transition.
- The link moves toward the stable actor.
- The server authorizes the relationship.
- Repeated requests are idempotent.
- Conflicting active destinations are rejected.
- Cycles are rejected.
- Creation records the responsible operator or credential.
Query correctness
- Raw events retain their original IDs.
- Unique users, funnels, retention, lifecycle, and experiments share one resolver.
- Identity coverage travels with analytical results.
- Unknown identity remains
unknown. - Ambiguity blocks confident person-level conclusions.
- Link changes are visible in the audit trail.
Lifecycle and privacy
- Logout behavior is defined and tested.
- Shared-device account switching is tested.
- Links can be revoked without rewriting events.
- Retention applies to identity and audit data deliberately.
- Exact raw-ID deletion and full-person erasure are separate contracts.
- Public notices accurately describe identifier use.
Identity resolution is not a cosmetic people-page feature. It determines the population behind nearly every actor-based product metric. Preserve raw facts, require explicit links, resolve them consistently, and make uncertainty visible.