3.5.0
Total CMS 3.5 is a platform release. The jump from 3.2 reflects the scope: new top-level subsystems — Site Builder, Extensions, CLI, MCP server, OAuth, Automations, Internationalization, Composer distribution, Setup Wizard, Event system — sit alongside the existing collections and templates engine.
Highlights
- Site Builder — build pages, routes, and templates in the admin, served dynamically at request time with no build step. Four starter kits (minimal, blog, business, portfolio), drag-and-drop page ordering, an optional Vite frontend pipeline, and git-first templates for teams that keep their site in a repo
- Extension system — extend Total CMS at every layer: Twig functions and filters, CLI commands, routes, admin pages, dashboard widgets, field types, event listeners, MCP tools, form actions. Capability detection turns what an extension actually does into per-capability permission toggles, backed by crash containment, auto-quarantine on production, a pre-enable review screen, and update re-consent
- MCP server + OAuth 2.1 — AI agents read and write your content over a standards-based interface. Three personas (anonymous, API key, OAuth), content tools with full schema validation, saved-query tools, templated prompts, pluggable search providers, and a full OAuth 2.1 authorization server with PKCE, consent, revocation and an audit log. Every install also serves its own documentation to agents
- Automations — run your own PHP handlers on a schedule, a webhook, or a content event, with contained failures, auto-disable after repeated errors, run history, and replay. (Pro)
- Internationalization — localized field types with synced locale tabs, a BCP 47 locale registry with region fall-down, per-user admin language, and an admin shipping in seven languages
- Publishing API — publish from MarsEdit, Byword, Ulysses or Open Live Writer over a WordPress-compatible XML-RPC endpoint. Off by default behind an explicit toggle
- Composer distribution & CLI —
composer create-project totalcms/totalcms, plus a fulltcmscommand-line tool and a first-run Setup Wizard that takes a new install from welcome to dashboard - Event system — a centralized, priority-ordered dispatcher with 20 core events, typed payloads, fault-isolated extension listeners, and import-time suppression so importers fire
import.*rather than a save firehose - Security — a per-site encryption key, security headers across admin, auth and setup, reduced error-report payloads, and extensions that can no longer override core services
- Sync — the Sync Manager now moves collection objects alongside schemas and templates, shows the diff before you commit to it, and backs up whatever it overwrites
The complete feature breakdown is in the 3.5.0 release notes at docs.totalcms.co. Detail follows.
Upgrade notes
- API prefix: API routes now live under
/api/. External callers must add the prefix; server-side templating is unaffected - Template include paths: the templates root is namespaced — prefix paths with
templates/, e.g.{% include 'templates/header.twig' %}. The same applies anywhere a template path appears - Whitelabel location: whitelabel templates must live under
whitelabel/. Re-save each in the admin to migrate it - CSRF on API writes: session-authenticated API writes require a CSRF token. A verified same-origin request is accepted in its place, so browser forms are unaffected — but anything scripted against the API with a session cookie needs updating. API-key callers are unaffected
- Super-admin scope: super admins are recognised only in the default auth collection. Sites running several auth collections may find an operator has lost privileges, and should be granted them in the default collection
- API key path grants now match on segment boundaries and across the
/apiprefix. A key that was unintentionally reaching neighbouring paths will stop - Install model: new installs are Composer-based; existing zip installs continue to work (docroot →
public/, writable dirs → project root) - Locale setting: the site locale moved to Internationalization settings (
i18n.default); affected sites self-heal - Templates permission retired in favour of the Site Builder permission. Stored
templatesvalues in existing group files survive as the read-side fallback, so groups keep working, but the checkbox is gone from the access-group form - Settings → Installation removed. Its one field (
datadir) orphaned every collection, key, session and user at the old path when saved through the UI.datadiris now set exclusively inconfig/tcms.php, which the Setup Wizard still writes on first run multicheckboxrenamed tochecklist. The old type name still works, so this breaks nothing today
Added
- Site Builder: dynamic page router matching
builder-pagesat request time; templated URLs like/blog/{id}; builder admin UI with hierarchical sidebar, live reload and a page-inspector overlay; starter kits viatcms builder:initeach shipping a custom 404; optional Vite pipeline viatcms builder:frontend; git-first templates from a project-rootbuilder/folder; Template Designer for inline token-gated template definition; per-page middleware with built-inauthgating;cms.builder.url()andcms.builder.canonicalUrl() - Extension system: two-phase lifecycle behind a curated
ExtensionContextAPI; extension points across Twig, CLI, routes, admin nav, dashboard widgets, field types, event listeners, container definitions, schemas, settings, form actions, MCP tools/resources/prompts and search providers; per-extension access control in Access Groups; a sanctioned per-extension storage API; extensions loadable from a project-levelextensions/directory; bundled extensions for Protect, Scheduled, Maintenance, Pushover, Algolia search and Docs - MCP server: built-in server at
/mcp; query, search, fetch, create, update and patch tools; saved-query tools and templated prompts defined as data, targetable at a collection or a Data View; output schemas on every tool; ChatGPT and deep-research compatiblesearch/fetch; an SSE listening stream onGET; a connection checker for the failures that are invisible from outside; access-group governed reach across read tools, write tools, resource reads and enumeration - OAuth 2.1: authorization-code flow with PKCE, refresh tokens, consent screen, revocation, dynamic registration (off by default), RFC 9728 protected-resource metadata, replay detection, and garbage collection of stale self-registered clients
- Automations: schedule, webhook and event triggers; a pre-wired handler context covering object and deck CRUD, querying, counters, mailer, file and image savers, importers, sync and logging;
apiKey,sameOriginandnonewebhook auth modes;ExtensionContext::addAutomation()so extensions can ship their own - CLI (
tcms): collections, objects, schemas, JumpStart import/export, sync, updates, builder scaffolding, extension management; a full object write surface withobject:create,object:patchandobject:delete;schema:lint,search:reindex,repair:files,repair:index,jobs:process,automations:process,skill:install,deploy;--jsonon every command - Composer distribution:
totalcms/cmsandtotalcms/totalcmson Packagist;totalcms/cmsis itself a Composer plugin running project-side maintenance on install and update; subpath-aware routing, assets, OAuth and MCP discovery - Setup Wizard: welcome → environment → data path → account → license → server config → complete, with auto-login. New trials register a name and a verified email at the license step so an evaluator can be told before their trial expires; skipping stays available throughout and existing trials are grandfathered
- Internationalization:
LocalizedText,LocalizedTextareaandLocalizedStyledTextfield types with locale tabs that sync across fields; BCP 47 locale registry with site default and region fall-down; per-user admin locale; localized import/export;cms.locale.*Twig helpers includinghtmlLang(); a locale-aware price field; admin translations for en_US, en_GB, de_DE, es_ES, it_IT, nl_NL and pl_PL - Fields and forms: singleton collections; card field for inline nested objects; secret field for masked storage; file and image fields inside cards and deck items; colour field with matching
colorsTwig helpers; checklist field (formerlymulticheckbox); form fieldsets via[[ ]]informgridorcms.form.fieldset(); conditional visibility gains a disable mode and works inside deck tables; tag suggestions on media fields including nested ones;${uid-N}autogen; public registration with an opt-in allow-list and optional email verification - Admin: Permission Matrix auditing what each access group can reach, including an AI / MCP reach dimension; user impersonation; multi-select bulk actions in the collection table with bulk delete and bulk download; a needs-attention dashboard panel and an automations status widget; Collection and Object Visualizers under Utilities → Reporting, both filtered to what the viewing operator can read
- Operations: cron URLs at
GET /cron/automationsandGET /cron/jobsfor hosts without shell cron;cache.domainScopedso installs sharing onetcms-datashare a cache namespace; a rebuilt job queue with an execution deadline, a daily vacuum, real indexes and on-demand clearing of failed jobs; OPcache file-cache-only mode detected and reported - Caching, search and performance:
{% cache %}fragment cache tag with tag-based auto-invalidation and auth-safe defaults; thelistifyfilter; relevance-scored search viacms.collection.searchScored(), shared with MCP search; Twig yield (streaming) mode; CodeMirror 6 and HTMX 4 across the admin - Sync: collection objects sync alongside schemas and templates; push and pull operate as create-or-update; a diff preview with per-item badges, a consequence preview and Select Changed; overwrites backed up first; collection settings sync carrying a timestamp content cannot move
- Developer experience: a built-in agent skill teaching AI coding agents how to build a Total CMS site, installed automatically with an
AGENTS.mdpointer; a Cursor rules file giving Cursor the same grounding;cms.assetsHead()andcms.assetsBody()for injecting core and extension assets into any front end; atotalcmsreference schema exercising every field type; docs reorganized into feature-first groups with the search index shipped in the package - DataViews: views can depend on other views, with the rebuild scheduler resolving the full transitive set and rebuilding in dependency order
Changed
- The MCP server moves from Pro to Standard — a Standard site can expose collections for an AI agent to read. Writing from an agent stays Pro, because both credentials granting a non-anonymous persona (API keys and the OAuth server) are Pro features. Lite does not include MCP. Text watermarks and barcodes also moved down to Standard
- Logging consolidated from 22 ad-hoc files into a nine-file taxonomy, with all warnings and errors mirrored into one place. Zip installs now log to
tcms-data/.system/logs/so logs survive updates mcp.publicAccessdefaults to on; each collection's ownmcp.accessremains the gate- Site Builder pages are identified by their route rather than their title in the builder sidebar and the Sync Manager — a route is short, unique, and is what identifies a page in a router-driven builder
Security
- Per-site encryption key — the
encrypt/decryptTwig filters are keyed to a secret generated attcms-data/.system/site.keyrather than a constant shipped in public source. Existing values keep working via a legacy fallback. Includesite.keyin your data-directory backups - Security headers on admin, auth and setup routes:
X-Frame-Options: SAMEORIGIN,frame-ancestors 'self',nosniff, and a strict referrer policy - Error reports carry less data — the error-monitoring SDK is explicitly configured never to attach personal data or request bodies; previously small POST bodies could ride along. The Setup Wizard now asks for consent rather than assuming it
- Extensions can no longer override core services — container definitions under the
TotalCMS\namespace, or already defined by the core container, are denied with a logged warning. An extension could previously registerLoginService::classand silently replace core authentication - Code-executing system collections are super-admin only on import — collections carrying handler code such as
automationscould previously be imported by any admin - Super admins are recognised only in the default auth collection; JumpStart is gated behind super-admin and no longer exports credentials; extension secrets are written with private permissions; dynamic OAuth client registration ships off by default
Pre-release history for the 3.5 line (32 beta and RC entries) is archived in CHANGELOG-3.5-prereleases.md.