Inspiration

What it does

Inspiration

Mods are the unpaid backbone of Reddit, and the highest-impact attack on a community — a coordinated raid — happens in minutes. By the time a mod opens the queue and starts manually approving or removing posts, the damage is already done. Existing tooling reacts; it does not predict, and it does not stop a raid mid-flight without a human in the loop for every individual decision. I wanted to build something that gives mods both the early warning and the off switch.

What it does

Raid Radar is a Devvit-native mod tool that detects coordinated raids on a subreddit while they are still forming, and lets moderators end them with a single click.

After installation, Raid Radar pins a "Raid Radar — Live Pulse" custom post to the subreddit. This dashboard polls every 15 seconds and surfaces five live metrics computed from Reddit's onPostCreate and onCommentCreate triggers:

  1. Posts per hour — with a rolling 24-hour hourly-bucket baseline and per-metric z-score
  2. Comments per hour — same baseline + z-score
  3. Ratio of recent activity from accounts under 7 days old
  4. Hours saved — a quantified, conservative estimate of mod time auto-saved
  5. Detection mode — "Calibrating" for the first 4 hours, then "Z-score" once enough history exists

The alert level (all-clear / elevated / critical) is recomputed every poll from three independent layers: z-score thresholds (2.5σ elevated, 4σ critical) when the baseline is ready; fallback hard thresholds while it calibrates; and a new-account-ratio gate (40% / 70%) with a minimum-events guard so quiet hours don't false-positive.

When the mod has seen enough, two menu items appear in both the subreddit overflow menu and on the dashboard post itself:

  • 🔒 Raid Radar: Lock down submissions (30 min) — starts a lockdown
  • 🔓 Raid Radar: End lockdown — ends it early

During a lockdown, every incoming PostCreate / CommentCreate trigger calls reddit.remove(id, false), increments the lifetime "blocked" counter, and adds 30 seconds to the Hours Saved metric. A Devvit scheduler.runJob automatically ends the lockdown when its window expires, so a sub cannot accidentally stay locked.

Every auto-removed item lands in the mod queue with an Approve button — false positives are still one click away from being restored.

How we built it

Stack: Devvit (Reddit Developer Platform) · React 19 · TypeScript · Tailwind CSS 4 · Hono · Devvit KV (Redis-compatible) · Vite 7.

Architecture

  • src/server/ — Hono routes (api, menu, triggers, tasks), the lockdown state machine, signal storage backed by KV sorted sets, and cached getUserByUsername lookups for account-age detection
  • src/client/ — React splash + expanded dashboard, with a usePulse hook polling /api/snapshot every 15s and a live countdown banner that ticks every second
  • src/shared/ — request/response types shared by both halves

Detection algorithm. Events flow from Reddit triggers into KV-backed sorted sets. readWindow reads the last 24h, buckets events by hour-offset, computes mean and standard deviation, then derives a z-score for the current 60-minute window. computeAlert layers z-score, fallback thresholds, and new-account ratio with priority rules.

Lockdown. State persists in KV under lockdown:active, lockdown:until, and lockdown:jobId. enableLockdown schedules an unlock job via scheduler.runJob and stores the returned jobId so a re-enable can cancel the previous one. getLockdownState defensively treats a missed unlock as inactive — the dashboard can never get stuck in a locked banner.

Challenges I ran into

  • PostV2.id / CommentV2.id arrive bare from trigger payloads (e.g. 1tam00t), but reddit.remove requires the t3_ / t1_ "Thing" prefix via template-literal typing. My initial asserts id is t3_${string} predicates threw on every PostCreate, the catch block silently fell through to normal recording, and the lockdown looked broken (Hours Saved stayed at 0) because Reddit's own anti-spam was independently removing posts. Fix: replace assertions with withT3Prefix / withT1Prefix helpers that prepend when missing.
  • Strict TS settings (exactOptionalPropertyTypes, noUncheckedIndexedAccess, no-floating-promises) caught real bugs but required careful handling of optional UI props and array indexing in the hourly-bucket computation.
  • Windows lint script — the scaffold ships eslint 'src/**/*.{ts,tsx}' with bash-style single quotes, which Windows PowerShell passes as a literal string. Switched it to eslint . since the flat config already scopes files per-folder.
  • The --template web flag is gonedevvit new no longer accepts it; the actual entry is React. The wizard falls back to the interactive picker, which is fine, but cost a few minutes of confusion.

Accomplishments I'm proud of

  • The whole product loop closes live on Reddit. Mod sees risk on the dashboard → taps lockdown → spam gets auto-removed → "Hours saved" ticks up → 30 minutes later, auto-unlock fires. Every step is verified end-to-end on a private test subreddit, not just unit-tested.
  • Zero configuration. No external services, no API keys, no settings form for v1. Install, get a working dashboard, calibrate in 4 hours.
  • Conservative "Hours saved" math. 30 seconds per blocked item is deliberately low — that conservatism is itself a credibility signal. The number is checkable.
  • Defensive state. If the scheduled unlock job ever misses its window (Devvit outage, scheduler bug), getLockdownState returns active: false automatically. No permanent locks are possible.
  • Strict TypeScript throughoutexactOptionalPropertyTypes, noUncheckedIndexedAccess, no-floating-promises, no as casts in domain code.
  • Iterated four times in 48 hours in response to real-world feedback. Public v0.0.3 → v0.0.8 shipped during the hackathon window: v0.0.5 README polish for App Directory audience; v0.0.6 fixed a real-user-reported bug (SampleOfNone — dashboard self-removal during lockdown) within an hour of the report; v0.0.7 addressed an official r/Devvit team compliance flag on mod-permission handling with server-side isModerator() gating, restricted-view sanitization, and comprehensive Privacy & data + Permissions README sections; v0.0.8 hardened the mod-check itself after testing surfaced an edge case in Reddit's server-side username filter. Real users, real feedback, real fixes — all within the content period.

What I learned

  • Statistical anomaly detection beats fixed thresholds for "what's normal for this sub" — a single hard threshold cannot serve a 5k-member fandom sub and a 5M-member news sub.
  • Devvit's Redis surface is rich enough (sorted sets, hashes, atomic increments) to implement non-trivial signal storage without external infra.
  • Quantifying invisible mod labor as "Hours saved" turns silent moderation work into a number mods can quote to their communities. That framing is itself a feature.

What's next for Raid Radar

  • Configurable thresholds — per-sub tuning of z-score sensitivity, new-account window, lockdown duration
  • Modmail / modnote alerts — push to the mod team when the alert level changes, not only on the dashboard
  • Per-incident reports — a postmortem view for each handled raid, with a shareable summary link
  • Multi-sub coordination — opt-in shared threat intel across allied subs

If Raid Radar finds an audience after the hackathon, it should qualify for the Reddit Developer Funds program — a path to keeping the lights on past the submission deadline.

How we built it

Challenges we ran into

Accomplishments that we're proud of

What we learned

What's next for Raid Radar — Live raid detection & one-tap lockdown

Built With

Share this project:

Updates