Identity Explorer
A single person arrives at your product as many fragments: an anonymous device id minted before they ever sign in, the user id you assign when they do, a Stripe or Apple customer the moment they pay. Crossdeck’s job is to collapse those fragments into one canonical customer so you count one person once. The Identity Explorer is how you see that collapse — and how you test it. It does not hold its own opinion of who someone is; it reuses the exact resolver every event runs through and round-trips every fragment back through it. A fragment that doesn’t resolve home is a split, shown mechanically.
TL;DR
- Give it any one fragment — a customer id, an anonymous id, a developer user id, an email, or a rail id — and it resolves the canonical person and lays out their whole identity graph.
- Every axis is round-tripped through the same resolver every event uses. An axis is
linked(resolves home),drifted(resolves to a different customer), orunlinked(no index entry — an orphan fragment). - A person is
consistentonly when every axis resolves home. That is the mechanical “one person, one dot” test — run by the production code, not a heuristic. - It surfaces the enforced
identity-stitchcontract’s latest verdict — the same contract test the platform runs to prove merges never orphan money or access. - Read-only. It never merges, repoints an index, or runs the contract.
The fragments of a person
Crossdeck stores one person as a single canonical customer record (a cdcust_…) that absorbs every identity fragment it has seen for them. Those fragments are the axes the Identity Explorer walks:
| Axis | Where it comes from |
|---|---|
| Anonymous device | The id the SDK mints before sign-in. A person can carry several over time; they all live on the one customer. |
| Developer user id | The id you pass to identify() when the person signs in. |
| Stripe customer | The cus_… attached when they pay through Stripe. |
| Apple transaction | The original transaction id from an App Store purchase. |
| Apple appAccountToken | The install-stable token the Swift SDK stamps on a purchase — its own identity channel, independent of who owns checkout. |
| Google purchase | The purchase token from a Play Store purchase. |
Each axis also has an index entry — a small pointer (anon:…, developer:…, stripe:…) that maps that fragment back to its canonical customer. The index is what makes resolution O(1) on every event. The Identity Explorer’s test is, in effect, “does every pointer still point home?”
The round-trip test
This is the heart of the tool, and why it is trustworthy. For each axis the canonical person holds, the Explorer feeds that axis back into resolveCrossdeckCustomerId — the identical function the event pipeline calls to decide “whose event is this?” — and follows the merge chain to the live customer. The answer is compared to the canonical person.
| State | Meaning |
|---|---|
linked | The axis resolves home to this canonical customer. The stitch holds. |
drifted | The axis resolves, but to a different customer — its index pointer points away from this person. A drift the reconciler should re-home. |
unlinked | The axis has no index entry at all — an orphan fragment. This is how “two dots for one person” appears at the index layer. |
A person is consistent when every axis is linked. Because the test runs the production resolver rather than re-implementing the logic, a green verdict here means the same thing a green verdict means in production: every fragment of this person funnels to one customer, so they count once and see one coherent history.
Reading the customer doc tells you what Crossdeck believes. Round-tripping each axis tests whether the index agrees — whether a later event arriving with that fragment would actually land on this person. The two can disagree after an interrupted merge or a drifted pointer, and that disagreement is exactly the bug you came to find.
Running a lookup
Open Developers → Workbench → Identity Explorer. Pick the identifier type (or leave it on Auto-detect), paste the value, and run it:
- Auto-detect reads the shape —
cdcust_…,anon_…,cus_…, an email — and falls back to a forgiving cascade across the other axes for anything else, so a bare id still finds its person. - Explicit types — Customer id, Anonymous id, User id, Email, Stripe customer, Apple transaction, Google purchase — when you want to be precise.
A value that resolves to nothing returns a plain Not found — a fact, not an error. An anonymous id that resolves to nothing is itself the finding: an unstitched fragment with no customer behind it.
Reading the result
The result reads top to bottom:
- Verdict — Consistent (every axis resolves home) or Split (one or more do not), stated plainly.
- Canonical customer — the
cdcust_…the input resolved to, with environment, developer user id, email, display name, and how many anonymous devices it carries. - Identity axes — every fragment, its index key, where it resolves, and its
linked/drifted/unlinkedstate. - Merge history — earlier customer records that were merged into this person. Their ids resolve here through the merge chain, which is why an old id still finds the right customer.
- Contract — the enforced
identity-stitchverdict (below). - Notes — plain-English observations, including the precise reason for any split.
The enforced contract
Crossdeck runs an enforced contract test, identity-stitch, that re-derives every customer merge from source records and checks six invariants — the survivor is identified, every loser points to the survivor, no subscription or purchase is left orphaned, revenue is never double-counted, and the survivor’s entitlements cover its subscriptions. The Identity Explorer surfaces that contract’s most recent recorded verdict for your project, so the per-person view sits next to the project-wide guarantee.
Running the contract writes a run record — a mutation. To stay strictly read-only, the Explorer reads the latest verdict already on file rather than triggering a fresh evaluation. The verdict you see is the platform’s own last word, unedited.
Where “two dots” come from
The most common split has a simple cause, and the Explorer makes it legible. On mobile, the Swift SDK mints one anonymous id at first launch and keeps it for the life of the install — one device, one id. On the web, the anonymous id lives in per-origin storage, so a person who onboards on your marketing origin and lands in the app on a different origin re-mints a fresh id. Until an identify() stitches the two, that one person exists as two anonymous fragments — “two dots.” In the Explorer, the second fragment shows as unlinked (no customer yet) or, once stitched, as a second linked anonymous axis on the same canonical customer.
The Explorer reveals a split; it never repairs one (it is read-only). The repair is the normal stitch path: when the person signs in and the SDK calls identify(), Crossdeck attaches the new fragment to the existing customer. See Identify users.
Read-only
- Reads only. The tool reads customer records, index pointers, and the contract log. It never merges customers, repoints an index, or writes a contract run. The guarantee is pinned by a test that scans the tool’s source for any write API and for any call that would run the contract.
- No verdicts beyond fact.
linked/drifted/unlinkedare mechanical outcomes of the production resolver, not opinions. The tool reports what resolution does; the conclusion is yours. - Owner / member gated. Like every Workbench tool, it is an operator surface and never exposed to your customers.
Related
- Workbench — the hub the Identity Explorer lives in, and the read-only principles it inherits.
- Rail Ledger — the sibling tool: the rail’s truth for a transaction, next to your stored record.
- Identify users — the stitch path that links an anonymous fragment to a known person, and the repair for a split.
- Identity verification — how Crossdeck proves an identity claim before acting on it.