Anti-cheat policy
Effective date: 2026-05-09
Stake-weighted competitions only work if participants believe the outcome is fair. This page documents what we do today to detect manipulated evidence, what we do not yet detect, how to report a suspected cheater, and how to appeal a verdict you disagree with.
Who decides who won
Verdicts are produced by an off-chain pipeline that runs in our Fly.io worker fleet (lc-workers on mainnet) and is deterministic for a given input. The pipeline:
- Pulls evidence from each participant's connected providers (Strava, Garmin, Fitbit, Steam, Riot, OpenDota, FACEIT…).
- Normalises the evidence to a common schema (
schemas/activity.normalized.schema.json,schemas/game.match.schema.json). - Runs the challenge's rule against that normalised evidence (
offchain/evaluators/*). - Emits a verdict that an attestor signer signs (EIP-712).
- Records the signed verdict on-chain via the
LightchallengeAttestorcontract.
Once on-chain, the verdict triggers payout via autoDistributeWorker. Verdicts are append-only — the contract refuses to overwrite a recorded verdict for a given (challengeId, subject) pair, so a compromised signer cannot flip an existing result.
What we currently check
Fitness challenges
Implemented in offchain/inference/metrics.ts. For activities with GPS + heart-rate data:
- GPS continuity — fraction of the activity covered by valid GPS samples, computed from the route's point density and gaps. A treadmill spoof reuploaded as an outdoor run typically shows discontinuities. Configurable per-challenge as
antiCheat.minGpsContinuity. - Teleport detection — counts GPS jumps where the implied speed between consecutive samples exceeds the maximum possible for the activity type. Configurable as
antiCheat.maxTeleportJumps. - Heart-rate vs intensity consistency — a 30-minute 5 km/h run with a flat 70 bpm HR is implausible. We compute a consistency score from HR variance correlated against pace. Configurable as
antiCheat.minHrConsistency. - Activity-type validation — Strava/Garmin types (
walk,run,ride) are required to match the challenge type. A bike ride uploaded as a run is rejected. - Time-window enforcement — only activities whose
start_tsfalls inside the challenge window count. We do not accept retroactively edited start times. - Provider verification — evidence is pulled server-side using your OAuth token. Manually uploaded files are flagged and downweighted unless explicitly allowed by the challenge rule.
Gaming challenges
Implemented in offchain/evaluators/gamingEvaluator.ts with provider-specific adapters in offchain/adapters/:
- Match integrity from provider — Steam, Riot, OpenDota, and FACEIT all sign their telemetry. We accept only matches reported by the provider's authoritative endpoint.
- Ranked-only flag — challenges can require
rankedOnly: true, rejecting custom-game / private-lobby matches that can be staged. - Match window — only matches whose
start_tsfalls inside the challenge window count. - Per-account binding — your wallet is bound to a single Steam ID / Riot account at OAuth time. Switching accounts mid-challenge produces unbound evidence that does not count.
- Hero / champion filter — for hero-specific challenges, only matches with the required hero / champion count.
- Streak verification — for streak challenges, the evaluator walks the chronological match list and breaks the streak on any loss, even between qualifying wins.
Account-level checks
- One wallet per provider account (Strava, Riot, etc.).
- One participant per challenge per wallet — duplicate joins from the same wallet are blocked at the contract level (
Lightchallenge.join). - Multi-account / Sybil at the wallet level is detected post-hoc by clustering (shared funding source, shared OAuth identity hash). Confirmed Sybil cases are blacklisted from the hosted UI.
What we do not yet check (explicit gaps)
Honesty about gaps is part of fairness. Today we do not:
- Detect e-bike pace on Strava cycling activities beyond pure speed thresholds. Power-meter cross-checks are on the roadmap.
- Detect game-state spoofing beyond what the provider itself reports. If a Steam match is reported by Steam, we trust Steam.
- Cross-reference third-party leaderboards for plausibility (e.g. comparing a claimed 4-min mile to your historical baseline). Personal-baseline outlier detection is on the roadmap.
- Run real-time on-device anti-tampering in our iOS app. We use HealthKit's native data; we do not currently attest to device-attested integrity.
How to report a suspected cheater
Email abuse@lightchallenge.app with the challenge ID, the suspect's wallet address, and a description of what you saw. We aim to acknowledge within 48 hours. Confirmed cases result in:
- The cheater's wallet hidden from the public leaderboard for that challenge.
- Their stake forfeited to the rest of the participants where the contract permits, or returned to the prize pool.
- Wallet blacklist from the hosted webapp and mobile app for repeat offenders.
We cannot prevent a blacklisted wallet from interacting with the contracts directly on-chain — but we can stop displaying their challenges, hide them from search, and refuse to accept their evidence into our pipeline.
How to appeal a verdict
If you believe a verdict against you is wrong, click Appeal verdict on the challenge result screen within 7 days of finalisation. State your case in the form. A human operator will:
- Acknowledge within 48 hours.
- Re-fetch your evidence from the provider and re-run the evaluator.
- Reply with the outcome — usually within 14 days.
If the appeal succeeds and a verdict needs reversal, we will issue a make-good payment from the protocol fee pool. The original on-chain verdict cannot be erased; the make-good is a separate transaction with a clear reference to the appeal ID.
Roadmap
Open improvements we're tracking publicly:
- Personal-baseline outlier detection (flag activities >3σ from your trailing-90-day distribution).
- Power-meter consistency check for cycling.
- iOS DeviceCheck attestation for HealthKit-only challenges.
- Cluster-based Sybil detection visible to the appeals reviewer.
- Public per-challenge anti-cheat dashboard so creators can see why entries were rejected.
Open source
The full implementation lives at offchain/evaluators, offchain/inference, and offchain/adapters. If you find a bypass, please follow our security disclosure process rather than publishing it.