Skip to content

Human Entropy Integrity Protocol

Know a human is behind every click.

HEIP reads live interaction for signs of automation. Your server then allows, steps up or denies each sensitive action.

HEIP wordmark drawn as a glowing cyan wireframe mesh

Why account checks are not enough

The login passed. The CAPTCHA was solved. The token is valid. None of it says who is driving the session when it moves money.

HEIP watches the session itself, not the account. These are four patterns the engine flags today, with the reason code it reports.

  • Scripts that fake human timing

    timing_quantized
  • Headless browsers with too-perfect motion

    overly_smooth_motion
  • Replayed or forged signal payloads

    proof_mismatch
  • Input from a hidden or unfocused tab

    active_while_hidden

Five steps from page load to verdict.

The browser collects evidence. Only your server, holding the secret key, ever hears the decision.

Load the SDK

One script tag with your public site key. The key only works from domains you whitelist.

pk_live_ key · ~5.6 KB gzip

Stream signed signals

About once a second the SDK sends timing and motion deltas, chained with an HMAC so a forged or replayed batch is rejected.

POST /v2/signal/:sessionId

Probe when unsure

When evidence is thin or risk climbs, the server answers with a canary or challenge directive that needs a live response.

directive: canary | challenge

Bind the action

On submit, prepareAction() returns a single-use nonce tied to this session and this action. It expires in 25 seconds by default.

TTL 5 to 120 s

Verify on your server

Your backend sends the nonce to /v2/verify with its secret key and gets ALLOW, STEP_UP or DENY with reasons. The browser never learns the verdict.

POST /v2/verify

Two keys. One call on your server.

The public site key runs in the page. The secret key stays on your backend and is the only thing that can read a verdict.

checkout.html
<script src="https://api.heip.io/sdk.js"
        data-site-key="pk_live_…" async></script>

<script>
  form.addEventListener("submit", async (e) => {
    e.preventDefault();
    const { sessionId, actionNonce } =
      await HEIP.prepareAction("payment");

    await fetch("/api/pay", {
      method: "POST",
      body: JSON.stringify({ sessionId, actionNonce }),
    });
  });
</script>
Example /v2/verify response
{
  "success": false,
  "decision": "STEP_UP",
  "confidence": 0.71,
  "risk": 1.32,
  "stage": 1,
  "mode": "enforce",
  "reasons": [
    { "code": "policy_confidence_low",
      "value": 0.71, "expected": ">=0.85" },
    { "code": "policy_stage_high",
      "value": 1, "expected": "<=0" }
  ]
}
  • ALLOWEvery gate in the policy passed.
  • STEP_UPNot enough evidence. Ask for a second factor.
  • DENYStrong adversarial signal, high risk, high confidence.

Policies

Each action gets the scrutiny it deserves.

Map your actions to a built-in template from the console, then override only the values you need. Your server keeps sending the same three fields.

DENY is hard to earn on purpose.

A false DENY locks out a real customer, so it needs all four conditions at once. Anything else that fails a gate becomes STEP_UP. A site in observe mode never returns DENY.

  1. Stage 2
  2. Risk at least 1.85 (default templates)
  3. Confidence at least 0.70
  4. Strong adversarial signal in the last 15 s

Every verdict says why.

Reasons come back as codes with the measured value and the expected one. Notable sessions also leave a short-lived audit trace you can fetch by id.

It measures rhythm, not people.

No identity, no biometrics, no stored profile. Session state lives in Redis and expires on its own.

What the SDK sends

  • Event kind: pointer, key, click or scroll
  • Time between events and movement size
  • Frame rate and screen pixel ratio
  • Page visibility and focus state

What it never touches

  • Which keys were pressed or what was typed
  • Names, emails, IDs, faces or voice
  • Cookies or a cross-site fingerprint
  • Page content or form values
  • Secret keys hashed at rest

    SHA-256 only. Shown once, two active at most, 24 h grace on rotation.

  • Browser tripwire

    A secret key used from a browser is refused and audited.

  • Domain whitelist

    Label-boundary matching with Public Suffix List rules, so *.com is never allowed.

  • Observe mode

    Run a site without enforcement. It never returns DENY while you tune policies.

Engineering

Tested by trying to break it.

Each security guarantee is broken on purpose in a mutation run, and the suite has to fail. The engine is replayed against golden fixtures from the original implementation.

Pilot access is open by request.

HEIP is self-hosted and in pilot. Tell us which action you want to protect and we will set up a site and keys with you.