Fingerprint joint (platform/gpu/screen/colour/cores) is improbable under the real-traffic prior
br.fingerprint_improbable ·
What it catches
v0.74.24 THRESHOLD cross-source recalibration (FP fix, fpgen second source): the committed threshold was browserforge's SELF-p1 (-7.73), i.e. the 1% tail of browserforge scored against its own prior. Measured against an INDEPENDENT dataset (fpgen, Scrapfly real-data-derived) it over-flags: ~5% of fpgen fingerprints fall below -7.73, not 1% — the threshold is over-fit to browserforge's narrower distribution (the exact single-source-number trap the standing constraint warns about; corroborating-only so no false BOT, but it lifts the legit-browser SUSPICIOUS rate ~5x above the 1% design). Fix: the threshold is now the CONSERVATIVE cross-source bound min(browserforge-p1, fpgen-p1) = -9.48, so it flags <=1% of BOTH distributions (fpgen drops 5.0% -> 0.8%, browserforge drops below the re-tier cutoff) — strictly FP-safer, never raises the flag rate. The near-impossible joints still fire (a one-eps-floor factor like Apple-GPU-on-Windows is ~-10, well below -9.48; synthetic tests + the corpus full-vector improbables unchanged). write_prior now takes a threshold_feats independent calibration set and build_prior_file calibrates on fpgen by default; guarded by test_prevalence.test_committed_threshold_is_cross_source_conservative. gpu stays single-source pending Tier-3 real-device data — the proper fix remains a real-traffic prior via build_prior_from_sessions. The prevalence/likelihood model (docs/prevalence-model.md): coherence rules catch hard contradictions, this catches soft IMPROBABILITY — a coherent fingerprint whose platform/gpu/screen/colour/cores combination is deep in the tail of the real-traffic prior (the BrowserForge/randomizer attack no contradiction rule fires on). Emitted by detector.prevalence below the prior's p1 threshold (~1% of real fingerprints). CORROBORATING-only (experimental, low weight): the prior is single-source (browserforge) until corroborated against Tier-3 real traffic, so it amplifies an existing suspicion, never convicts alone. v0.73.1: the SCREEN factor was cross-checked against the Intoli real-traffic source and FAILED as exact 'WxH' — the browserforge prior missed 13-46% of real desktop resolutions (a circular single-source FP: validated only on browserforge, it would convict real users on an unseen screen). Fixed by coarsening screen to (size-class, orientation) buckets, cutting the real-traffic miss to ~0% (macOS/Android/Linux); gpu/colour/cores stay single-source pending Tier-3. v0.74.8: 'unknown never fires' — the committed threshold is the p1 of FULL-vector browserforge fingerprints, so scoring a PARTIAL vector let a missing factor's eps floor (~-9.2 nats each) masquerade as improbability (a session lacking screen+colour sank ~-18 below threshold and tripped on ABSENCE, not improbability — an absence-as-improbability FP). Now is_improbable abstains unless gpu/screen/colour/cores are ALL observed; on the live corpus this dropped 14 fires → 3 (the genuine full-vector improbable joints: iframe-spoof/stealth-patched/ios-ua-spoof) with ZERO net detection loss (the 11 dropped were partial-vector captures already hard-convicted by coherence/automation). v0.74.20: the COLOUR factor was DROPPED — a second circular single-source FP, like the screen factor. color_depth is a DISPLAY property (24=sRGB, 30=HDR), OS-INDEPENDENT — every real browser reports 24 regardless of platform (GROUNDED: the headful Chromium/Firefox/WebKit captures all report 24, and color_depth does not vary by OS). But browserforge GENERATES color_depth=32 for Windows at 93%, so a real Windows user (24) took a ~-3 log penalty that the browserforge calibration could NEVER see (browserforge scores its own 32s against a 32-heavy prior, no penalty; the bias only manifests on real 24-reporting traffic). Conditioning a display property on the OS is unsound and the colour prior is uncorroborable (Intoli lacks color_depth), so per the standing 'never act on a single-source number' rule the factor was removed rather than trusted. The prior + p1 threshold were regenerated over gpu/screen/cores (threshold -10.75 -> -9.04); a real-Windows-like fingerprint's margin improved (-6.55 -> -3.48) and the corpus label distribution is unchanged (51 bot / 1 suspicious, zero detection loss; corroborating-only). v0.74.21: the CORES factor was COARSENED into size-class buckets (<=2 / 3-4 / 5-8 / 9-16 / 17+), the same fix the screen factor got. The EXACT hardware_concurrency was a single-source FP landmine: browserforge under-samples real cores diversity and its prior has gaps + garbage (a 384-cores value), so a real but uncommon-or-high count took a deep penalty the browserforge calibration could not see — a real 128-core workstation hit the eps floor (-9.2) and was flagged improbable on CORES ALONE, and a common 6-core CPU took ~-3.7. Bucketing maps every plausible real count to a populated bin (no eps-gap): a real Windows + 128-core workstation now scores -4.91, a 6/8-core -3.32. Prior + p1 threshold regenerated over gpu/screen/cores-bucket (-9.04 -> -7.73); corpus label distribution unchanged (51 bot / 1 suspicious, zero detection loss). gpu remains single-source pending Tier-3; screen and cores are now both coarsened-robust and colour is dropped. v0.74.33: the GPU 'other' CATCH-ALL was the last single-source FP and is now UNKNOWN→abstain. GROUNDED on real captures: headful firefox/firefox-stock report 'llvmpipe, or similar' (Firefox software-render generalization) and privacy mullvad reports 'Mozilla' (RFP renderer generalization), both of which map to no recognized GPU family → the old 'other' bucket, which browserforge under-generates (it emits concrete vendor strings), so P(gpu='other'|plat) is a tiny eps-floor and all 3 REAL browsers tripped br.fingerprint_improbable on gpu ALONE (a corroborating false-positive that lifts the legit-browser flag rate — the exact single-source trap, same class as the dropped colour and coarsened screen/cores). Fix: _gpu_family returns None for an unclassifiable renderer, so the existing 'abstain unless gpu/screen/cores all observed' gate withholds (unknown never fires) — real firefox/mullvad now score is_improbable=False (3→0 real-capture flags) while the recognized-family improbable joints are unchanged (swiftshader-on-macOS ios-ua-spoof and apple-GPU-on-Windows webkit-ua-spoof still fire). A renderer-masking spoof that generalizes to a generic string (renderer-spoof) drops the prevalence fire but stays bot via br.webgl_renderer_artifact — zero net detection loss. No prior regeneration needed (the dead 'other' bucket is simply never queried; recognized-GPU scoring + the committed threshold are unchanged and the change only REMOVES fires, strictly FP-safer). Guarded by test_prevalence.test_unclassified_gpu_abstains_real_firefox_mullvad_not_improbable + test_gpu_family_branches.
Signals it reads
browser.prevalence_low
How it fires
present