One site needs a contact form, the next wants comments under posts, the third has old URLs to redirect. None of that is a content model — but building the same scaffolding every time, storage, spam protection, admin view, is wasted effort. RustyCMS ships these as plugins: optional, compiled in per feature, each with its own admin page.
Compiled in, not loaded later
CARGO_FEATURES=comments,forms — and the binary contains exactly those two, admin UI included. What was not built is not there: no code in the bundle, no sidebar entry, no route. GET /api/plugins tells the frontend what is active. No dlopen, no WASM, no marketplace — RustyCMS stays one binary.
The same principle carries the backends underneath: asset storage and version history sit behind narrow interfaces. The filesystem runs with no configuration; version history already has a second backend for database mode. An environment variable picks which one runs — not a rewrite.
Comments with layered spam protection
Visitors comment without an account — an anonymous session cookie lets them edit or delete their own comment later. In front of that sit several filters: rate limit per IP, cooldown per session, duplicate window, honeypot, minimum page age (bots post in zero milliseconds), IP blocklist. With RUSTYCMS_COMMENTS_REQUIRE_APPROVAL=true every new comment enters the queue; the admin page approves, rejects, deletes.
POST /api/comments/my-article
{ "author": "Max", "body": "Great piece!", "honeypot": "", "client_age_ms": 5000 }
GET /api/comments/counts?ids=article-1,article-2 # counts for the index page
Forms whose submissions live in the CMS
POST /api/forms/contact/submit accepts any JSON object and stores it. No schema needed — the form exists with its first submission. Protection: rate limit per IP, body limit, honeypot field, origin allowlist and an allowlist of permitted form IDs (RUSTYCMS_FORMS_ALLOWED_IDS=contact,feedback) so nobody invents forms of their own. The admin page lists submissions per form with a new/read status.
Redirects the frontend picks up
Send old paths to new ones, 301 or 302, switchable per entry. Editors maintain the list in the admin UI; GET /api/redirects is public and returns only active entries — the frontend loads them at startup and checks every request against them.
A wiki that is a collection
Hierarchical Markdown pages with a tree, drag-to-reorder, wiki links ([[slug]]) and embeds ({{embed:slug}}). The plugin adds no routes of its own — it is a view onto the wiki_page collection. Versioning, search, validation, export: whatever applies to content applies here.
Deliberately left out
No plugin marketplace, no loading of third-party code at runtime. A plugin is Rust code that gets built with the rest. That is the price for a deployment containing exactly what you built — and nothing else.