Skip to content

osdc/type

Repository files navigation

type

A Speed Typing platform for hosting tournaments. It focuses on server-side cheating mitigation rather than client-side (though there are client-side mitigations as well). It's certainly not a perfect filter but it makes the bar for cheating higher. Though it may be susceptible to white-box attacks because of its open-source nature.

It features 2 modes:

  • Individual mode: solo async runs against a running leaderboard.
  • Heat mode: rolling live lobbies (≤16) with a synchronized countdown and a 2 Hz live race board.

Prerequisites

  • Go 1.24+, Node 20+ with pnpm, Docker + docker-compose
  • A GitHub OAuth app (callback http://localhost:8080/auth/github/callback)

Setup

Make sure to complete the .env file

cp .env.example .env
make up
make migrate

Run (two terminals)

make run
make fe-dev

Open http://localhost:5173. Sign in with GitHub, create a competition in the organizer console, open it, then share the join code.

Dev login (no GitHub)

For local testing without OAuth, start the server with DEV_AUTH=true and POST /auth/dev-login to get a session cookie for a synthetic player.

Admin panel

A admin panel can be activated by setting the ADMIN_USER and ADMIN_PASSWORD in .env, it can be accessed through /admin route

  • Users: search, edit name/username, toggle organizer, ban/unban, force-logout
  • Competitions: list, force state transition, set visibility, delete
  • Global settings: hosting lock, public feed, login kill-switch
  • Audit log: immutable record of every admin action
  • Dashboard: aggregate stats (users, competitions, runs)

NOTE: You can setup behind a reverse proxy for hosting.

How it works

  • Credit ledger: Players are given attempt credits. With configurable grace failure attempts for genuine reasons like network issue.
  • Anti-cheat: see below
  • Scoring: net WPM (correct_chars/5)/minutes; accuracy below the floor scores 0. Best-of-N or average-of-N with qualify gating and accuracy / earliest-qualifying tie-breaks.
  • Audit: every committed run's raw keystroke stream is stored gzip-compressed for human replay of contested placements.

The Anti-Cheat

The anti cheat system uses various techniques to deter and flag cheaters. Policy is to always flag the user rather than kicking them. It is designed around server-side mitigations rather than client-side mitigations which can be easily bypassed. Uses various statistical models as well as deterministic models to flag cheat runs.

Like all anti-cheats it is not perfect, no where near perfect. But it should deter most people and set a higher bar on cheating than simple i.i.d. timing cheats.

I am explaining this on the homepage because it is visible already in the code, also I do not believe in security through obscurity.

It consists of various pieces:

i) Deterministic gate:

  • empty runs
  • overtyping (1.5 times the length of the prompt)
  • keystrokes timestamped before t = 0
  • faster than 50ms/char overall (~240WPM)
  • inter-key median < 20ms,
  • >= 30% of gaps < 15ms

ii) Arrival envelope timing

If client's timestamps exceed servers elapsed time + 750ms slack, so an attacker can't batch all of the keystrokes in one go.

iii) Behavioral scorers

  • Coeffecient of Variation of inter-key intervals
  • 50 <= ms/char <= 65, flags automatically, its too fast to ignore, but not impossible
  • pasted bursts, mid-run local sub-8ms gaps, they can get missed by the deterministic >= 30% < 15ms or the inter-key median
  • Structural detectors:
  1. digraph F-ratio: it basically groups every inter-key gap and computes ANOVA F-ratio, rationale is that "th", "qu", "er", as humans we type each specific pair at a stable, characteristic speed (for example on QWERTY, "th" is usually faster than "rd" as "th" can be typed with two hands while "rd" requires one hand), a bot using uniform timing doesn't follow this.
  2. lag-1 autocorrelations: A human doesn't generate each keystroke delay independently, consecutive delays are correlated, it captures that correlation like ramp ups, bursts on easy word, slow down. Though on a short test, effect of this probably negligible, but it none the less contributes in unison with other scorers.
  3. Skewness: determines the shape of the distribution, humans tend to have cluster of fast strokes, while a bot can be uniform.
  4. Wald-Wolfowitz runs test: basically used to test whether the timing of characters are mutually independent (they shoudn't be) \

A conjunction of these 4 metrics is used to determine rather than these individually, since a human will most likely break one of these.

iv) Cross attempt fingerprinting

Detects if the timings between characters are being replayed and flags the user.

v) Frontend barriers

  • isTrusted guarded, so synthethic KeyboardEvents are dropped. Doesn't protect against someone playing websockets events directly

About

Speed Typing Competitions for all!

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors