Skip to guide
Designlof CMS

THE PUBLISHING GUIDE

A clear path from setup
to your first story.

Install on your own hosting, find your way around the workspace, and build a publishing workflow you can maintain.

Explore the five setup steps
On this page 14 chapters

Designlof CMS user guide

A practical guide for this self-hosted core preview. Begin with an empty installation, then build a publishing workflow you can maintain. Menu names below refer to the included English interface.

1. What is included

Prerequisites: this core package and hosting you control. Steps: read this chapter before selecting a host or promising features to a client. Result: a clear boundary for the product you are installing. Recovery: if a feature described for an older full Designlof deployment is absent here, use this guide and the core package rather than copying premium modules.

The core includes posts, pages, structured rich text, nine visual page elements, image media, categories/tags, menus, Editorial and Journal layouts, five roles, revisions, recovery autosave, moderated comments, smart forms, SEO metadata, sitemap/RSS and optional AI/mail adapters. Each installation manages one website. No default account, site data, photographs, private configuration or customer records are shipped.

This is not compatible with WordPress plugins or themes. Storefront/payments, premium layouts, public signup, SSO, two-factor authentication, automatic updates, emailed account recovery, collaborative cursors and media compression/cropping are outside this core. Scheduled publication is query-time eligibility; it sends no timed email. Keep third-party notices and artwork/font licenses with any redistributed package. This distribution is version 0.4.0-core-preview; review it on an isolated installation before updating an existing website.

2. Requirements and hosting layout

Prerequisites: PHP 8.2+, JSON, sessions, fileinfo, PDO plus either pdo_sqlite or pdo_mysql; a private application directory writable by the application owner; terminal access to PHP CLI for ownership preparation and upgrades. ZIP is optional for declarative theme import/export; cURL is optional for AI. Steps: extract the package outside the document root; point the web server only at public/; configure HTTPS and private writable storage; run php dl doctor. Result: required checks pass and private files cannot be fetched over HTTP. Recovery: resolve failed extensions/permissions with the host; do not use world-writable 0777 permissions.

Use 0700 private directories and 0600 private files where the same filesystem user runs PHP and CLI. On a host with a separate web-service user, have the operator configure narrowly scoped ownership/group access; do not make credentials readable by everyone. PHP session storage must also be private. Configure upload_max_filesize/post_max_size above the intended image limit (the application limit is 5 MB); a lower host limit still applies.

Local loopback PHP testing and isolated Apache/PHP plus SQLite/MySQL/MariaDB lifecycles are tested. Existing-server Apache/MariaDB runtime rehearsal is separate evidence; it does not certify every hosting provider. Shared hosting must let you select public/ as the document root or provide an equivalent private split layout, run private CLI commands and protect storage. No specific cPanel/shared-host provider or one-click deployment is verified. On a VPS the operator must configure Apache rewrite/PHP, TLS and private permissions; this package does not provision a server or firewall.

For local testing only, set APP_HTTPS=false, APP_URL to your loopback URL and APP_SITE_MODE=cms, then run the PHP development server:

php -S 127.0.0.1:8080 -t public public/router.php

Production requires HTTPS and APP_HTTPS=true. Keep APP_INDEXABLE=false until you intend search indexing. Set a trusted APP_URL and any APP_BASE_PATH privately; never derive them from untrusted request data. Test /api/health and verify /.env, /storage/ and source files are unavailable from the web. The development server is not a production server.

3. Prepare the database and ownership access

Prerequisites: hosting checks pass; an empty selected database; privileges to create the CMS schema and read/write its data. Steps: choose SQLite for a private local file, or create an empty MySQL/MariaDB database and dedicated database user through your host; then run php dl cms:setup-key as the application filesystem owner. Read storage/cms-setup.key privately. Result: a 0600 one-time ownership key exists; no database tables were created. Recovery: an installed, unrelated, unavailable or unowned incomplete database is refused; restore its connection or inspect the original installation privately rather than clearing it.

SQLite uses the fixed private storage/cms.sqlite path. MySQL and MariaDB both use the PDO mysql driver and utf8mb4. Obtain host/port or an explicit Unix socket, database name, username and password from your host. Initial setup/upgrades need CREATE/ALTER/INDEX and runtime needs SELECT/INSERT/UPDATE/DELETE on that one database. Do not grant privileges on unrelated databases through this guide.

The command cms:prepare remains a compatibility alias for ownership preparation in the generic core; it no longer performs initial DDL there. Existing databases are upgraded with cms:upgrade, never reinstalled. Existing nonempty CMS_DB_DSN takes precedence; legacy DB_DSN is preserved when CMS_DB_DSN is absent. Conflicting nonempty current/legacy DSNs or credentials fail closed. If the server supplies a database, the wizard tests that configuration and does not replace it.

4. Complete the setup wizard

Prerequisites: private ownership key ready; browser session cookies enabled; required hosting checks pass. Result: one administrator and the default Editorial website are installed; setup closes. Recovery: use the original key to resume a recorded interrupted installation; never delete its journal or tables to force a fresh attempt.

The five stages below are the actual wizard labels: Welcome, Database, Your website, Review and Ready. Screenshots come from a real, disposable installation using MySQL / MariaDB. Desktop originals and readable mobile captures are included. Numbered markers in the browser guide point to the matching field rows; original image files are unedited.

Example values only: designlof_demo, cms_demo_user, The Publishing Notebook and publisher@example.test illustrate the fields. They are not bundled accounts or credentials. Your hosting provider supplies your own database values; you choose your own site identity. Passwords, setup keys and hidden session tokens are not shown. A database password is separate from your CMS administrator password.

Step 1 — Welcome

Step 1 Welcome: actual hosting checks, blank one-time setup-key field and Continue to database button
Step 1 · Welcome. Actual example installation; passwords and setup keys are blank. The numbered markers refer to the field table below. Open the original desktop screenshot.

Check every hosting requirement, then prove ownership with the private key. Run php dl cms:setup-key in the application directory as its filesystem owner, then read storage/cms-setup.key privately through your authorized terminal or private file manager. The command reports the key's location; it does not print the key or create database tables. If the key has not been prepared, setup shows a preparation note instead of the input. Do not paste a key into a URL, support ticket, screenshot or chat.

# / Field or controlPurposeSafe exampleWhere to obtain itRequired / optionalValidation and recovery
1. Hosting requirementsConfirm PHP, private writable storage, fileinfo and a supported PDO database driverAll checks show ReadyYour PHP/hosting configuration; run php dl doctor privatelyAll listed checks must pass; either SQLite or MySQL PDO is enoughAsk the operator to resolve Needs attention. Do not use 0777. Database choice comes after ownership proof.
2. One-time setup keyProve access to this fresh installation before choosing a databaseBlank in the screenshot; never an example secretRun php dl cms:setup-key and privately read storage/cms-setup.keyRequired to unlock setupPaste the exact 64-character key. A wrong key is refused; after too many attempts, wait 15 minutes. Keep the original key for interrupted-install recovery.
3. Continue to databaseStart the owned browser setup sessionContinue to databaseThe button on this screenRequired to advanceA 419 means reload the form; do not reuse an old CSRF request. Setup sessions expire after 30 minutes.

Step 2 — Database

Step 2 Database: actual MySQL/MariaDB fields filled with nonsecret example host, port, database name and username; password and socket fields blank
Step 2 · Database. Actual example installation; passwords and setup keys are blank. The numbered markers refer to the field table below. Open the original desktop screenshot.

Choose the database family before filling its connection fields. MySQL and MariaDB use the same MySQL / MariaDB option and PDO mysql driver. Create an empty database and dedicated database user through your hosting provider or authorized database administrator first. This wizard does not provision a MySQL server, create its database or create its database user.

SQLite alternative: choose SQLite — private local file. The fixed target is storage/cms.sqlite; the MySQL host, port, database name, user, password and socket fields do not apply. No host password is needed for SQLite. It needs the PDO SQLite driver and private writable storage. Connection testing does not create the SQLite file; the final installation does. On a MySQL-only PHP host, SQLite is disabled and MySQL / MariaDB is selected.

# / Field or controlPurposeSafe exampleWhere to obtain itRequired / optionalValidation and recovery
1. Database typeSelect the storage engineMySQL / MariaDB, or SQLite — private local fileChoose a supported driver with your host/operatorRequiredA missing driver is disabled. Ask the host to enable the matching PDO extension; do not switch targets to bypass an existing-site outage.
2. HostAddress of the database server for TCP127.0.0.1 for local TCP; a provider may give db.example.comHosting database panel or administratorRequired with TCP; blank with an explicit Unix socketUse the provider's actual host. 127.0.0.1 means this host; localhost can select a driver-default socket. Do not paste a full DSN or URL.
3. PortTCP listener port3306Hosting database panel or administratorRequired with TCP; blank with an explicit Unix socketEnter digits from 1 to 65535. Use the provider's custom port if it differs.
4. Database nameSelect the existing empty databasedesignlof_demoThe database you created in your host panelRequired for MySQL / MariaDB; unused by SQLiteUse 1–64 letters, numbers or underscores. Include any host-added prefix. Existing unrelated tables or views are refused.
5. Database userAuthenticate to that selected databasecms_demo_userThe dedicated user assigned to the databaseUse the provider's required username; unused by SQLiteUp to 128 bytes. Blank works only if your provider's authentication explicitly permits it. Check this user's permissions on this database; this is not your CMS sign-in email.
6. Database passwordAuthenticate the database userBlank in the screenshot; enter your own private valueYour provider or private password managerRequired when the database user's authentication requires it; unused by SQLiteNever reuse a displayed example. It is not echoed; re-enter it when changing/testing the connection. Keep it out of URLs and logs.
7. Socket pathUse a local Unix socket instead of TCP/run/mysqld/mysqld.sock is an illustrative pathYour hosting operator; do not guessOptional alternative for MySQL / MariaDBExpand Connect with a Unix socket instead. Use an absolute provider-supplied path and leave Host and Port blank. Do not combine a socket with TCP fields.
8. Test connection and continueValidate the selected connection and inspect emptinessTest connection and continueThe button on this screenRequired to advanceReads metadata only: no schema/account creation and no clearing of existing data. Cannot connect means check driver, target, private credentials and privileges. A successful test still needs the final Install my website action.

If the server already supplies CMS_DB_DSN or legacy DB_DSN, the screen explains that its configuration is preserved and does not show replacement connection fields. Test that existing connection; change its settings privately with the operator if needed. Conflicting current/legacy database settings fail closed. Never attempt a fresh installation against an installed database.

Step 3 — Your website

Step 3 Your website: actual form with example site title, publisher name and administrator email; both password fields blank
Step 3 · Your website. Actual example installation; passwords and setup keys are blank. The numbered markers refer to the field table below. Open the original desktop screenshot.

Choose the site's identity and the first administrator. The email is the future sign-in identifier, not a database username. No shared default administrator/password is shipped. Both secret fields are blank in the screenshot; enter your own password privately on your installation.

# / Field or controlPurposeSafe exampleWhere to obtain itRequired / optionalValidation and recovery
1. Site titleName displayed for your websiteThe Publishing NotebookChoose your site's nameRequiredUp to 190 bytes in this form. Non-ASCII characters can occupy multiple bytes. Keep a meaningful name; you can change it later in Settings.
2. Your nameDisplay name for the administratorDemo PublisherYour chosen publishing identityRequiredUp to 100 bytes. Non-ASCII characters can occupy multiple bytes. This is a name, not a password or database account.
3. Administrator emailIdentify your CMS sign-in accountpublisher@example.test is a non-deliverable exampleUse your own valid email addressRequiredMust be a valid email; it is stored lowercase. No activation/reset email is sent, so record the chosen identifier privately.
4. Administrator passwordProtect the new CMS administratorBlank in the screenshot; choose a unique private passwordYour private password managerRequiredExactly 12–200 bytes, no null byte. Spaces count and are preserved; non-ASCII characters can occupy multiple bytes. It is never echoed.
5. Confirm passwordPrevent an accidental password mismatchBlank in the screenshotRe-enter the exact password you choseRequiredMust match every byte, including spaces. On a mismatch, both inputs stay blank: enter both again.
6. Review installationSave the checked identity privately and open the summaryReview installationThe button on this screenRequired to advanceThis does not create the account or publish the site. Re-enter passwords when changing identity. Back to database returns to the connection step.

Step 4 — Review

Step 4 Review: actual summary of the example site, sign-in email and tested MySQL/MariaDB connection; passwords remain undisplayed
Step 4 · Review. Actual example installation; passwords and setup keys are blank. The numbered markers refer to the field table below. Open the original desktop screenshot.

Check the summary against the choices you made. This capture follows the genuine tested example MySQL / MariaDB connection. The default layout is Editorial; Journal can be selected later in Appearance. Passwords are intentionally absent from the summary.

# / Field or controlPurposeSafe exampleWhere to obtain itRequired / optionalValidation and recovery
1. Site title / AdministratorConfirm the identity being installedThe Publishing Notebook / Demo PublisherYour previous Your website stepRequired review; read-only hereUse Back to your details to correct it before installation starts.
2. Sign-in emailConfirm the exact future CMS identifierpublisher@example.testYour previous Administrator email fieldRequired review; read-only hereCheck for spelling errors. This is separate from the database username.
3. DatabaseConfirm the selected storage family and tested connectionMySQL / MariaDB — tested connectionYour successful Database stepRequired review; read-only hereA tested connection is not yet an installed site. To change a fresh target, go Back through your details to Database and retest privately.
4. PasswordsConfirm that secrets stay privateKept private; never displayedThe passwords entered in prior stepsNo input on this screenThe summary never reveals the key, database password, administrator password or stored hash.
5. Install my websitePerform the final owned installationInstall my websiteThe final confirmation buttonRequired to finish a fresh installationCreates the CMS schema and one administrator, records the unique installed claim, saves private configuration and closes setup. If interrupted, return with the same original key and use Resume installation; do not delete files/tables or switch targets.

An interrupted installation shows Resume installation rather than a new-install action. Its database and administrator fields are locked. Completed work is verified and resumed; an existing account/password is not replaced. Repeated final requests cannot create a second owner. Back to your details is available only before the final installation has started.

Step 5 — Ready

Step 5 Ready: genuine completed example installation with setup closed, Sign in to your CMS link and hosting/backup reminder
Step 5 · Ready. Actual example installation; passwords and setup keys are blank. The numbered markers refer to the field table below. Open the original desktop screenshot.

This is the real success screen after the final installation, not a decorative confirmation mockup. Setup is closed. There are no connection, key or password fields here.

# / Field or controlPurposeSafe exampleWhere to obtain itRequired / optionalValidation and recovery
1. Setup is closedConfirm final installation succeededYour publishing space is ready.The result of Install my websiteNo inputA completed installation must not be reinitialized. The success screen belongs to the finishing session; other visits to setup redirect to sign-in.
2. Sign in to your CMSOpen the account's sign-in page/admin/login; use the email you choseThe button on this screen and your private password managerNext step to start publishingSign in with your chosen CMS email/password, not the database user. A database outage needs operator connection recovery; do not run setup again.
3. Hosting and backup reminderPrepare the installed site for normal useHTTPS, protected private directories and a recoverable backupYour operator and chapter 14Required before sharing a production siteKeep private configuration/backups out of the web root. There is no emailed password reset; private operator recovery is documented in chapter 11.

Validation, expiry and interrupted-install recovery

Back, refresh and validation errors preserve the last safe server-side step; password inputs always stay blank. Re-enter administrator passwords when changing identity. Setup sessions expire after 30 minutes: unlock again with the original private key. A 419 means reload the form; 429 means wait for the setup rate-limit window. Never place credentials into a retry URL.

Cannot connect means verify the selected driver, host/port/socket, permissions and private credentials. A missing selected driver is a requirement failure. Existing unrelated tables/views are refused. Incomplete schema without the matching private journal and database claim requires operator review. An outage on a closed installation never unlocks fresh setup.

Setup state/configuration remain private in storage/cms-installation.json and storage/cms-config.json; neither belongs in source control, demos or releases. Keep the original journal during recovery. After success it contains closure metadata rather than account/password material. Configuration contains the necessary database credential and must remain private with 0600 permissions.

If the process stops after account commit or configuration promotion, resume with the same original key; completed work is verified rather than repeated. If setup is already closed but one-time-key cleanup was interrupted, the operator may remove the remaining private key after verifying the installed marker and closure record. A changed database target, mismatched ownership, unrelated schema or corrupt private state requires private investigation with a backup first. There is no automatic destructive cleanup.

5. First sign-in and settings

Prerequisites: installation succeeded and you have the private administrator password. Steps: open /admin/login, sign in, select Settings, change Site title and Description, choose New comments, then Save settings. Open View website / Visit site to inspect the public result. Result: saved identity appears on your website. Recovery: check exact email/password and browser secure-cookie settings; an HTTP-only local test needs APP_HTTPS=false, while production needs HTTPS. An installed database outage is an operator connection issue, not a reason to run setup again.

Use Profile for your own account changes. Account/password edits revoke existing CMS sessions. Configure origin/indexing privately; Settings does not change the trusted URL or search-indexing environment. New comments defaults to Closed site-wide. Forms, Appearance, Menus, Users, Plugins and Tools & health are administrator workspaces.

6. Write and publish your first post

Prerequisites: an administrator, editor, author or contributor account; optimized images you have rights to publish. Steps: create a category/tag if needed, upload your image in Media library, then open Posts and Add post. Fill Title; use Add content and Text & formatting to write. Add an Excerpt, choose a Featured image, assign Categories & tags and set URL slug. Keep Status at Draft and click Save. Result: a saved draft with an assigned ID; Preview saved shows the saved version. Recovery: preview reflects the last saved content, so Save before checking a change.

Upload through Media library, open Edit details and write meaningful alternative text and a caption. In the post editor use Image for media you uploaded. Text formatting supports headings, lists, quotes, links and tables. Pasted HTML retains only supported formatting. Raw HTML/scripts/iframes/arbitrary CSS are not accepted. Without JavaScript the Content source (JSON) fallback remains available; it requires validated block JSON rather than arbitrary markup.

When ready, review Preview saved, select Published under Status and a current/past Publication date (UTC), choose the intended Visibility, then Save. Contributors choose Pending review for an editor instead. Open the public permalink and check title, body, image alt text and URL. If it does not appear publicly, check status, publication time and visibility; do not change permissions merely to expose private content.

7. Scheduling, review, visibility and recovery

Prerequisites: saved content and a role permitted for the action. Steps: under Publish choose Scheduled and a future Publication date (UTC), then Save. Or select Pending review and ask an editor to review the saved draft through the normal team workflow. Result: scheduled public content becomes discoverable when its UTC timestamp is reached; no worker is required. Recovery: check the host clock and UTC conversion. The record remains Scheduled; no notification job changes it to Published.

Public content is discoverable through eligible lists/API/feed/sitemap. Private content requires authorized editors. Password protected content requires its content password; the permalink title/existence remain public. Uploaded media URLs remain public independently of a post’s visibility. Do not upload confidential files expecting post privacy to protect them.

Autosave stores a recovery copy after 15 seconds of typing inactivity; it does not publish or replace saved settings. On reload choose Restore unsaved text, inspect it, then Save. Revisions keeps the latest 20 prior saved versions; Restore as draft restores content/SEO fields without blindly replacing taxonomy or publishing permissions. Copy your unsaved text before resolving a concurrent-change 409 response, reload the newer version and merge deliberately.

Trash is reversible and restored content returns to Draft. Posts/pages use reversible trash and have no permanent content deletion interface. Unreferenced images can be permanently removed with Delete image. Non-administrator users with no owned content/media/revisions can be removed with Delete user; these operations are not a trash workflow and need a backup. Deleting a category/tag removes relationships, not posts. Keep backups; trash and revisions are not a full recovery strategy.

8. Build a page with nine core elements

Prerequisites: administrator/editor access, a page draft and any required images/forms. Steps: open Pages, choose Add page, then choose Visual (which first saves the text version), add a section, then add an element from the palette. Configure it, preview and save. Result: a structured page rendered through the selected standard layout. Recovery: use the source fallback only with valid structured JSON; copy work before reloading a version conflict.

The core palette provides Heading, Text, Image, Button, Form, Divider, Content, Posts and Categories. Images use Media library IDs and alternative text. Button links must be valid URLs. Form embeds a saved form; publish/open the form before expecting public submissions. Content contains supported rich-text blocks. Posts/Categories collections use eligible public content and taxonomy. Premium hospitality, agency, commerce, video and animated widgets are outside this package.

Switch between Text and Visual thoughtfully: switching saves current work first. Save a page as Draft, use Preview saved, review mobile layout and only then select Published. A collection can be empty when its posts are drafts, future-dated or non-public; publish suitable content rather than embedding private results.

9. Media, categories and tags

Prerequisites: an upload-capable role (administrator/editor/author) and rights to use your image. Steps: open Media library, expand upload, choose a JPEG/PNG/WebP/GIF image and submit; use search/filter, then Edit details to update alt text/caption or Copy URL. Result: a MIME-validated image served at its media URL. Recovery: check the host request limits, 5 MB application limit, 40-megapixel limit and writable private media storage. SVG, scripts and other file types are rejected.

Media displays 24 items per page. Authors manage their own media; editors/administrators manage the library. There are no generated thumbnails, crop tools or image compression. Optimize files before uploading. Never paste an upload path or private storage filename into a public page.

Open Categories & tags, create a term with its name/slug/taxonomy, then check it in the post editor and Save. Public taxonomy browsing uses eligible posts. Hierarchical taxonomies and page-parent trees are not implemented. If a term appears empty, check its relationships and public content eligibility.

10. Appearance and navigation

Prerequisites: administrator access and saved public content. Steps: open Appearance, preview Editorial or Journal, choose the layout and inspect View website. Open Menus, create a named menu, add valid links, assign header/footer locations and save. Result: the chosen layout/navigation appears publicly. Recovery: preview before changing an active layout; a layout change does not move data or create missing images/content.

Menus support two-level links. Empty menus/collections remain empty until configured. Import/export supports reviewed declarative DL Theme v1 packages when PHP ZIP is available; it is not a PHP/JavaScript plugin installer or a WordPress theme uploader. Keep package size/path/asset restrictions. The distribution includes Editorial/Journal only; no premium theme or remote marketplace is promised.

11. Roles and account recovery

Prerequisites: administrator access to Users for account management, or private server access for recovery. Steps: create the required named account and role in Users; test its own workflow through a separate browser session. Result: roles restrict actions and protected requests recheck current account version/role. Recovery: keep an administrator available; the UI does not demote administrators. Server migrations for role changes require deliberate operator review.

RolePublishing and administration
AdministratorAll content, site settings, menus/themes, forms, users, plugins, tools and own profile
EditorAll posts/pages, categories/tags, media and comment moderation
AuthorCreate/publish/edit own posts and upload/edit own media metadata
ContributorOwn post drafts and review submission; cannot publish or edit published posts
SubscriberDashboard and own profile; no publishing

There is no public registration, invitation system, custom capabilities, SSO or 2FA. The CMS identity is separate from the framework example authentication. Owner recovery uses php dl cms:password EMAIL with the new password on private stdin; never include passwords in shell arguments, chat, screenshots or tickets. Supply the exact intended bytes; adding a newline changes the password. This operation increments the account version and revokes prior sessions. There is no emailed password reset. A real credential change requires the owner/operator to handle the password privately.

12. Forms, comments and mail

Prerequisites: administrator access for Forms and private mail configuration if delivery is needed. Steps: choose Create form in Forms, define supported fields, set its button/success text and status, save, then embed the saved form on a page or use its public route. Submit only synthetic data while testing. Result: accepted entries appear under the form’s Responses action. Recovery: check published/open state, honeypot/CSRF/session expiry, rate limits and current schema; do not retry a real submission repeatedly without inspecting its existing receipt.

Notifications require the operator’s explicit private MAIL_* configuration; the clean default disables mail. Configure/test SMTP privately with a synthetic recipient before relying on delivery. The required private SMTP fields are MAIL_TRANSPORT=smtp, MAIL_HOST, MAIL_PORT, MAIL_ENCRYPTION (tls or ssl), MAIL_USERNAME, MAIL_PASSWORD and MAIL_FROM. The defaults are TLS/587 or SSL/465; use the values supplied by the provider and keep the password private. cURL is required. Stored entries and email delivery status are separate. Open Responses; review the status and use Retry email when available and CSV export only as an authorized administrator. The Export button produces CSV (up to 10,000 responses). CSV/export contains user data and belongs in private storage, not a demo or release. No real SMTP delivery is claimed by the local preview.

For comments, enable New comments as Open on opted-in posts in Settings and Allow comments on the post, then Save. New submissions are Pending. Editors/administrators use Comments to approve, hold, mark spam/trash and manage threads. Author email is not public; replies support three levels. Honeypot/rate limits/CSRF are protections, not an anti-spam guarantee. There are no notification emails or external anti-spam service in this core.

13. SEO, feeds and optional AI

Prerequisites: a trusted private APP_URL, intended indexing policy and eligible public posts. Steps: add SEO title and Meta description under Search appearance; review the SEO Meter and saved preview; check /sitemap.xml (also /cms-sitemap.xml) and /feed.xml. Result: eligible published content has structured search/feed metadata. Recovery: check visibility, timestamp, canonical origin and APP_INDEXABLE. SEO Meter is an editorial checklist, not a ranking prediction or promise of traffic.

Drafts, future and non-public content are excluded from public discovery. Turning indexing off is a crawler instruction, not access control. Public API endpoints only expose eligible public content. Use post privacy/roles for authorization, and keep uploaded confidential media off this public media system.

Optional AI needs privately configured AI_MODEL, AI_BASE_URL and AI_API_KEY plus cURL. Writers choose Suggestion type and Generate suggestion; review the result before using it. It never publishes automatically. Calls may incur charges and are limited by adapter budgets and per-user limits (3/minute, 30/day). Keys never go to the browser. The release tests use synthetic adapters, not a real provider request; verify your provider privately before relying on it.

14. Upgrades, backup, restore and troubleshooting

Prerequisites: a recoverable consistent backup, matching code and control of all writers. Steps: pause site writers/CLI jobs, snapshot the selected database plus media/themes/private configuration and matching code, test restoration into an isolated private instance, then run php dl cms:upgrade as the application owner using the existing database. Result: versioned additive migrations finish and php dl cms:status reports the schema. Recovery: keep writes paused after a migration error; MySQL DDL is not transactional. Resume only after diagnosis with the same code/database. Never run initial setup/prepare/install against an installed database.

There is no automatic updater. Review the new package/manifest and dependency notices, preserve all private configuration/runtime files and data, compare PHP requirements, and follow its upgrade notes. The browser does not run ordinary schema upgrades. Export in Tools & health is supported content transfer, not a full backup of accounts, media, form responses or all settings.

For SQLite, create a consistent backup with SQLite’s backup API or equivalent while coordinating writers; do not copy a live main file and ignore WAL. For MySQL/MariaDB use a transaction-consistent InnoDB dump with private existing credentials; keep passwords off command lines/logs. Coordinate shared media/theme writers across every site using the same storage. Protect backup directories/files at 0700/0600, retain internal hashes privately and never copy customer records to screenshots/demo fixtures.

Restore to a separate private database/storage location with no public routing, disabled mail/AI/indexing and matching code. Verify schema, counts, account roles/session versions, instance, content, media and forms privately. Before replacing live state, drain writers, preserve the newest state and decide how to reconcile newer writes. A snapshot restore discards later changes; do not silently restore over them or over a companion site’s shared storage.

Old code may reject tagged account AND protected-content password hashes introduced by the current core. After new account/password/content changes, code-only downgrade is not automatically compatible. Prefer a reviewed fix on the current core; if restoration is necessary, preserve new writes and review the reconciliation/data-loss decision with the owner. Never reset accounts merely to make a rollback work.

Troubleshooting: missing driver or fileinfo → enable the matching PHP extension; setup key unavailable → run private ownership preparation as the application user; 419 → reload and resubmit through a new CSRF form; 429 → wait for the rate-limit window; connection error → verify the selected target/driver/credentials privately; schema-not-current → backup and run private cms:upgrade; 409 content conflict → copy your work, reload and merge; empty public listing → check status/visibility/UTC; upload failure → check MIME/dimensions/host limits and storage; failed mail → inspect authorized entry delivery status and test SMTP privately. Logs should contain safe operational metadata, not credentials or form/customer records. An installed-site outage must never trigger reinstallation.