Skip to content

Behavioral Scoring

Cerno’s behavioral scoring uses deterministic math — no ML on the hot path. Pure statistical thresholds against known human distributions.

Public features

The client SDK extracts 7 behavioral features from raw pointer events:

FeatureWhat it measures
velocity_stdSpeed variance across the trace
path_efficiencyEuclidean vs. actual path distance
pause_countHesitations over 100ms
movement_onset_msReaction time before first move
jerk_stdThird derivative of position
angular_velocity_entropyRandomness in direction changes
timing_cvCoefficient of variation of inter-event timing

All features are re-extracted server-side from raw events. The client cannot lie about them.

Scoring

Each feature is scored against baselines. Features are combined via weighted average with penalties for suspicious patterns. The server also computes additional secret features not present in client code — see Secret Features.

Maze-relative baselines

Published mouse-movement baselines assume free-form movement. A maze constrains the path. A human solving an easy 4x4 maze behaves differently than one solving a complex 12x12.

Cerno computes a MazeProfile for each generated maze and derives expected feature ranges from the maze structure. This prevents false rejections on easy mazes and missed bots on hard ones.

Penalties

  • Low sample count — not enough pointer data is suspicious
  • Fast completion — humans don’t solve mazes instantly

Threshold

Default: 0.6 (configurable via ScoringConfig). Adjust based on your use case.

Runtime configuration

Use ScoringConfig to override baselines and scoring parameters at runtime:

const config: ServerConfig = {
secret: process.env.CERNO_SECRET!,
store: new MemoryStore(),
scoring: {
gaussianK: 3,
publicScoreWeight: 0.7,
secretScoreWeight: 0.3,
},
}

See the Server SDK guide for full configuration options.