The knownprint demo

What knownprint sees about this browser, and under it a playground to wipe it, ban it and run it again.

Who is this browser?

Live on the pilot node

The playground

Wipe this browser and run again, ban yourself, and read everything knownprint recorded.

    For developers

    Wire it up

    Safari caps cookies written by script at seven days, and cookies served from a third party's address too. knownprint runs from your own domain, so its cookie arrives in your own server's response and Safari does not cap it.

    1. Step 1

      One proxy rule

      Your nginx or Caddy forwards /kp/ to knownprint, so the agent, its endpoints and the cookie are all on your own domain. Only the kp_t cookie leaves your server; your session cookies stay where they are.

      nginx, from the knownprint recipe, shortened
      # Inside your site's TLS server block. The full recipe adds the bundle
      # location, the cache headers and /.well-known/knownprint.json.
      # Signup (coming) gives you $knownprint_upstream and the proxy secret.
      include /etc/knownprint/proxy-secret.conf;
      
      location = /kp/agent.js {
          proxy_pass $knownprint_upstream;
          proxy_set_header X-Knownprint-Proxy-Secret $knownprint_proxy_secret;
          proxy_set_header X-Knownprint-Client-Ip $remote_addr;
          proxy_set_header X-Knownprint-Forwarded-Host $host;
          proxy_set_header Cookie "";
          proxy_hide_header Set-Cookie;
      }
      
      location ^~ /kp/ {
          proxy_pass $knownprint_upstream;
          proxy_set_header X-Knownprint-Proxy-Secret $knownprint_proxy_secret;
          proxy_set_header X-Knownprint-Client-Ip $remote_addr;
          proxy_set_header X-Knownprint-Forwarded-Host $host;
          proxy_set_header X-Knownprint-Net "";
          # only kp_t leaves your server ($knownprint_cookie, knownprint-map.conf)
          proxy_set_header Cookie $knownprint_cookie;
      }
      Caddy, inside your site block
      import knownprint
    2. Step 2

      One script tag, and the request id posted with the form

      The agent reads the browser and hands the page a request id (requestId) and its expiry, never the visitor id, the confidence or a verdict to tamper with. The page never learns which browser it is; your backend does. Your form posts the request id (kp_request_id) with the signup.

      Your page
      <script src="/kp/agent.js" defer></script>
      
      // PUBLIC_KEY is your environment's public key (pk_live_...).
      const agent = await window.knownprint.load({ pk: PUBLIC_KEY, profile: 'security' });
      const { requestId } = await agent.identify();
      form.elements.kp_request_id.value = requestId; // posted with the form
    3. Step 3

      One call in your backend, in shadow mode first

      Read the event with your secret key, then ask for a verdict. Shadow mode records what knownprint would have done and blocks nothing. Switch to enforce after a week of verdicts.

      HTTP, live today
      GET /v1/events/evt_q3vx7k2mz4rb6nw5tc2ehj7ypd
      Authorization: Bearer <your secret key>
      
      POST /v1/check
      Authorization: Bearer <your secret key>
      Content-Type: application/json
      
      {"request_id": "evt_q3vx7k2mz4rb6nw5tc2ehj7ypd", "action": "signup", "shadow": true}
      Node SDK, coming with the SDK release
      app.post('/signup', kp.require('signup', options), signupHandler);

    What comes back

    A deny in shadow mode for a browser you banned: the verdict, its reasons, the identity behind it and the counters, for your code to log or act on. The ids are made up.

    CheckResponse
    {
      "action": "deny",
      "score": 0, coming
      "reasons": ["visitor_blocklisted", "shadow_mode"],
      "list_hits": [
        {
          "list": "blocklist",
          "subject_type": "visitor",
          "subject": "v_m5tk2wq7xr3bz6ne4yc2hv7pja",
          "via": "token"
        }
      ],
      "identity": {
        "visitor_id": "v_m5tk2wq7xr3bz6ne4yc2hv7pja",
        "match_type": "token",
        "confidence": 1,
        "anonymity_set_size": 1,
        "device_cluster_id": null,
        "cluster_confidence": null
      },
      "velocity": {
        "accounts_visitor_5m": 0,
        "accounts_visitor_1h": 0,
        "accounts_visitor_24h": 0,
        "ips_visitor_5m": 1,
        "ips_visitor_1h": 1,
        "ips_visitor_24h": 2,
        "signups_ip_5m": 1,
        "signups_ip_1h": 1,
        "signups_ip_24h": 3,
        "events_ip_5m": 2,
        "events_ip_1h": 4,
        "events_ip_24h": 9
      },
      "human_review_required": true,
      "shadow": true,
      "check_id": "chk_b7zq2m4kx6vn3rt5wy2ech7pdl",
      "request_id": "evt_q3vx7k2mz4rb6nw5tc2ehj7ypd"
    }
    • What to do: action (allow, challenge or deny).
    • Why, in codes that never change meaning: reasons.
    • Which of your lists matched, and how: list_hits.
    • Which browser, how it was recognised, how sure, and how many look alike: identity.
    • Counts over 5 minutes, an hour and a day: velocity.
    • Whether a person should look before you act: human_review_required.
    • Whether this was a dry run that blocked nothing: shadow.

    score: 0 until the suspect score arrives coming

    Get early access

    What you get

    Every field

    Someone on your team asks whether two accounts came from the same browser. The answer should come with its working. Everything below is live on the pilot today, and the panel above shows most of it on your own browser.

    Identification

    • Recognise a returning browser, with or without its cookie.

      visitor_id match_type

    • Decide how far to trust a match: meant as a probability, capped where the data is thin.

      confidence

    • See how many browsers look like this one, so a common setup never reads as sure.

      anonymity_set_size

    • Read why it matched, or why it refused to.

      evidence

    • When knownprint will not link, get the likely candidate, if there is one, for review.

      high_recall_id

    • Spot a second browser on the same hardware and network.

      device_cluster_id cluster_confidence

    • Pull every visit of a browser, an account, a device cluster or an address.

      GET /v1/events GET /v1/visitors/{id}

    Signals

    • Stop scripted signups, and know which tool ran them.

      signals.bot detail.bot_type

    • Know when a browser blurs or hides its fingerprint.

      signals.privacy_settings protection_flags

    • Know when a visit comes from a private window.

      signals.incognito

    • Read the browser, engine and OS as knownprint classified them.

      browser

    • Keep the address and network block for your own lists, and know which network signals were measured.

      network.ip network.ip_prefix network.availability

    • Catch replayed and forged requests.

      integrity

    • Check what the agent costs your page, source by source.

      timings

    See yours in the panel above

    Proof

    How we measure

    You have been quoted an accuracy figure before. Ask for the corpus behind it and the call goes quiet. Here is the honest version: what every event tells you, how we test it, and what we have not measured yet.

    Read the confidence, not a claim

    • See how many browsers look like this one before you act (anonymity_set_size).
    • Know which signals could not be measured, instead of getting a guess: every event says how much of the network knownprint saw (network.availability), absent values stay null, and below full the bot confidence is capped at 0.7.
    • Confidence is meant as the probability that the match is right, and it is capped where the data is thin: an exact match at 0.95, or at 1 divided by the number of browsers that share its fingerprint when that is lower, and a linked match at 0.8 until the pilot has confirmed 400 matches for its browser family.
    • When knownprint refuses to link, it still names a likely candidate for your review when it has one (high_recall_id), and a ban on that candidate answers challenge, never deny.

    How we test it

    1. G0, every commit: synthetic browsers in CI, in gate mode. A change that breaks stability, uniqueness or cost fails the build.

      Gating
    2. G1, nightly: the lab's corpus of real browsers. It reports until it holds 3,000 hard negative and 400 positive real pairs, then it gates. On it held 0.

      Reporting
    3. G2, weekly: labelled pairs from the pilot, and never blocking. It has not run yet.

      Not run

    Targets, and where they stand

    • Target: pairwise precision (of the visits knownprint links, the share that really are one browser) at least 0.995. Status: not yet measured on real pairs (G1 holds 0 real pairs; G2 has not run), as of .

    • Target: recall (of the visits that really are one browser, the share knownprint links) at least 0.90 on desktop after 7 days. Status: not yet measured on real pairs (G1 holds 0 real pairs; G2 has not run), as of .

    • Target: evader recall (the same, for banned users who come back) at least 0.80 on desktop after 7 days. Status: measured only by G2 on pilot pairs, which has not run, as of .

    • Target: calibration error (how far the stated confidence strays from the share of matches that are right) below 0.02. Status: not yet measured on real pairs (G1 holds 0 real pairs; G2 has not run), as of .

    No accuracy percentage appears on this page until the lab publishes one with its gate.

    What we have measured

    Identify, p95 Identify: 95 of every 100 identifications answered within 33.96 ms (p95), match included, at 200 identifications a second against a gate of 40 ms, measured in CI on synthetic data on the self-hosted gate runner on (G0); on that runner the same code also read 41.71, 55.52 and 130.83 ms in its other gate runs that day, and older unchanged code reads from 33.76 to 45.65 ms from run to run, so read it as a range until the runner question is settled (A-161, open).

    Check, p95 Check: p95 3.86 ms at about 200 checks a second, against a gate of 50 ms, measured in CI on synthetic data on the self-hosted gate runner on (G0).

    The pilot

    Live traffic, not a gate Since knownprint has run as a shadow pilot on a live community site: it records verdicts and blocks nothing. A count of live traffic, not a gate result: in the 24 hours before its upgrade at 21:34 UTC on , the pilot node answered 5,802 identify requests in total, by its own request log. That upgrade moved it to identification v2.2 and wiped its collected data first, so its history starts again from that day.

    Per-site switches

    coming

    Three behaviours stay off unless your site turns them on. Each switch says in plain words what it does and records that you turned it on.

    • Token respawning: refill a cleared cookie or copy from the one that survived. Turning it on records your acknowledgement, and your disclosure file and notice say that your site does it.
    • Cluster deny: deny, instead of challenge, when a blocklisted device cluster comes back. A cluster is hardware plus a shared network and can include someone else's browser; the switch records your acknowledgement of that.
    • Network links: challenge or deny a browser on the same home connection as a banned one. VPNs, proxies, Tor, datacentres and, by default, any connection with more than 10 visitors a day are left out, and deny needs your acknowledgement.

    Get early access