Where did this text come from?

Most paste detectors answer a yes/no question. That question is close to useless: pasting a URL and pasting an entire cover letter are the same event under that model. This one keeps a running breakdown of what the text is made of, and updates it as the text changes.

Write something

Try it three ways: type normally, paste a block in, then delete the pasted part again. The breakdown follows the text as it stands, not the history of what happened to it.

What it’s made of

Nothing yet.

The verdict appears once there is something to judge.

Verify it on the server

The score above came from the browser, so a determined user could edit it before posting. This sends the raw event log to /api/verify, where the same engine recomputes the verdict from scratch.

How it decides

Two sources of truth, used together, because each covers the other’s blind spot. InputEvent.inputType says what the user meant to do — the spec requires a paste event to precede any insertFromPaste. A diff of the value against the last known one says what actually happened, which is the only way to catch anything that changes a field without announcing itself: extensions, password managers, framework re-renders, automation drivers.

Underneath is a piece table with provenance attached — a list of runs that always sums to the field’s length. Paste a hundred characters and rewrite half, and you get fifty typed and fifty pasted. Delete the paste entirely and the pasted ratio returns to zero, because that is the honest answer.

What it deliberately does not do

The limits, plainly

It cannot detect a ghostwriter. Someone transcribing text from a phone beside their keyboard produces an entirely ordinary typing profile. This measures how text entered a field, never who composed it.

It is client-side. JavaScript can be disabled, the payload edited, the events synthesised. Server-side replay raises the cost of a forgery; it does not eliminate it.