Context
Comments are being made persistent + moderated (see branch feat/dashboard-tags-comments). The current implementation lets readers submit anonymously — no auth, and authentication is not required. That is a deliberate product stance (low friction, more engagement), but it needs a proper design so open submission stays trustworthy.
This issue is to think through how to do anonymous comment submission responsibly — not to force logged-in accounts.
Risks with open/anonymous submission
- Spam / bots — automated junk.
- Impersonation — anyone can type any display name (including the blog author's or a known person's).
- Abuse — offensive content with no accountability.
- Moderation load — if every comment needs a human, it doesn't scale.
- Privacy — collecting email from strangers is PII we may not want to store.
None of these require login to mitigate — they're design levers we can dial.
Design levers
Moderation model
- All comments
pending → admin approves (current behaviour). Safest, but every comment needs review.
- Auto-publish + report/remove after the fact. Lower friction, riskier.
- Trusted fast-lane: first comment moderated; once approved, a signed cookie/token lets that commenter's later comments auto-publish. Good middle ground for a small blog.
Spam defense (all no-auth, low-friction)
- Honeypot field + minimum time-to-submit (kills naive bots for free).
- Rate limit per IP / session; store a hashed IP, not raw.
- Lightweight content heuristics (link count, keyword / URL blocklist).
- Optional invisible captcha (Cloudflare Turnstile / hCaptcha) for a hard wall — near-zero user friction.
Identity without accounts
- Email optional, never displayed — used only for optional reply notifications / Gravatar.
- Reserve author/admin display names so nobody can impersonate the author; label everyone else "guest".
- Keep the door open: if a user happens to be signed in (admin today, readers later), attribute + auto-approve — but never require it.
Proposed baseline
Anonymous allowed · name required, email optional · comments moderated by default with a trusted fast-lane after first approval · spam-hardened (honeypot + min-time + hashed-IP rate limit + link heuristics) · author display name reserved · hashed IP for abuse control, minimal PII. Add Turnstile only if a strong wall is wanted.
Open decisions
Known gaps in the current implementation (to fix as part of this)
- The public comment form requires email but the backend RPC treats email as optional — mismatch to reconcile.
- No spam hardening yet (no honeypot, rate limit, heuristics, or captcha).
- No impersonation guard (author display name is not reserved).
- No abuse controls (no IP handling, no per-post disable).
Current state (for reference)
- Migration
supabase/migrations/20260717000200_blog_comments.sql: blog_comments table (status pending/approved/spam), RLS locked to RPCs, public submit + approved-list, admin list/counts/set-status/delete.
- Public UI wired in
src/features/comments/*; admin moderation queue at /dashboard/comments.
Once the four decisions above are settled, the rework touches the migration (spam fields, hashed IP, trusted token), the RPCs (spam checks + fast-lane), and the form/UI.
Context
Comments are being made persistent + moderated (see branch
feat/dashboard-tags-comments). The current implementation lets readers submit anonymously — no auth, and authentication is not required. That is a deliberate product stance (low friction, more engagement), but it needs a proper design so open submission stays trustworthy.This issue is to think through how to do anonymous comment submission responsibly — not to force logged-in accounts.
Risks with open/anonymous submission
None of these require login to mitigate — they're design levers we can dial.
Design levers
Moderation model
pending→ admin approves (current behaviour). Safest, but every comment needs review.Spam defense (all no-auth, low-friction)
Identity without accounts
Proposed baseline
Anonymous allowed · name required, email optional · comments moderated by default with a trusted fast-lane after first approval · spam-hardened (honeypot + min-time + hashed-IP rate limit + link heuristics) · author display name reserved · hashed IP for abuse control, minimal PII. Add Turnstile only if a strong wall is wanted.
Open decisions
Known gaps in the current implementation (to fix as part of this)
Current state (for reference)
supabase/migrations/20260717000200_blog_comments.sql:blog_commentstable (statuspending/approved/spam), RLS locked to RPCs, public submit + approved-list, admin list/counts/set-status/delete.src/features/comments/*; admin moderation queue at/dashboard/comments.Once the four decisions above are settled, the rework touches the migration (spam fields, hashed IP, trusted token), the RPCs (spam checks + fast-lane), and the form/UI.