feat(abuse): shadow-mode claim anomaly detector (v1.4)#167
Open
code418 wants to merge 1 commit into
Open
Conversation
Adds the v1.4 Trust & safety "abuse detection" item in shadow mode: the
detector observes every claim, records flags for internal review, and never
blocks or voids a claim. The existing 1900 m/min hard reject in startScoring
is left intact.
Backend:
- _abuseSignals.ts: pure, unit-tested helpers (impossible-travel, out-of-window,
coordinate-cluster signals; severity rollup; trust-score decay; coordKey).
Shadow flag threshold (1500 m/min) sits below the hard reject so it can fire.
- startScoring: accepts/validates optional clientTsMs; enforceTravelSpeedLimit
returns the computed speed; each claim persists clientTsMs, travelSpeed and
coordKey6 for the detector.
- abuse.ts: onClaimCreated 2nd-gen trigger (europe-west4, eur3 Eventarc region)
writes moderationFlags + decays server-only trustScores; never throws.
reviewFlag admin callable stamps a flag reviewed (no voiding yet).
Client:
- Send clientTsMs with claims (claim_quiz_sheet, wear_claim_page).
- admin_abuse_screen.dart: Open/Reviewed queue mirroring the reports screen,
wired into the Home admin menu.
Rules: moderationFlags (admin-read, server-write) + trustScores (server-only).
Trust score is kept out of the world-readable users/{uid} doc so it can't leak;
flat moderationFlags/trustScores collections realise the roadmap's intent.
Roadmap: App Check enforcement moved v1.4 -> v1.7 (gated on iOS AppleProvider).
Tests: 400 functions tests + 413 flutter tests pass; flutter analyze clean;
firestore.rules validated.
Deferred follow-ups: device-hash signal + two-signal guard; enforcement/voiding;
move thresholds to Remote Config.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the v1.4 Trust & safety roadmap item "Abuse detection (impossible-travel & claim anomalies)" in shadow mode — the detector observes every claim, records flags for internal review, and never blocks or voids a claim. The existing
1900 m/minhard reject instartScoringis left intact.Ship-gate clause satisfied: "impossible-travel detector in shadow mode logging flags but not blocking."
Plan:
~/.claude/plans/wise-shimmying-thompson.md(one of v1.4's four items, scoped full-depth).What's included
Backend
functions/src/_abuseSignals.ts(new) — pure, TDD'd helpers:impossibleTravelSignal,outOfWindowSignal,coordClusterSignal,summariseFlags,applyTrustDecay,coordKey+ tunable constants. The shadow flag threshold (1500 m/min) sits below the live hard reject (1900) so it can actually fire (a load-time guard enforces this).functions/src/startScoring.ts— accepts/validates optionalclientTsMs;enforceTravelSpeedLimitnow returns the computed speed; each claim persistsclientTsMs,travelSpeed,coordKey6. Hard reject unchanged.functions/src/abuse.ts(new) —onClaimCreated2nd-gen Firestore trigger ineurope-west4(eur3 Eventarc region, mirroringonFriendAdded) writesmoderationFlags+ decays server-onlytrustScores; never throws.reviewFlagadmin callable stamps a flag reviewed (no voiding yet).Client
clientTsMssent with claims (claim_quiz_sheet.dart,wear_claim_page.dart).lib/admin/admin_abuse_screen.dart(new) — Open/Reviewed queue mirroring the reports screen, wired into the Home admin menu.Rules —
moderationFlags(admin-read, server-write) +trustScores(server-only).Roadmap — App Check enforcement moved v1.4 → v1.7 (gated on iOS
AppleProviderwork).Design notes / deviations
trustScores/{uid}collection rather than onusers/{uid}— that doc is world-readable, so atrustScorefield there would leak. FlatmoderationFlags/trustScorescollections realise the roadmap'smoderation/flags/users.trustScoreintent in a rules-correct way.Verification
cd functions && npm test→ 400 passingflutter test→ 413 passing;flutter analyze→ cleanfirestore.rulesvalidated (Firebase MCP)firebase deploy --only functions,firestore:rulesto create theonClaimCreatedEventarc trigger ineurope-west4.Deferred follow-ups (separate PRs)
device_info_plus+crypto) + the two-signal false-positive guard.recomputeUserAggregates); move thresholds +SHADOW_MODEto Remote Config.🤖 Generated with Claude Code