Conformance Diff
Compare your parser/collector against reference captures: status, header order, JA3/JA4, tokens.
The diff answers one question: would the server’s anti-fraud gate see your parser’s traffic differently from the real app’s? Give it two captures — a reference from the genuine client and a subject from your own collector, parser, or replay run — and it reports every measurable shape divergence: status distribution drift, HTTP/2 header-order signatures, cookie ordering and splitting, JA3/JA4 per SNI, decoded body keysets, and token mint→use joins with timing deltas.
This is the productized version of the manual analyze_traffic.py comparison loop. The motivation: anti-fraud gates rarely reject on one loud signal — they accumulate quiet ones. A wrong header order, a swapped cookie pair, and a too-fast token reuse each look harmless alone; stacked, they become a 451. The diff surfaces every dimension side by side so you can close them all at once.
[!NOTE] This workflow compares traffic from systems you are authorized to test. The diff is passive — it compares two captures you already hold and sends nothing to any server.
Load the reference and subject captures
Import both captures first (/docs/en/capture/importing/):
- The reference: a
pcap/pcapng(or HAR) recording of the real application, ideally with a TLS keylog so JA3/JA4 and decrypted bodies are available. - The subject: your collector’s output — a pcap recorded while your parser ran, or replay history captured back into the workspace (/docs/en/replay/fingerprints/ covers the TLS fingerprint control that makes replay comparable).
Both sessions must be enabled for the API lab. The more complete the import (keylog present, decode pipeline configured), the more dimensions the diff can measure.
Run the diff
Open the API lab → Collector tab → Conformance view (the default), then:
- Pick the real-app capture under Reference (real app).
- Pick your parser/replay capture under Subject (parser / replay).
- Optionally set a Path filter — a substring matched against request paths (e.g.
/catalog/product/card) to scope the diff to one endpoint. - Optionally set a Join cookie — the token name to join carriers to minters on (e.g.
__gsac_a-goldapple). Leave blank and the diff auto-detects the most common shared cookie. - Click Run conformance diff.
The same run also evaluates device-identity rules and a token lifecycle report against the subject; both appear below the diff sections.
Read the findings
The report opens with a severity-ordered findings list; each finding maps to a divergence class:
| Finding | Severity | Meaning |
|---|---|---|
Status drift: 451 appears N× in subject vs M× in reference | high (4xx/5xx), else warning | Subject gets statuses the reference does not; a 4xx/5xx-heavy subject fails the iteration instantly. |
Header-order signature differs on METHOD host/path | warning | The most common ordered header-name sequence differs per endpoint. |
Cookie order differs on METHOD host/path | warning | Cookie names appear in a different order inside the Cookie header. |
TLS fingerprint differs for SNI (JA4 … vs reference …) | high | The JA3 or JA4 set for that SNI does not match. |
Join field "X" disagrees in N joined pairs | warning | A carrier header disagrees with the value the minting request’s body declared. |
Subject capture conforms to the reference on all measured dimensions | info | Nothing diverged on any measured dimension. |
Below the findings, the Status distribution table shows per-status counts for both sides (drifted rows highlighted), and TLS per SNI lists each host with a match/drift badge. The rejection-class distribution buckets responses into labels such as accepted, antifraud-reject (451), rate-limited (429), and client-certificate-required (a 400 whose body names the missing client cert).
Report sections in detail
Each section compares reference and subject per endpoint key (METHOD authority+path) and reports the top signatures on each side — up to 8 samples per section — with a matches verdict on the most common entry:
| Section | What it compares |
|---|---|
| Header-order signatures | Ordered request-header name sequences (lowercased, HTTP/2 :-pseudo-headers excluded). |
| Cookie orders | Order of cookie names inside each Cookie header, per endpoint. |
| Cookie header counts | How many Cookie headers each request carries — catches header-splitting differences. |
| TLS per SNI | Unique JA3, JA4, and ALPN sets per server name; a host matches only when both JA3 and JA4 sets agree. |
| Body keysets | Sorted JSON key paths (two levels deep) of decoded request bodies per endpoint — schema-shape drift. |
| Token join | See below. |
Token joins and timing deltas
When a join cookie is set or auto-detected, the diff classifies each side’s requests as minters (a response set the cookie via Set-Cookie) or carriers (the request sends it), then joins carriers to minters by exact cookie value. The join block reports:
- joined / unmatched pair counts for both sides;
- Δt min/median/p90/max — the delay between a token being minted and first carried, in seconds. Reusing a token 0.1 s after minting where the real app waits seconds is a timing tell;
- requests before first mint — carrier requests preceding any minting event — bootstrap-less traffic the real app would never produce.
The underlying report can also check field agreement across the join — e.g. that a plaid-os-version header on later requests equals mobileSdk.data.text.AndroidSDK from the minting request’s decoded body.
Typical fixes by divergence class
| Divergence | Typical fix |
|---|---|
| Status drift to 451/403 | The symptom, not the cause — work the behavioral rows below, then re-measure. |
| Header-order signature differs | Emit request headers in the captured order; most HTTP stacks sort or reorder them by default. |
| Cookie order differs / splitting | Serialize cookies in the observed name order as a single Cookie header (or the same split the app uses). |
| TLS fingerprint differs | Match the ClientHello — use the captured JA3/JA4 as the replay/collector fingerprint target (/docs/en/replay/fingerprints/). |
| Body keyset differs | Align the decoded request JSON schema: same keys, same nesting, no extra debug fields. |
| Join Δt or pre-mint requests off | Reproduce the app’s bootstrap sequence and token cadence rather than minting and firing back-to-back. |
[!TIP] Fix one class, re-run the diff, and confirm its finding disappears before moving on — the findings list is your regression check. When every dimension conforms but combined changes still get rejected, switch to the Probes view: it replays single and pairwise mutations and flags stacked rejections — mutations accepted individually but rejected in combination.