<- All projects

mod-oud

A self-hostable Discord moderation bot with an accompanying web dashboard. 28 features, blazingly fast, and scalable.

RustSerenityAxumSQLxPostgreSQLRedisNext.jsTypeScriptTailwind CSSDocker
3 min readSource Live
mod-oud

What it is

A self-hostable Discord moderation bot written in Rust, with a companion web dashboard in Next.js. The bot serves its own HTTP API, and the dashboard talks to that API and to Postgres directly.

The point of the project was to replace a pile of small Discord bots with one binary and one dashboard that a single operator can actually run and reason about.

Architecture

One Rust binary runs the gateway bot, the web server, or both, selected by env flags:

Shell
RUN_BOT=true
RUN_WEB=true
RUN_MIGRATIONS=true

Sharding

Gateway sharding is handled by index and count, so each shard is its own container sharing Postgres and Redis:

Shell
SHARD_INDEX=0
TOTAL_SHARDS=2

Concurrency without double-execution

Background and cron jobs take distributed Redis locks before doing work, so N replicas never run the same job twice. This is the main reason Redis is a hard dependency rather than a cache.

No inbound ports

Production sits behind a Cloudflare Tunnel, so the host exposes nothing publicly. The compose file that ties it together is docker-compose.prod.yml; docker-compose.multi.yml covers spreading many guilds across several hosts.

Feature slices

Every feature is a vertical slice under src/features/ — 28 of them:

automod         bad_words       birthday        custom_commands
economy         gambling        giveaways       invite_tracking
join_leave      leveling        live_feed       media_only
member_counter  message_logging moderation      music
raid_detection  reaction_roles  reminder        reporting
search          starboard       temp_voice      tickets
verification    warning         ...

Two categories:

  • Moderation & safety — automod, bad-word rulesets, raid detection, warnings, moderation actions, media-only channels, human verification (Turnstile / hCaptcha), reporting, message logging, invite tracking.
  • Community — leveling with rewards and multipliers, starboard, giveaways, birthdays, reaction roles, custom commands, reminders, member counters, join/leave messages, temp voice hubs, tickets, live feed, search integrations (Spotify, YouTube, Genius, GIPHY, TMDB, RAWG), and music playback via Songbird.

The convention is strict: code lives inside the feature that owns it, and nothing is shared until three features genuinely need it.

Compile-time SQL

Queries use SQLx with offline metadata. .sqlx/ is committed, so the project compiles with no database running:

Shell
cargo build --release   # no live DB required

Migrations run automatically on startup — 38 of them so far. Every query is compile-checked, so a typo against the schema is a build failure rather than a runtime surprise.

The dashboard

A separate Next.js app, 32 routes, talking to the bot's API. Its Server Actions reach Redis for cache invalidation and Postgres for config.

Markdown is rendered with react-markdown plus KaTeX for math, Prism for syntax highlighting, and rehype-slug for heading anchors:

TSX
import { MarkdownWithToc } from "@/components/ui/markdown/MarkdownWithToC";

<MarkdownWithToc content={post.body} />

(The exact same architecture that my portfolio site uses)

Code fences get a language label resolved from a generated GitHub Linguist extension map, and a copy button.

Linting as design pressure

The Rust side runs pedantic and nursery Clippy lints, with missing_docs and rustdoc::all denied:

TOML
[lints.rust]
missing_docs = "deny"

[lints.clippy]
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }

missing_docs = "deny" is the strict one. Every public item needs a doc comment or the build fails. It sounds fussy until you try reading unfamiliar code six months later.

By the numbers

Rust source files449
Rust lines~56,400
Dashboard TS/TSX files478
Dashboard lines~47,800
Feature slices28
Dashboard routes32
Migrations38
Commits297
EditionRust 2024