All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
- Email logo now loads from a remote URL instead of base64 — email clients (Gmail, Outlook) strip data: URI images
- Domains empty state copy updated to point to the new "+ Add Domain" button
- Express
trust proxyset to 1 so express-rate-limit reads the correct client IP behind Nginx/Apache
- Email notifications — per-domain SMTP configuration in Site Settings. Admins receive alerts on new comments and replies to their own comments. SMTP credentials are encrypted at rest using AES-256-GCM via a server-side
ENCRYPTION_KEY. - Commenter reply notifications — when a visitor leaves an email address and someone replies to their comment, they receive a notification automatically.
- Email preview — preview all three email templates (new comment, reply to admin, reply to commenter) directly from the dashboard in a modal with desktop/mobile width toggle.
- Global comment search — search bar inside the Comments section searches name, email, content, and post URL across all domains.
- Configurable server bind address —
HOSTenvironment variable (default127.0.0.1) controls which interface the server listens on. ENCRYPTION_KEYandAPP_URLenvironment variables documented in.env.example.
- Admin inbox — full comment content shown (no truncation). Cards reuse the same avatar, mail-icon tooltip, and action controls as the thread view (DRY). Only "Pending" badge shown; "Approved" badge removed as redundant.
- Inbox tabs — simplified to All and Pending. Approved tab removed.
- Domains page — domain list moved to the top; Add New Domain form moved to the bottom.
- Site Settings — sections reorganised into horizontal tabs (General, Appearance, Anti-spam, Notifications, Embed). Save/Cancel row anchored at the bottom with a separator.
- Admin sidebar — wider (240 px), more generous item padding, larger icons.
- SMTP password field — shows masked saved state with a "Change password" link; real input only revealed on demand.
- Email templates — logo embedded as base64 (no remote load), CTA links replaced with button-styled anchors, footer split into per-audience copy.
Set ENCRYPTION_KEY in your .env before entering SMTP credentials in the dashboard — see docs/upgrading.md.
- Same-origin commenting from the admin dashboard —
getDomainOrErrornow falls back to theHostheader when noOriginheader is present. - Admin dashboard commenting — accepts
domain_idfrom the POST body when a valid admin JWT is present, enabling the admin to comment from the thread view.
- Zero-config embed — server URL is now inferred from the
client.jsscriptsrcautomatically. No JavaScript configuration block required. Embed is now just two lines: a<div>and a<script>tag. - Stable thread keys — widget resolves
postUrlin priority order:data-urlattribute →<link rel="canonical">→window.location.pathname. Sites using canonical tags (Ghost, Hugo, Jekyll, WordPress) get stable thread keys across slug changes automatically at zero config. data-urlattribute — power users can pin a thread to an explicit stable key:<div id="discuss-comments" data-url="/posts/123">.- Admin inbox actions — inbox comment items now show the full action set: Approve/Unapprove, Pin/Unpin, inline Edit, Delete — matching the threaded post view.
- Clickable post URLs — post URLs in the inbox and posts table are now clickable links.
- "View thread →" link — each inbox item links directly to the threaded comment view for that post within the admin.
- Tailwind v4 / KDS migration — replaced inline Tailwind v3 config with
@karthikeyankc/ken-design-system(local npm package, Tailwind v4). Deletedglobals.css,tailwind.admin.config.js,tailwind.client.config.cjs,src/design-system/tailwind.config.js. - Admin UI KDS compliance — replaced all hardcoded
text-neutral-*colour classes with KDS semantic tokens (--t1–--t5). Section headings, field spacing, help text, and label weights now follow KDS spec. - Widget scoped CSS reset — added a scoped reset for
button,input,textarea,selectinside#discuss-comments, stripping browser-native appearance, box-shadow, and outline. Fixes raised/3D button appearance on host pages without a CSS reset. - Post button KDS compliance —
.discuss-btnnow has proper base styles:inline-flex,height: 40px,border-radius,font-weight: 500, focus ring, active scale transform — matching KDS.btnspec. - Placeholder consistency —
::placeholderreset setsopacity: 1to fix Firefox's 54% default opacity on placeholder text. - Inbox typography consistency — excerpt, date, and avatar initials font sizes now match the threaded widget view.
currentScriptdetection — switched fromscripts[scripts.length-1]todocument.currentScriptfor reliable script origin detection withasync/defer.
- Security:
sanitize-html— updated to 2.17.4 (critical XSS viaxmpraw-text passthrough, GHSA-rpr9-rxv7-x643). - Security:
express-rate-limit— updated to 8.5.2 (moderate XSS inip-addressAddress6 HTML methods, GHSA-v2v4-37r5-5v8g). - Security:
postcss— updated to 8.5.15 (arbitrary file read via user-generated CSS, fixed in 8.5.12). better-sqlite3— updated to 12.10.0 (SQLite 3.53.1, Node.js 26 prebuild support).- Removed personal email address from
SECURITY.md, replaced with GitHub issue link.
- Rate limiting on admin login (10 attempts per 15 minutes)
- Content-Security-Policy and Referrer-Policy security headers
JWT_SECRETenvironment variable required in production — server refuses to start without it- Centralised config module (
src/config.js) .env.examplewith all supported environment variablesSECURITY.mdwith vulnerability disclosure policyCONTRIBUTING.mdwith dev setup, conventions, and PR guidelinesCHANGELOG.md- Dependabot config for weekly dependency updates
- GitHub issue templates and PR template
- CI workflow (
npm audit, module import smoke test)
- Hardcoded JWT secret fallback removed
- JWT secret deduplicated into a single config module
- Anonymous commenting with name and email
- Comment threading (nested replies)
- Comment moderation via admin dashboard
- Spam protection: honeypot hidden field and configurable question
- Gravatar avatars with initials fallback
- Markdown rendering (bold, italics, lists, inline code, code blocks) with server-side HTML sanitisation
- Admin dashboard:
- Login / logout with JWT stored in HttpOnly cookie
- Overview stats (total, pending, approved, domain count)
- Comments inbox with tabs (all / pending / approved) and bulk approve/delete
- Add and delete domains
- Per-domain settings: name, brand colour picker with WCAG AA/AAA + APCA indicators, spam trap label, blocked words
- Embed code snippet with one-click copy
- Posts view and per-post comment moderation
- User avatar (initials) with sign-out dropdown
- Dynamic CORS: allowed origins are stored in SQLite and checked per request
- Security headers: X-Content-Type-Options, X-Frame-Options, HSTS, CSP, Referrer-Policy
- Argon2id password hashing
- SQLite WAL mode, busy timeout, and foreign key constraints enabled