Skip to content

Reputation System

Cerno’s reputation system (Phase 3) builds a behavioral fingerprint across sessions. The same human solving multiple challenges accumulates trust. Behavioral consistency across sessions is extremely hard to fake at scale.

This is Cerno’s software equivalent of an iris hash: not “do you have a body” but “do you behave consistently like the same human over time.”

How it works

  1. A user completes a challenge and passes validation.
  2. Your application supplies a stable first-party identity as stable_id.
  3. The server derives a reputation key from that stable identifier.
  4. Six behavioral features are recorded as a running mean (EMA).
  5. On subsequent sessions, the system compares current behavior against the stored fingerprint.
  6. Consistent behavior earns a trust bonus added to the session score.

Tracked features

The reputation system tracks six motor-control features across sessions:

FeatureSignal
velocity_stdSpeed variance across the trace
path_efficiencyEuclidean vs. actual distance
pause_countHesitations during solving
jerk_stdThird derivative of position (muscle jerk)
angular_velocity_entropyRandomness in direction changes
timing_cvCoefficient of variation in timing

Feature means are blended using an exponential moving average (EMA), creating a behavioral fingerprint that evolves with each session.

Trust scoring

When a returning user completes a challenge, the final trust score blends the current session score, historical trust, and behavioral consistency. The result is clamped to [0, 1].

Consistent behavior across sessions earns a trust bonus. Large deviations from the stored fingerprint reduce it.

Decay mechanics

Trust decays when a user is absent. After a period of inactivity, stored trust erodes progressively. Records expire entirely after the configured TTL (default 30 days).

API reference

reputationKey(stableId)

Derives a namespaced store key from your app-level stable identifier.

import { reputationKey } from '@cernosh/server'
const key = reputationKey(stableId)
// => "rep:a1b2c3..."

queryReputation(store, key)

Returns the trust score for a key. Returns 0.5 (neutral) if unknown or if the store does not implement reputation methods. Applies time-based decay automatically.

import { queryReputation } from '@cernosh/server'
const trust = await queryReputation(store, key)
// 0.5 for unknown users, 0-1 for returning users

updateReputation(store, key, score, features, ttlMs?)

Updates the stored reputation after a validated session. On first session, initializes the record. On subsequent sessions, blends the new score and features using EMA.

import { updateReputation } from '@cernosh/server'
await updateReputation(store, repKey, 0.87, extractedFeatures)
// Optional: custom TTL (default 30 days)
await updateReputation(store, repKey, 0.87, extractedFeatures, 60 * 24 * 60 * 60 * 1000)

computeConsistencyBonus(features, reputation)

Computes a bonus score (max +0.1) based on how closely current features match the stored fingerprint. Requires at least 2 prior sessions. Confidence scales linearly up to 10 sessions.

import { computeConsistencyBonus } from '@cernosh/server'
const bonus = computeConsistencyBonus(currentFeatures, reputationData)
// 0 for new users, up to 0.1 for consistent returning users

The bonus is added to the raw behavioral score during validation, before the threshold check.

KV store integration

Reputation is opt-in. The CaptchaStore interface has two optional methods:

interface CaptchaStore {
// ... core methods ...
// Reputation (optional)
setReputation?(key: string, data: ReputationData, ttlMs: number): Promise<void>
getReputation?(key: string): Promise<ReputationData | null>
}

If your store does not implement these methods, reputation is skipped. MemoryStore includes a built-in implementation for development and tests. For production, use a durable strongly consistent store. The Cloudflare KV adapter in this repo does not meet that bar.

The ReputationData shape stored per key:

interface ReputationData {
trust_score: number // 0-1 weighted average
session_count: number // completed sessions
feature_means: Partial<BehavioralFeatures> // EMA of tracked features
last_seen: number // timestamp (ms)
}

Pipeline integration

Reputation plugs into the validation pipeline automatically. After behavioral scoring completes, the server:

  1. Derives repKey from stable_id.
  2. Looks up existing reputation data.
  3. Calls computeConsistencyBonus() to get a bonus (up to +0.1).
  4. Adds the bonus to the raw score before the threshold check.
  5. After a successful validation, calls updateReputation() to persist the updated fingerprint.

Reputation is opt-in. Enable it only when you have both a real stable identity source and durable store support.