# LightCMS Version History

> Complete version history and changelog for LightCMS, the AI-native open-source content management system. Track new features, improvements, and fixes across all releases.

Source: https://metavert.io/lightcms-version  
Published: 2026-10-05  
Updated: 2026-10-05

# LightCMS Version History

All notable changes to [LightCMS](https://metavert.io/lightcms), organized by version. Source: [CHANGELOG.md](https://github.com/jonradoff/lightcms/blob/main/CHANGELOG.md)

---

## v7.3 — SEO & AI

See how AI systems read your site, decide what they may do with it, and give them clean, well-attributed content (v7.3.0–v7.3.4).

### Added

- **AI traffic analytics:** A new AI view in Analytics shows which AI crawlers read your site, split by purpose: model training (GPTBot, ClaudeBot, …), AI search (OAI-SearchBot, PerplexityBot, …) and user-initiated fetches (ChatGPT-User, Claude-User, …). It also lists the pages AI reads most and how many visits ChatGPT, Perplexity, Claude, Gemini and Copilot send you, including visits that arrive with a `utm_source` tag instead of a referrer. A summary is included in CMS Agent digests.
- **AI crawler controls:** Allow or block AI training, AI search and user fetches separately, with per-crawler overrides and an optional Content-Signal line, all rendered into robots.txt. Google and Bing search are untouched unless you say otherwise.
- **Markdown copies:** Every page is also available as clean Markdown at `/<page>.md`, linked from the page and from llms.txt, so AI agents can read it without the HTML. The copies are kept out of search results.
- **Richer structured data:** Authors (site default plus per-page override), publisher logo and profiles, breadcrumbs, FAQ markup from explicit FAQ sections, and “last modified” dates that only change when the page content actually changes.
- **RSS & Atom feeds:** `/feed.xml`, `/atom.xml` and per-collection feeds with full content.
- **Hide from search & AI:** A per-page switch that adds noindex and removes the page from the sitemap, llms.txt, feeds and IndexNow. The page stays visible to visitors.
- **Tools & API:** A new SEO & AI settings screen, a REST API, and four MCP tools (128 total).

### Fixed

- **IndexNow from the editor:** Unpublishing, moving or deleting a page in the admin editor now notifies search engines, as API and MCP edits already did.
- **Canonical links** are now absolute URLs, and Markdown copies keep spaces between adjacent links (v7.3.1).
- **Published dates:** Pages created already-published through the API, MCP, bulk tools or imports now get a published date, which structured data and feeds rely on. A one-time backfill tool (API and MCP, with a dry-run option) gives older pages their creation date (v7.3.2, 128 MCP tools).
- **Sitemap freshness:** sitemap.xml now refreshes itself when it’s more than 10 minutes old. Before, only edits made in the admin screens updated it, so pages managed through the API or MCP could go missing or linger after removal (v7.3.3).
- **Homepage safety:** Deleting an old page with an empty path no longer removes the homepage’s generated file (v7.3.4).

---

## v7.2 — The CMS Agent & IndexNow

A built-in agent that reports on your site, plus instant search-engine notification (v7.2.0–v7.2.5).

### Added

- **IndexNow (v7.2.3–v7.2.5):** When you publish, edit, move, unpublish or delete a page, LightCMS tells Bing, Yandex, Seznam, Naver and other [IndexNow](https://www.indexnow.org) search engines right away instead of waiting for a recrawl. No setup needed: each site generates its own key, confirms it really serves its public URL before submitting anything, and submits the whole site once when IndexNow first switches on. Edits are batched, unchanged re-renders are skipped, and each URL is resubmitted at most once every 10 minutes. Status and submission history are under Tools → IndexNow, with three new MCP tools (124 total).
- **CMS Agent (v7.2.0):** Your site’s built-in agent, starting with configurable email digests: site health, traffic, the review queue (forks, approvals, scheduled posts), agent activity, broken links, and optional AI commentary from Claude. Delivered through Resend; daily, weekdays or weekly at the hour you choose.

### Fixed

- **Sitemap:** sitemap.xml no longer lists deleted pages or sandbox fork copies, and builds faster on large sites (v7.2.3).
- **IndexNow follow-ups:** Unknown `.txt` URLs return 404 instead of the homepage (v7.2.4), and changes made while a search engine is still verifying a new site key are retried instead of dropped (v7.2.5).
- **CMS Agent feedback:** Clear success and failure banners after sending a test digest or saving settings (v7.2.1).
- **Analytics:** The trend KPI card resets correctly when switching visitor tabs, and edit-link lookups use one lightweight query instead of three full-document loads (v7.2.2).

---

## v7.1 — The Ambient Copilot

The copilot becomes a slide-in drawer on every admin page (v7.1.0–v7.1.1).

### Added

- **Ambient copilot:** A floating button on every admin page opens a slide-in AI copilot — edit, create, and publish content in plain language without leaving your current screen. Fullscreen toggle, ChatGPT-style vertical chat history with full-text search, animated typing indicator with progressive status, and markdown tables rendered as styled tables.
- **Copilot analytics & site health:** Ask for your most popular pages, top referrers, traffic summaries, or the daily maintenance report (stale pages, missing meta descriptions, lingering drafts).

---

## v7.0.x — Patch Train

Rapid follow-ups to the agentic release (v7.0.1–v7.0.9).

- **AI-search output:** Homepage schema.org WebSite JSON-LD with SearchAction, including raw-HTML homepages. llms.txt/llms-full.txt queries use projections — ~40s to ~1s on large sites.
- **Analytics ranges:** 60-day and 90-day presets plus custom date ranges across all analytics views.
- **Version visibility:** The admin sidebar shows the running software version.
- **Fixes:** Copilot CSRF token double-quoting; agent-session rollback now selects targets by version provenance (a session’s own versions are never rollback targets), eliminating a timestamp race.

---

## v7.0.0 — The Agentic Release

AI agents become first-class, governed users of the CMS. 115 MCP tools total.

### Added

- **Agent sandbox (“pull requests for content”):** Agents work in copy-on-write fork sandboxes; publishing, deleting, and bulk operations are blocked inside a sandbox, and nothing reaches the live site until a human reviews the per-field diff and merges.
- **Agent governance:** API keys accept permission scopes and a server-enforced sandbox-only flag. Every agent session has an audit ledger and one-call rollback (created→soft-deleted, deleted→restored, updated→reverted to the pre-session version).
- **Change provenance:** Every content version records who authored it — human or agent, via which channel, and which session.
- **llms.txt + JSON-LD:** Auto-generated llms.txt and llms-full.txt for AI crawlers; schema.org structured data (BlogPosting/NewsArticle/WebPage) on all published pages.
- **Public read-only MCP:** An unauthenticated MCP endpoint at /mcp-public lets visitors’ agents search and read the published site natively. Drafts and forks are structurally excluded.
- **Local embeddings:** LIGHTCMS_EMBEDDINGS_PROVIDER=ollama generates semantic-search embeddings locally — no hosted API or per-call credits.
- **Self-maintaining sites:** Daily maintenance scans surface stale pages, missing meta descriptions, and drafts as an agent-ready work queue via API and MCP.
- **Module path:** Now github.com/jonradoff/lightcms/v7 (semantic import versioning).

---

## v6.1.1 — Security Hardening

Comprehensive security review and fixes for bulk operations and MCP features.

### Security

- **Search/replace input validation:** Pairs array capped at 100 per request. Search and replace text capped at 100K characters. Regex patterns capped at 1,000 characters to prevent ReDoS.
- **Legacy API keys deprecated:** API keys without an associated user are auto-migrated to the first admin user on first use. Keys must have user context for permission enforcement.
- **Script policy at write time:** Bulk create and bulk update sanitize content data fields when script policy is admin_only or none, as defense-in-depth alongside render-time enforcement.
- **Bulk field operation validation:** System fields (_id, published, template_id, full_path, etc.) blocked from modification via bulk field operations.
- **Per-page locking:** Search/replace, bulk update, and bulk field operations use per-page mutual exclusion to prevent race conditions.
- **Error message sanitization:** Bulk operation errors no longer expose MongoDB internals. Detailed errors logged server-side only.
- **Enhanced audit logging:** Bulk create includes sample of created content IDs in audit trail.

---

## v6.1.0 — Bulk Operations, Analytics & Programmatic SEO

Major release focused on scale, observability, and bulk content management for programmatic SEO workflows.

### Added

- **Bulk Create Content:** New bulk_create_content MCP tool creates up to 100 pages per call with parallel HTML generation. Supports upsert mode for retry-safe operations.
- **Content Upsert:** create_content and bulk_create_content accept upsert: true — updates existing pages instead of failing on duplicates.
- **Multi-Pair Search & Replace:** Pass a pairs array to apply all replacements in a single O(pages) pass instead of O(pairs × pages).
- **Content List Pagination:** list_content supports limit (1-500) and offset with {items, total, has_more} envelope.
- **Burst Rate Limiting:** 20 requests/second per token, complementing the 300/min sliding window.
- **Site Analytics Dashboard:** Hourly visitor chart with hover tooltips, uptime monitoring strip, top pages by views, top referrers (Non-bot/Bots/All tabs), browser donut chart.
- **Per-Page Analytics:** View counts, referrer breakdown, and edit links for individual pages at /cm/analytics/page.
- **Referrer Drill-Down:** Click any referrer domain to see which pages received traffic from that source.
- **Analytics Tab on Content Editor:** 7d and 30d view counts plus top referrers inline on every content edit page.
- **Page View Tracking:** Buffered writes (30s flush) for page views, referrers, and user agents with zero performance impact.

### Changed

- **Search/Replace returns counts:** Responses include pages_scanned, pages_modified, total_replacements.
- **Conditional Republish:** SHA-256 content hashing skips unchanged pages during regeneration.

### Fixed

- **Analytics data not recording:** site_stats collection blocked by Atlas 500-collection limit; moved to user_activity sentinel pattern.
- **Analytics chart empty:** ISO time format mismatch between Go and JavaScript fixed with epoch-ms keys.
- **Buffer flush BSON error:** Null byte separator in page_refs keys rejected by BSON; changed to || separator.

---

## v6.0.1 — Security Hardening

Comprehensive security patch addressing 15 issues identified in a full security review of the v6.0.0 API surface.

### Security

- **CSRF key hardening** — CSRF protection key is now derived via SHA-256 hash of the session secret instead of zero-padding, eliminating low-entropy padding.
- **OAuth system key user context** — The system API key used for OAuth-authenticated MCP sessions is now associated with an admin user. OAuth sessions previously bypassed all permission checks via a nil-user path; they now properly resolve to admin-level RBAC.
- **20+ read-only API endpoints now require login** — All `/api/v1/` read endpoints (content, templates, assets, snippets, redirects, folders, collections, theme, config, comments) now require viewer-level credentials. Public end-user endpoints (`/api/search`, `/api/chat`, `/api/contact`) are unchanged.
- **Self-approval blocked** — Users can no longer approve their own content submissions.
- **Sequential workflow order enforced** — Only the approver assigned to the current step may advance a sequential workflow.
- **Workflow approver validation** — Approver user IDs are validated against real users when creating or updating approval workflows.
- **Comment rate limiting & length cap** — `POST /api/v1/content/{id}/comments` is rate-limited to 20 req/min per token; text capped at 10,000 characters.
- **Comment XSS fix** — `user_display_name` and `user_email` are HTML-escaped before injection into dynamically created comment DOM nodes and mention dropdowns.
- **Bulk export gated** — `APIExportContent` now requires `content.edit` permission.
- **Config endpoint auth & token redaction** — `GET /api/v1/config` requires `settings.view` permission; Cloudflare API token is redacted in the response.
- **Force-unlock RBAC** — `APIForceUnlockContent` now uses `requirePermission` instead of a hardcoded role string check.
- **Sidebar badge efficiency** — Approval badge count now uses `CountPending()` (single O(1) query) instead of loading the full pending result set on every admin page render.

---

## v6.0.0 — Content Approvals & Discussions

Adds configurable approval workflows for content publishing, a new Contributor role, and inline threaded discussion threads on every content item.

### Content Approvals

- **Approval workflows** — Configurable trigger-based workflows by contributor, folder path, template ID, or tag. Supports sequential mode (approvers in order) and concurrent mode (any N-of-M).
- **Approvals dashboard** at `/cm/approvals` — Shows “My Queue” and “Other Pending.” Approve/reject with a required rejection comment. Badge count in sidebar nav.
- **Contributor role** — New RBAC role between Viewer and Editor. Contributors can create content and upload assets (held pending), submit for approval, and post discussion comments — but cannot publish directly.
- **Dashboard integration** — “Requiring Approvals” section and “Recent Comments” section on the admin dashboard.

### Content Discussions

- **Discussion tab** — Inline threaded comment section at the bottom of every content edit page. Live badge count on the tab.
- **@mention autocomplete** — Type `@` in a comment to search and mention other users.
- **Rejection comments** — When an approval request is rejected, the rejection comment is auto-posted to the content discussion thread.
- **Tabbed bottom section** — Discussion, Version History, and Forks are now organized into tabs on the content edit page.

### New MCP Tools (14 added, 92 → 106 total)

- `list_comments`, `create_comment`, `delete_comment`
- `list_approval_workflows`, `get_approval_workflow`, `create_approval_workflow`, `update_approval_workflow`, `delete_approval_workflow`
- `list_approval_requests`, `get_approval_request`, `submit_for_approval`, `approve_request`, `reject_request`, `cancel_approval_request`

### Webhook Events

- Three new event types: `comment.created`, `content.pending_approval`, `asset.pending_review`
- Messages “Mark All as Read” button added to the contact messages list page

---

## v5.0.0 — Import Pipeline

Adds a unified content import pipeline with support for RSS/Atom feeds, Markdown files, and CSV data — with a live streaming job log and full MCP tool coverage.

### Import Types

- **RSS/Atom Import** — Configure recurring feed sources with schedule (hourly/daily/weekly), template mapping, folder targeting, and auto-publish. Full run history and per-job logs.
- **Markdown Import** — Upload single `.md` files or `.zip` archives. YAML frontmatter controls title, slug, folder, template, tags, and scheduling. Supports Notion, Obsidian, Hugo/Jekyll migrations, and AI-generated content.
- **CSV Import** — Upload CSV files and map columns to content fields. Specify the title column; all other columns become fields automatically.
- **Deduplication** — Imports match by `full_path` — re-importing the same slug updates rather than duplicating.
- **Real-time import status** — SSE-powered live log stream at `/cm/imports/{jobID}`.
- **Agentic bulk content creation** — `import_markdown` is specifically designed for AI agents to generate and import large batches of content in a single call.

### New MCP Tools (10 added)

- `list_import_sources`, `create_import_source`, `update_import_source`, `delete_import_source`, `trigger_import_source`
- `import_markdown`, `import_csv`, `list_import_jobs`, `get_import_job`, `cancel_import_job`

---

## v4.5.0 — Webhooks, Scheduled Publishing & Content Locking

Adds HMAC-signed webhook events, scheduled publishing, collaborative content locking, Cloudflare cache integration, edge caching headers, and structured JSON logging.

- **Webhooks** — HMAC-SHA256 signed webhook events for content lifecycle. Configurable per-webhook with retry logic and delivery history. Admin UI at `/cm/webhooks`.
- **Scheduled Publishing** — Set a future `publish_at` timestamp; background scheduler publishes automatically.
- **Content Locking** — Advisory lock when editing (30-minute expiry). Warning banner if another user is editing. Force unlock for admins.
- **Incremental Static Regeneration** — Template changes regenerate content in batches of 20, preventing server overload.
- **Edge Caching Headers** — ETag, Cache-Control, Last-Modified, and Vary headers on all public pages. 304 Not Modified support.
- **Cloudflare Integration** — Configure Zone ID and API Token to automatically purge Cloudflare cache on publish/unpublish.
- **Structured JSON Logging** — All server logs emit structured JSON with timestamp, level, message, and context fields.
- **Rate Limit Dashboard** — New tab on the audit log page showing locked IPs, attempt counts, and a clear button.
- **MCP Prompt Resources** — Three new MCP resources: `lightcms://site/structure`, `lightcms://content/recent`, `lightcms://theme/config`.

---

## v4.2.0 — AI Chat Widget, Security Hardening & Performance

Adds an embeddable AI-powered chat widget backed by hybrid semantic search, 8 new fork MCP tools (64 → 72 total), major security hardening, and a comprehensive performance overhaul.

### AI Chat Widget

- **Embeddable widget** — Add a floating AI chat bubble to any page with a single `<script>` tag. Self-contained JS widget fetches its config from `/api/chat/config` and streams answers from `/api/chat/query`.
- **Two-phase pipeline** — Each query runs hybrid semantic+fulltext search to retrieve relevant excerpts, then streams those through Claude Haiku for a conversational synthesized answer — reusing the same infrastructure as `end_user_search`.
- **SSE streaming** — Haiku token-by-token output forwarded live to the browser via Server-Sent Events. Falls back to plain JSON for non-SSE clients.
- **AI-optional mode** — Without an Anthropic API key, the widget still returns ranked search results with excerpts — a useful search-in-chat experience with no API cost.
- **Admin configuration** — New “Chat Widget” admin page controls title, welcome message, placeholder, color, position, max results, and editable system/user prompt templates with `{siteName}`, `{excerpts}`, `{question}` placeholders.
- **Dedicated rate limiting** — Separate per-IP (5/min) and global (30/min) limiters, independent from API and search limits. CORS restricted to configured `BASE_URL`.
- **Source attribution** — Every response includes a `sources` SSE event listing the content pages whose excerpts were used, with titles and paths.

### New Fork MCP Tools (8 added, 64 → 72 total)

- `list_forks` — list all forks with status and page count
- `create_fork` — create a named fork workspace
- `get_fork` — retrieve fork details including merge conflicts from last merge
- `fork_page` — add a page to a fork by content ID or path
- `remove_fork_page` — remove a page from a fork
- `merge_fork` — merge all fork changes into live content (admin only)
- `archive_fork` — archive a fork without merging
- `delete_fork` — permanently delete a fork and all its page copies

### Security

- **Prompt injection defense** — Chat query text wrapped in `<user_question>` XML delimiters before interpolation; `</` sequences escaped. Saved prompts validated to only allow known placeholders.
- **Configurable upload limit** — `MaxUploadBytes` in SiteConfig, settable via admin Configuration or `update_site_config`. Defaults to 1 MiB, enforced via `http.MaxBytesReader`.
- **API body size cap** — All `/api/v1/` endpoints enforce a 10 MiB request body limit, preventing memory exhaustion from oversized payloads.
- **Fly.io IP spoofing fix** — `TrustFlyProxy` mode reads the unspoofable `Fly-Client-IP` header instead of `X-Forwarded-For` which can be forged by clients.
- **Session secret entropy** — Production hard-fails on startup if `SESSION_SECRET` is shorter than 32 characters.
- **Per-endpoint rate limiters** — Dedicated limiters for regenerate (2/min), search-replace execute (10/min), bulk-update (5/min), export (5/min), reindex-embeddings (1/min).
- **Rate limiter map pruning** — Background goroutine prunes stale entries every 5 minutes, preventing unbounded memory growth.

### Performance

- **Admin template caching** — Templates compiled once at startup via `sync.Once`; previously re-parsed on every admin page request.
- **Content list pagination** — Admin Content page loads 100 items per page with Previous/Next controls; previously loaded the full collection into memory, causing OOM crashes at scale.
- **New content indexes** — `{updated_at: -1}` and compound `{fork_id, deleted, updated_at}` indexes fix `Sort exceeded memory limit` errors on Atlas free tier.
- **Search/replace streaming** — All four search/replace handlers stream documents via MongoDB cursor instead of loading the full collection. Complete coverage, O(1) memory.
- **Wikilink index cache** — 60-second TTL cache with explicit invalidation on publish/title/path changes; eliminates O(n) full collection scans per page on bulk publish.
- **Parallel hybrid search** — Full-text and semantic search now run concurrently, halving latency when both sources are enabled.
- **Parallel content regeneration** — `RegenerateAllContent` uses a 6-worker goroutine pool instead of sequential per-page processing.

---

## v4.1.0 — Bug Fixes

Fixes redirect deletion not taking effect immediately due to browser caching.

### Bug Fixes

- **Redirect deletion now takes effect immediately** — Redirects were served with `Cache-Control: max-age=3600`, causing browsers to cache redirect responses for up to an hour. Deleting a redirect had no visible effect until the browser cache expired. Changed to `Cache-Control: no-store` so browsers never cache redirect responses.

---

## v4.0.0 — Content Forks

Adds fork workspaces for safe page experimentation, bulk operation resilience improvements, and content search refinements.

### Content Forks

- **Fork workspaces** — Create isolated staging workspaces where sets of page changes can be authored, previewed, and reviewed before going live. Only touched pages live in a fork (sparse model — unmodified pages fall through to live).
- **Fork preview mode** — Activate via a floating bar injected into the live site. A `lc_fork_preview` cookie routes page requests through the fork, showing exactly how the site will look after merge.
- **Merge with conflict detection** — Admins merge forks into live content. If a live page was edited after the fork was created, the conflict is recorded (fork wins). Newly created fork pages are inserted as live content on merge.
- **“Fork to workspace” button** — Available on any content edit page; opens the forks list with a one-click “Add to this fork” action.
- **Fork exclusion from content list** — Fork copies are invisible in the main admin content list and search results.

### Performance & Resilience

- **Coalescing background workers** — `RegenerateIndexPages` and `RebuildKeywords` now use debounce channels: a bulk update of 25 items triggers one regeneration pass instead of 25 concurrent goroutines. Eliminates the goroutine explosion that caused server timeouts during large bulk operations.

### Content Search

- **Slug search mode** — New search option alongside Title Only and Full Text. Searches slug and full_path fields, sorted alphabetically by slug.
- **Homepage always first** — The site index page (`/`) is always pinned to position 0 in the content list and all search modes.
- **“/” query fix** — Searching `/` in Title or Full Text mode now correctly includes the homepage.

### Dashboard & Bug Fixes

- **Removed Collections KPI** — The Collections stat card has been removed from the admin dashboard.
- **Fork page unique index** — Replaced the single-field `full_path` unique index with a compound `{full_path, fork_id}` unique index, allowing fork copies to share paths with their live counterparts without constraint violations.

---

## v3.3.0 — Bulk Operation Performance

Significantly improves performance of all bulk and batch operations, with particular gains at scale (hundreds of content items).

### Performance Improvements

- **Scope filters pushed to MongoDB** — `bulk_field_operation`, `scoped_search_replace_execute`, and `export_content` now push template, category, folder, and content_ids filters down to MongoDB instead of loading all content and filtering in Go. For a 600-item site scoped to 100 pages, this eliminates ~500 unnecessary document transfers.
- **Goroutine worker pool (10 concurrent)** — All four bulk/search-replace execute handlers now process items concurrently with a pool of 10 workers instead of sequentially. On a 100-item batch this yields up to 8–10× faster wall time.
- **Batch content fetch in bulk_update_content** — Per-item `GetContent` calls replaced with a single `$in` query. 100-item bulk update: 100 DB round-trips → 1 for the initial fetch.
- **content_versions index on content_id** — Previously missing; every `UpdateContent` call triggered a full collection scan on `content_versions` for the version count. Now O(log n) per item.
- **template_name index on content** — Enables MongoDB-level template scope filters to use an index scan instead of a full collection scan.

### New Service Methods

- **ListContentScoped** — MongoDB-native scoped list with support for template_name, category, folder_path, and content_ids filters.
- **GetContentByIDs** — Batch-fetch multiple content items in one `$in` query, returning a map keyed by ID.

### MCP Tool Fixes

- **list_content include_data / include_fields** — Fixed a bug where enriched field data was fetched correctly by the API but silently dropped before being returned to MCP callers.
- **auto_republish on search & replace execute** — New boolean parameter on `search_replace_execute` and `scoped_search_replace_execute`. When true, pages that were published before the change are automatically re-published after replacement.
- **bulk_field_operation dry_run has_value** — Dry-run results now report whether each matching page currently has a non-empty value for the target field.

---

## v3.2.0 — Healthz Endpoint & DAU/MAU Analytics

Adds a structured health check endpoint following the VibeCtl Health Check Protocol, plus user activity tracking for DAU/MAU metrics surfaced in the health endpoint and admin dashboard.

### /healthz Endpoint

- **GET /healthz** — Unauthenticated structured JSON status: `status`, `name`, `version`, `uptime`, `dependencies` (live MongoDB ping), and `kpis` (DAU, MAU, pages created today)
- **HTTP 503 on unhealthy** — Returns 503 when status is `unhealthy`; 200 otherwise
- **Existing /health unchanged** — Plain-text `OK` endpoint for Fly.io TCP probes is preserved

### Analytics & KPIs

- **AnalyticsService** — New service backed by `user_activity` collection. Activity recorded on admin login and API key auth via fire-and-forget goroutine (no request latency impact)
- **Unique-per-day deduplication** — In-process `sync.Map` dedup prevents repeat DB writes for the same user on the same calendar day. Storage: at most one document per user per day with 90-day TTL auto-expiry
- **DAU / MAU** — Daily Active Users (distinct today) and Monthly Active Users (distinct last 30 days) via aggregation pipeline
- **Dashboard stat cards** — Three new cards added: Daily Active Users, Monthly Active Users, Pages Created Today

---

## v3.1.0 — Bulk Operations, Wiki-Like Markup & Security Hardening

First-class bulk content operations (eliminating N×1 API call patterns), a full wiki-like markup system for content authoring, configurable script policy, and comprehensive security fixes.

### Bulk Content Operations

- **bulk_update_content** — Update up to 100 content items in a single MCP/API call with merge semantics. Supports `clear_fields`, `dry_run`, and per-item success/error details
- **bulk_field_operation** — Apply a single operation (clear, set, prepend, append, wrap) to a field across all matching pages. Scoped by template, folder, category, or IDs. Supports dry_run
- **export_content** — Export content items with full field data as structured JSON. Designed for the export → transform → bulk_update pipeline
- **list_content with field data** — New `include_data` and `include_fields` parameters return full template field values in list results, eliminating per-item get_content calls
- **clear_fields on update_content** — Explicitly zero out fields, removing ambiguity about merge semantics
- **dry_run validation** — Validate update payloads without committing changes on both update_content and bulk_update_content

### Regex Search & Replace

- All four search/replace tools now accept `"regex": true` for Go RE2 regular expression matching
- Use `$1`, `$2` for capture group references in replace strings
- Input validation: 500-char pattern limit, max 20 capture groups, 10× expansion guard

### Wiki-Like Markup System

- **Wikilinks** — Page Title , display , /path syntax in any content field. Resolves to <a> at page generation time; broken links render as broken-link spans
- **Snippet includes** — include:snippet-name embeds a named snippet inline in any content field. Depth limit 3, cycle detection
- **Table of contents** — Add {{.lc_toc}} in a template layout to auto-generate a <nav class="lc-toc"> from page headings
- **Heading IDs** — All h1–h6 tags automatically get id= attributes for deep-linking
- **Markdown field type** — Set a template field type to `markdown` for GFM rendering (tables, strikethrough, task lists, autolinks)
- **Inline #tag detection** — Mentioning #tagname in any content field automatically tags the page for lc:query index pages
- **get_backlinks MCP tool** — Returns all published pages that wikilink or link to a given path. Useful before renaming or deleting pages
- **Version history attribution** — "By" column in version history shows the email of the editor who made each change

### Configurable Script Policy

- New site config `markdown_script_policy` controls who may use raw <script> tags in content
- `"all"` (default) — all users may use scripts; fully backward-compatible
- `"admin_only"` — editor-authored content sanitized via bluemonday; admin content passes through unchanged
- `"none"` — all content sanitized regardless of author role
- Configurable via admin Settings page or update_site_config MCP tool

### Security Fixes

- Export authorization: export_content now requires authentication
- Regex DoS protection: pattern length cap (500 chars), capture group limit (20), expansion guard
- Snippet recursion guard: depth limit 3 with cycle detection
- Version history permissions: get_content_versions and get_content_version now enforce PermContentView
- Folder path scope bug: scoped search/replace with /blog no longer matches /blog-old
- Bulk field op limit: 500-page cap prevents unbounded database writes per request
- Wikilink href safety: resolved links not starting with / render as broken-link spans
- TOC HTML escaping: heading IDs in TOC anchors are properly HTML-escaped
- Rate limiter cleanup: background goroutine prunes stale entries every 10 minutes

---

## v3.0.0 — Dynamic Index Pages: Tags, Snippets & `lc:query`

The most significant content modelling capability added since v1.0 — a first-class system for building dynamic, automatically-updated index pages.

### Content Tagging

- **Tags field on all content items** — Any content item can carry zero or more freeform string labels, set in the admin editor or via `{"tags": [...]}` in the REST API
- **Full API support** — `GET /api/v1/content?tag=TAGNAME` filters by tag; `PUT /api/v1/content/{id}` accepts a tags array
- Tags are exact-match strings with preserved capitalization, indexed in MongoDB for efficient filtering

### Snippets

- **New `snippets` collection** — Named HTML template fragments with Go template variable support
- **Admin UI** at `/cm/snippets` — Create, edit, and delete snippets with a live editor
- **REST API** — Full CRUD at `/api/v1/snippets`
- **Available variables** — `{{.Title}}`, `{{.FullPath}}`, `{{.Slug}}`, `{{.MetaDescription}}`, `{{.PublishedAt}}`
- Rendered through Go’s `html/template` — safe from XSS by default

### `lc:query` Directives

- **Embed live content queries in template layouts** — Using HTML comment syntax: `<!-- lc:query filter="tag:X" snippet="name" -->`
- Processed at page generation time, replaced with rendered output of all matching published content items
- **Filter options** — `tag`, `category`, `template`, `folder`
- **Sort options** — `title:asc`, `title:desc`, `created_at:asc`, `created_at:desc`
- Multiple directives per template — each is an independent query

### Automatic Cascade Regeneration

- **Auto-regeneration** — When tagged content is published or updated, all index pages whose templates contain `lc:query` directives are automatically regenerated
- Also triggers when a template layout or snippet is updated

### New MCP Tools (5 added, 54 → 59 total)

- `list_snippets` — list all snippets
- `get_snippet` — retrieve a snippet by ID or name
- `create_snippet` — create a new named HTML snippet
- `update_snippet` — update a snippet’s name or HTML
- `delete_snippet` — delete a snippet

### Security Fix

- **XSS in `lc:query` default fallback** — When no snippet attribute was specified, title and path were concatenated without escaping. Fixed to use `template.HTMLEscapeString()`

---

## v2.6.0 — MCP Tool Improvements

### Bug Fixes

- **`get_theme` returned empty strings** — `ThemeSettings` struct was missing `json` struct tags; all theme fields now serialize correctly via the REST API and MCP
- **`search_replace_execute` response missing search/replace fields** — Both `search_replace_execute` and `scoped_search_replace_execute` now echo back the `search` and `replace` strings used

### New MCP Feature: Rendered HTML in `get_content`

- `get_content` now accepts an `include_rendered` boolean parameter
- When `true`, the response includes a `rendered_html` field containing the fully rendered page HTML (template + theme header/footer applied)
- Works for both published and draft content — lets agents inspect exactly what visitors see without publishing

---

## v2.5.0 — Security Hardening

- **SSRF prevention** — `upload_asset_from_url` blocks all private/reserved IP ranges (loopback, RFC1918, link-local, CGNAT, IPv6 ULA) before making any network request
- **ReDoS prevention** — Full-text search runs under a 5-second context deadline, preventing pathological queries from pinning CPU
- **Permission checks** — Search-replace preview and reindex-embeddings endpoints now require admin-only permissions
- **API rate limiting** — Per-bearer-token sliding-window limiter: 300 requests per token per minute across the entire `/api/v1/` subrouter
- **Asset serve_path whitelist** — Upload paths restricted to `/assets/`, `/images/`, `/docs/`, `/media/`, or `/files/`
- **Trusted proxy IP extraction** — Rate limiter uses configured trusted proxy settings, preventing IP spoofing via forged headers
- **Database indexes** — Compound index on the content collection for improved query performance under load
- **Session secret validation** — Server warns if secret is too short, fails hard in production if under 16 characters

---

## v2.1.0 — Agentic API Improvements

### Theme Reliability

- **Theme CSS on startup** — `static/css/theme-vars.css` is now regenerated from the database every time the server starts, preventing blank styles after a deploy or container restart

### New Content Endpoints

- **Update by path** — Update content by URL path instead of MongoDB ID, useful when you know the page URL but not its ID
- **Batch publish** — Publish multiple content items in one call; pass an ID list or `publish_all_drafts: true`
- **Content preview** — Render a content item’s HTML without publishing; accepts optional title/data overrides to preview unsaved edits; returns `rendered_html` and `warnings` (missing required fields, unclosed tags, unresolved placeholders)
- **Rendered HTML in get_content** — `include_rendered=true` returns the fully rendered page HTML alongside regular content data

### New Asset Endpoints

- **Upload from URL** — Fetch a remote URL and store it as a LightCMS asset (HTTP/HTTPS only, 50 MB cap, MIME validation)

### Theme Version Pinning

- **Pin/unpin theme versions** — Lock a theme version to protect it from auto-pruning; pinned versions are marked with `locked: true` in version history

### Scoped Search & Replace

- **Targeted replacements** — Run search-and-replace filtered by `content_ids`, `folder_path`, `template_name`, and/or `category` — safer than a full site-wide replacement

### New MCP Tools (13 added, 41 → 54 total)

- `update_content_by_path` — update by URL path; merges data fields
- `publish_multiple` — batch publish by ID list or all drafts at once
- `preview_content` — render HTML without saving; supports field overrides
- `scoped_search_replace_preview` / `scoped_search_replace_execute` — folder/template/category-scoped search & replace
- `upload_asset_from_url` — fetch remote file and store as asset
- `pin_theme_version` / `unpin_theme_version` — protect important theme milestones
- Improved descriptions on existing tools with workflow guidance and examples

---

## v2.0.1 — Configurable Search Ranking

- **Database-stored ranking config** — All search ranking parameters now stored in the database and editable from the admin panel at Tools → End User Search → Search Ranking
- **Configurable fields** — Title match boost, nav page boost, boosted template name substrings, template boost score, demoted path prefixes, demotion penalty score
- **Safety clamping** — Values clamped to −1.0…1.0 to prevent accidental misconfiguration
- **Instant effect** — Changes take effect immediately; in-memory cache invalidated on save
- **Search API documentation** — Expanded typeahead suggest API docs, full parameter tables, response schemas, and JavaScript examples in the admin Integration Guide and README

---

## v2.0.0 — Multi-User RBAC & Smart Search

### Multi-User Access Control

- **Role-Based Access Control (RBAC)** — Three roles (admin, editor, viewer) with granular permissions enforced on every admin UI page and REST API endpoint
- **User Management** — Admin-only panel at `/cm/users` for creating users, assigning roles, disabling accounts, and resetting passwords
- **Email-based login** — Authentication migrated from a single shared password to per-user email + password credentials
- **Force password change** — Temporary passwords prompt a mandatory change on first login
- **Automatic migration** — On first startup with an empty users collection, the existing admin password hash is carried over into a new admin user account

### Audit Logging

- **Persistent audit trail** — All mutations logged with acting user, action, resource, and timestamp
- **365-day retention** — Audit logs auto-expire via MongoDB TTL index
- **Filterable UI** at `/cm/audit` — Filter by action type, resource, and date range
- **Async logging** — LogAsync fire-and-forget pattern to avoid blocking request handlers

### User-Scoped API Keys

- API keys now belong to a specific user and inherit that user’s permissions
- Admins can view and manage all keys; non-admins can only manage their own
- Keys created before v2.0 remain functional as system-level keys with full access

### Smart Search Ranking

- **Structural boost** — Nav-linked pages (parsed from header HTML, cached 5 min) surface above other results
- **Template-based ranking** — Concept pages rank above generic body-only content
- **Video deprioritisation** — Pages under `/videos/` rank below all other content types
- **Typeahead suggestions** — Same structural ranking applied to prefix-match suggestions
- Ranking priority: title+nav > title-only > nav-linked > concept pages > body-only > video transcripts

### Bug Fixes

- Fixed `/cm/audit` page crash caused by subtract/add template functions receiving mismatched integer types
- Made arithmetic template functions type-flexible via `interface{}` dispatch

---

## v1.4.0 — End-User Search API

- **Full-text search** (`/api/search?q=...`) — Regex-based exact matching across all published content
- **Semantic vector search** — Voyage AI embeddings stored in MongoDB Atlas; `$vectorSearch` pipeline for similarity queries
- **Hybrid mode** — Reciprocal rank fusion (RRF, k=60) merges full-text and semantic results into a single ranked list
- **Title boosting** — Results where the query appears in the page title float above body-only matches
- **Graceful degradation** — Works without a Voyage API key (full-text only); automatically enables semantic search when configured
- **Rate limiting** — Per-IP (10 req/min) and global (100 req/min) limits with DDoS protection
- **Embedding pipeline** — Background batch generation with progress tracking in the admin panel
- **Typeahead suggestions** — `/api/search/suggest` endpoint for prefix-matching page titles and extracted keywords
- WARP proxy for Voyage API calls on Fly.io to avoid IP-based rate limiting
- Upgraded embedding model from voyage-3-lite to voyage-4-lite
- Fixed SVG assets not displaying when uploaded with `/assets` path prefix
- Expanded upload allowlist to include CSS, JS, JSON, and other text-based web assets

---

## v1.2.0 — OAuth 2.1 & HTTP MCP Transport

- **OAuth 2.1 authorization server** — Full authorization code flow with PKCE (S256), dynamic client registration (RFC 7591), token rotation, and revocation (RFC 7009)
- **Remote MCP clients** — HTTP streamable MCP endpoint at `/mcp` — connects Cowork, Claude Desktop, and any MCP-compatible app without embedding credentials
- **Discovery endpoints** — `/.well-known/oauth-authorization-server` (RFC 8414) and `/.well-known/oauth-protected-resource` (RFC 9728) for automatic client setup
- **Dynamic MCP server card** — `/.well-known/mcp/server-card.json` with full tool schemas, served live from the running server
- **Smithery registry support** — smithery.yaml and packaging config for registry publication
- Test suite: 82% → 86% coverage with CI via GitHub Actions and Codecov integration
- Loading state feedback on OAuth authorize buttons

---

## v1.1.0 — REST API, CLI Tool & API Keys

- **REST API** at `/api/v1/` — Full JSON API for all content management operations (content, templates, assets, theme, config, redirects, folders, collections)
- **API key authentication** — `lc_`-prefixed keys stored as SHA-256 hashes; created and managed in the admin panel
- **CLI tool** (`cmd/cli`) — Command-line interface wrapping the REST API for scripting and CI/CD workflows
- **MCP refactor** — MCP tools now use the REST API client rather than direct DB access
- Partial update support on all PUT endpoints (send only changed fields)

---

## v1.0.0 — Initial Release

- **Content management** — Create, edit, publish, and delete content using customizable templates
- **Template system** — Define reusable page structures with typed fields; HTML layout with `{{.field_name}}` placeholders
- **Static page generation** — Published content rendered to `content/generated/` for fast, zero-runtime serving
- **Content versioning** — Automatic version snapshots on every update; revert to any prior version
- **Soft delete** — Deleted content recoverable from the admin panel
- **Content collections** — Auto-generated paginated listing pages filtered by category
- **MCP server (stdio)** — 43 tools for managing the entire site through AI agents
- **Theme customization** — Colors, fonts, border radius, custom CSS; header/footer HTML injection with versioning
- **Asset management** — Upload, organize, and serve images, documents, and other files
- **URL redirects** — 301/302 rules managed from the admin panel
- **Rich text editor** — TinyMCE integration for visual editing
- **Search & replace** — Site-wide text replacement with preview before execution
- **Security** — CSRF protection, bcrypt passwords (cost 12), session cookies (SameSite=Strict), file upload validation, login rate limiting
- **Fly.io deployment** — fly.toml and Dockerfile for one-command production deploy

---

[View on GitHub](https://github.com/jonradoff/lightcms) [Back to LightCMS](https://metavert.io/lightcms)

MIT License · Copyright 2026 Metavert LLC
