StrikeMod — Moderation with memory.
Progressive discipline for Reddit moderators. One right-click. AI-drafted reason. Automatic consequences.
Inspiration
Every Reddit moderator knows the moment: a user breaks a rule, you remove their post, write a removal reason from scratch, check whether they've done this before (they have — you just can't remember when), and then manually decide whether this warrants a warning, a temp ban, or nothing. Then you do it again tomorrow. And the day after.
The real pain isn't the single action — it's the accumulated invisible labour that surrounds it. Mods track repeat offenders in Google Docs, shared notes, or just memory. New mods on the team have no context on a user's history. Removal reasons get copy-pasted from the same tired template. Bans that should happen automatically don't, because nobody has the time to cross-reference.
I wanted to fix the part of moderation that should have been automated years ago: progressive discipline with a paper trail.
The second spark came from watching how AI is changing writing workflows everywhere except moderation tools. Every removal reason a mod writes is based on the same inputs — the content of the violation, the subreddit's rules, how many times this has happened before. That's a perfect prompt. Claude can draft a better, more personalised removal reason in two seconds than most mods write in two minutes.
Those two problems — missing discipline memory and repetitive writing — became StrikeMod.
What it does
StrikeMod adds a progressive discipline pipeline to any subreddit, installable in one click from the Devvit App Directory.
Moderators right-click any post or comment and select ⚡ Issue Strike. StrikeMod then:
- Fetches the violating content and the subreddit's rules
- Calls the Claude API to draft a personalised removal reason based on the actual content of the violation
- Shows the mod a pre-filled form — fully editable before confirming
- On confirmation: removes the content, sends a modmail to the user, and executes the appropriate action
The action is determined by configurable thresholds:
$$ \text{action} = \begin{cases} \text{warning modmail} & \text{if } n \geq t_{\text{warn}} \ \text{temporary ban} & \text{if } n \geq t_{\text{temp}} \ \text{permanent ban} & \text{if } n \geq t_{\text{perm}} \end{cases} $$
Where $n$ is the user's active strike count (after TTL expiry) and $t_{\text{warn}}, t_{\text{temp}}, t_{\text{perm}}$ are all configurable per subreddit via the app's Settings panel.
Strikes expire after a configurable TTL (default: 90 days), so a user who behaves for three months starts fresh. Mods can open the StrikeMod Dashboard — a custom Devvit post — to see all users with strikes, view the full history for any user, and pardon individual strikes with one click.
How I built it
The stack is entirely within the Devvit ecosystem, with one external call:
| Layer | Technology |
|---|---|
| Platform | Reddit Devvit (TypeScript) |
| Storage | Devvit Redis — key pattern: strikes:{sub}:{user} |
| UI | Devvit Blocks — custom post dashboard + context menus |
| AI | Anthropic Claude API (claude-sonnet-4-5) |
| Auth | Devvit Settings — API key stored as isSecret |
The data model is intentionally flat. Each user's strike record is a single JSON blob in Redis:
strikes:{subredditName}:{username} → UserStrikeRecord { strikes[], totalCount }
strikeindex:{subredditName} → string[] (list of all struck usernames)
The index set enables the dashboard to list all struck users without scanning every possible Redis key.
The AI integration passes three things to Claude: the full text of the violating content, the subreddit's rule list (fetched live via reddit.getSubredditRules()), and the user's current strike number. The prompt is engineered to produce a 2–4 sentence message that is firm but constructive — not hostile. Mods always see and can edit the draft before it sends.
The progressive logic runs on every strike action: expired strikes are pruned first (respecting TTL), then the new count determines which threshold is crossed. The three code paths — warn, temp ban, perm ban — all share the same executeStrike() function, branching only at the Reddit API calls.
The dashboard is a Devvit custom post using useState with async initialisers that load from Redis. It renders a two-view UI: a user list and a per-user detail view with pardon buttons, all without any external backend.
Challenges
The http: true trap. Devvit requires you to explicitly opt into external HTTP calls in Devvit.configure(). Without it, fetch() fails silently — no error, just an undefined response. This cost me an entire evening before I found the one-line fix. I've since added it as a comment in every config block I write.
Redis and the missing index. Devvit Redis doesn't support key scanning or pattern matching (KEYS strikes:* doesn't exist). To build the dashboard's user list, I had to maintain a separate index set manually — every time a new user receives their first strike, their username gets appended to strikeindex:{subredditName}. It's a simple pattern but easy to forget one of the two write paths.
Devvit Blocks layout model. The layout engine for custom posts is declarative and constrained — no CSS, no flexbox, no absolute positioning. Building the dashboard's two-view navigation (list → detail → back) required learning how useState and conditional rendering work within Blocks, which is different enough from React that several hours went into getting the pardon flow to update correctly without stale state.
Rate limiting the AI call. The Anthropic API call happens before the form is shown, which means the mod sees a "Drafting removal reason…" toast before the form appears. On slow connections or under API rate limits, this felt broken. The fix was adding a 5-second timeout with a graceful fallback to a template reason, so the flow never blocks.
Testing moderation actions on yourself. To test the ban path, you need a user account that isn't a moderator. Creating a second Reddit account purely for testing, moderating your test subreddit from account A, and getting banned from account B via StrikeMod — it works, but it is a deeply strange experience.
What I learned
- Devvit's Redis is more capable than it looks — but you have to design your data model around its limitations upfront, not as an afterthought.
- AI-generated text needs a human review step — always showing the mod the draft before sending, rather than auto-sending, was the right UX call. It makes the AI feel like a collaborator rather than an autocrat.
- The best moderation tools are invisible — StrikeMod's goal is that mods stop thinking about the mechanics of discipline and start thinking about their communities. A tool that gets out of the way is harder to build than one that demands attention.
- Context menus are underused in Devvit apps — adding
⚡ Issue Strikeas a right-click on posts and comments puts the action exactly where the mod already is, at zero navigation cost. More Devvit apps should live in context menus.
What's next
- Mod team audit log — a dedicated view showing which mod issued which strike, for accountability across large teams
- Strike reason categories — tag strikes by rule number so mods can filter the dashboard by rule type
- AutoModerator sync — trigger strikes automatically when AutoModerator removes content, not just on manual mod action
- Cross-subreddit shared blocklists — opt-in network of subreddits that share perm-ban decisions for the worst repeat offenders
Built for the Reddit Mod Tools & Migrated Apps Hackathon — May 2025. Platform: Devvit | AI: Anthropic Claude API | Storage: Devvit Redis
Built With
- claude-api
- devvit
- jsx
- redis
- typescript
Log in or sign up for Devpost to join the conversation.