Anti-fraud: one account per person enforced at sign-up (device cookie + IP), flags, admin duplicate signals, suspend switch

Marty, 2026-09-16, after @megamol created megamol2/megamol3 under his own link and bought $20 on each
to fake his two qualifying buyers. fraud.js records sign-up IP/UA/browser id (iap.dev cookie set with
the code request) and last-seen on sign-in. New accounts: dup-device (browser already has an account)
and sponsor-device are refused, ip-burst (> fraudMaxSignupsPerIpDay, default 2, per 24h) is refused;
sponsor-ip and shared-ip are flagged only. Flagged/suspended accounts never count on the leaderboard
and cannot adopt from the tank; suspended accounts are signed out everywhere (auth.fromRequest
wrapper) and refused at sign-in. Admin > Members: Duplicate signals card (shared browser / IP,
flagged, suspended), flags badge, Suspend/Unsuspend; GET /api/admin/fraud; PATCH members {suspend,
reason, flags}. Telegram admin alert on every block/flag. Privacy page + chatbot prompt updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-16 14:39:13 -05:00
parent 125be174e3
commit 34c62b9d3a
7 changed files with 224 additions and 9 deletions
+3 -2
View File
@@ -263,7 +263,8 @@
</div>
<div class="card">
<div class="card-head"><h3>Members</h3><span class="sub" id="memSub">newest first</span></div>
<p style="margin:0 0 10px"><input id="memFilter" placeholder="Filter by email, username, member # or sponsor" style="width:100%"></p>
<p style="margin:0 0 10px"><div class="card" id="fraudBox" style="margin-bottom:12px"><p class="small muted">Loading duplicate signals…</p></div>
<input id="memFilter" placeholder="Filter by email, username, member # or sponsor" style="width:100%"></p>
<p class="small muted">Sponsor = the token the account joined under (username, share code, or member #). Editing it re-points free referrals and future purchases. On-chain sponsorship is permanent once activated.</p>
<div class="tablewrap"><table class="adm-table" id="memTable"></table></div>
</div>
@@ -487,6 +488,6 @@
</div>
<script src="/assets/common.js?v=20260916a"></script>
<script src="/assets/admin.js?v=20260916b"></script>
<script src="/assets/admin.js?v=20260916c"></script>
</body>
</html>