Skip to content

feat(client): behavioral human-likelihood for clearance mints (#328) - #11

Merged
cport1 merged 2 commits into
mainfrom
feat/328-behavioral-clearance
Jul 24, 2026
Merged

feat(client): behavioral human-likelihood for clearance mints (#328)#11
cport1 merged 2 commits into
mainfrom
feat/328-behavioral-clearance

Conversation

@cport1

@cport1 cport1 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

SDK half of WebDecoy/app#328 (PRD FR10) — pairs with WebDecoy/app#354.

A session that a human actually interacts with can now present positive evidence of it and upgrade its wd_clearance token to the graded human-likely trust level that routes can require.

Flow

Mint clean at idle exactly as before, then watch passively. Once there has been real interaction, summarize it once, re-mint with the aggregates attached, and stop listening. A session that never interacts sends nothing. One upgrade per page load, no matter how many times clearance is started or refreshed.

What leaves the browser

Counts, durations, variances and ratios — and nothing else.

Never: pointer coordinates, key identities, form values, DOM or page content, URLs, or any replayable event stream. The aggregates describe how a session moved, never what it did — the whole difference between this and a session-replay product. A test asserts the exact field list, so the payload cannot grow without a deliberate change to the published contract.

Reuses the existing BehavioralCollector rather than a parallel implementation; summarizeBehavior() maps it to the wire shape and drops everything else.

Details worth a second look

  • The 24-sample floor sits above the collector's own 20-sample threshold on purpose. Below 20, _detectMicroTremor returns a 0.5 placeholder rather than a measurement — and the server reads 0.5 as genuine human tremor. Shipping it would manufacture human evidence out of a mouse twitch.
  • The 750ms window floor also refuses tight scripted bursts client-side, so what reaches the server is the harder scripted case: automation that paces itself. (One test vector had to be rewritten for exactly this reason — the tight-loop version was correctly refused before it could be compared.)
  • The device fingerprint — the only expensive step, one canvas + one WebGL read — is computed inside the upgrade callback, so a session that never interacts never pays for it.
  • Listeners are passive (never delay scroll or input) and removed on delivery: zero steady-state cost for a cleared session.
  • A failed upgrade leaves the existing clean token untouched.
  • startClearance({ behavior: false }) opts out entirely; tokens then stay at clean.

Accessibility

A keyboard-only session produces no pointer cadence and is unscored, not scored badlysummarizeBehavior returns null rather than a low number, so the mint stays clean instead of looking bot-like. Covered by an explicit test.

Verification

10 new tests, 20/20 green in @webdecoy/client; turbo build clean including the DTS build.

cport1 added 2 commits July 24, 2026 12:41
The SDK half of app#328 (PRD FR10). A session that a human actually interacts
with can now present positive evidence of it and upgrade its wd_clearance
token to the graded 'human-likely' trust level, which routes can require.

Flow: mint clean at idle as before, then watch passively. Once there has been
real interaction, summarize it once, re-mint with the aggregates attached, and
stop listening. A session that never interacts sends nothing, and one upgrade
happens per page load no matter how many times clearance is started or
refreshed.

What leaves the browser is counts, durations, variances and ratios — and
nothing else. No pointer coordinates, no key identities, no form values, no
DOM, no replayable event stream. The aggregates describe HOW a session moved,
never WHAT it did, which is the whole difference between this and a
session-replay product. A test asserts the exact field list, so the payload
cannot grow without a deliberate change to the published contract.

Reuses the existing BehavioralCollector rather than a parallel implementation;
summarizeBehavior() maps it to the wire shape and drops everything else.

Notes:
- The 24-sample floor sits above the collector's own 20-sample threshold on
  purpose: below that, _detectMicroTremor returns a 0.5 PLACEHOLDER, and
  shipping it would manufacture human evidence out of a mouse twitch.
- The window floor also refuses tight scripted bursts client-side, so the
  harder scripted case (one that paces itself) is what the server scores.
- The device fingerprint — the only expensive step, one canvas + one WebGL
  read — is computed inside the upgrade callback, so a session that never
  interacts never pays for it.
- Listeners are passive and removed on delivery: zero steady-state cost.
- opts.behavior: false opts out entirely; tokens then stay at 'clean'.
The wdfp1 fp is the enforcement identity (tokens bind to it, the deny-list
keys on it) and now has three implementations that must agree byte-for-byte.
Names them, and warns against confusing it with the pipeline's server-composed
device_fp — a confusion that had made manual denies inert in the app repo.
@cport1
cport1 merged commit 9f0d0d4 into main Jul 24, 2026
2 checks passed
@cport1
cport1 deleted the feat/328-behavioral-clearance branch July 24, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant