# CMS installieren und aktualisieren – ohne Datenbank

> CMF installieren: hochladen, DocumentRoot auf public/, Schreibrechte, site.json, erster Login, API-Zugang – dazu System-Update mit Backup und Rollback.

Kanonische URL: https://cmf.brosemedien.de/installationsanleitung

---

# Installationsanleitung

Vom Download bis zur laufenden Website – und später zum Update. Kein Installer, keine Datenbank, keine Build-Tools.

### Inhalt

- [Systemvoraussetzungen](#voraussetzungen)
- [Ordnerstruktur verstehen](#ordnerstruktur)
- [Hochladen und einrichten](#hochladen)
- [Schreibrechte](#schreibrechte)
- [Konfiguration: site.json](#konfiguration)
- [Erster Login](#erster-login)
- [API-Zugang anlegen](#api-zugang)
- [System-Update, Backup und Rollback](#system-update)

### Kurz gesagt

PHP 8.1 oder neuer, Apache mit mod_rewrite, ein SSL-Zertifikat und Schreibrechte. Keine Datenbank, kein Composer, kein npm. Die Domain zeigt auf den Ordner `public/`.

## 1. Systemvoraussetzungen

CMF hat bewusst wenige Anforderungen. Ein üblicher Webspace mit PHP reicht.

✓**PHP 8.1** oder neuer, mit den üblichen Erweiterungen fileinfo (Uploads), zip (Backup und Update) und dom (SVG-Bereinigung)
✓**Apache** mit mod_rewrite – Weiterleitungen und Schutzregeln stehen in `.htaccess`
✓**SSL-Zertifikat** – die mitgelieferte `.htaccess` leitet alle Aufrufe auf HTTPS um
✓**Schreibrechte** für bestimmte Ordner (siehe Abschnitt 4)
✓**FTP-Zugang** oder Dateimanager des Hosters
✗Keine Datenbank, kein Composer, kein npm

**!****nginx:** CMF liefert keine nginx-Konfiguration mit. Die Regeln aus `.htaccess` – Weiterleitung aller Seiten auf `index.php`, HTTPS, Sperre für `config/` und `content/`, keine Skripte in `media/` und `files/`, Security-Header – musst du dort selbst in die Server-Konfiguration übertragen.

## 2. Ordnerstruktur verstehen

Das ist der wichtigste Punkt der Installation. CMF trennt öffentliche und private Dateien:

### public/ = DocumentRoot

Der Ordner `public/` ist das **Web-Root-Verzeichnis** – der Ordner, auf den die Domain zeigt. Nur er ist öffentlich erreichbar.

Er enthält:

- `index.php` – Website
- `admin.php` – Admin-Bereich
- `api.php` – REST-API
- `.htaccess` – Weiterleitungen, HTTPS, Caching, Schutzregeln
- `assets/` – CSS, JavaScript, Schriften
- `media/` – hochgeladene Dateien
- `files/` – Download-Dateien, z. B. `README_MASCHINEN.md`

Dazu schreibt CMF hier automatisch `sitemap.xml`, `robots.txt`, `feed.xml`, `llms.txt` und `search-index.json`.

### Alles andere = nicht öffentlich

Die Ordner `app/`, `config/` und `content/` liegen **neben** `public/`, also eine Ebene über dem DocumentRoot. Über den Browser sind sie nicht erreichbar – das ist Absicht.

- `app/` – der PHP-Systemcode
- `config/` – `site.json`, Benutzer (Passwörter nur als Hash), Theme
- `content/` – Seiten, Blog-Beiträge, Header/Footer und Formular-Einsendungen als JSON
- `version.json` – installierte Version

In `config/` und `content/` liegt zusätzlich je eine `.htaccess`, die jeden Zugriff sperrt – ein Sicherheitsnetz, falls der DocumentRoot doch einmal falsch gesetzt ist.

**→****Die häufigste Fehlerquelle:** Den Inhalt von `public/` in ein anders benanntes Web-Root (etwa `htdocs/`) zu kopieren, funktioniert nicht. CMF erwartet `app/`, `config/`, `content/` und `public/` nebeneinander in einem Ordner – und schreibt Sitemap, Feed und Theme-CSS nach `public/`. Lässt dein Hoster keinen eigenen DocumentRoot zu, frag dort nach, bevor du installierst.

## 3. Hochladen und einrichten

Die Struktur aus dem ZIP bleibt auf dem Server genau so erhalten. Du lädst alles in einen Projektordner und lässt die Domain auf dessen Unterordner `public/` zeigen.

1
**ZIP entpacken**
Das Archiv von der Download-Seite lokal entpacken

2
**Alles hochladen**
app/, config/, content/, public/ und version.json in einen Projektordner kopieren

3
**DocumentRoot setzen**
Im Hosting-Panel die Domain auf den Ordner public/ zeigen lassen

4
**Rechte und site.json**
Schreibrechte setzen, name, lang und baseUrl eintragen

5
**Im Browser testen**
Domain aufrufen – die Startseite sollte erscheinen

So sieht die Struktur auf dem Server aus:

```
/projektordner/             ← nicht öffentlich
├── app/                     ← PHP-Systemcode
├── config/                  ← site.json, Benutzer, Theme
├── content/                 ← Seiten, Blog, Einsendungen
├── version.json             ← installierte Version
└── public/                  ← DocumentRoot (hierhin zeigt die Domain)
    ├── index.php
    ├── admin.php
    ├── api.php
    ├── .htaccess
    ├── assets/
    ├── files/
    └── media/
```

## 4. Schreibrechte

CMF speichert alles als Dateien. PHP braucht deshalb Schreibrechte auf diese Ordner – einschließlich aller Unterordner und Dateien darin. Welche Werte nötig sind, hängt vom Hoster ab: Oft genügt `755` für Ordner; läuft PHP unter einem anderen Benutzer als dein FTP-Zugang, braucht es `775`.

### config/

Benutzer, Theme, Login-Sperre und die Backups des System-Updates. Nötig für: Passwörter, Benutzer, Theme speichern, Updates.

### content/

Seiten, Blog-Beiträge, Header/Footer und Formular-Einsendungen. Nötig für: jedes Speichern im Admin oder per API.

### public/

Hier entstehen `sitemap.xml`, `robots.txt`, `feed.xml`, `llms.txt` und `search-index.json` bei jeder Änderung neu.

### public/media/

Speicherort für hochgeladene Bilder, Videos und Dokumente. Nötig für: Upload im Admin und per API.

### public/assets/css/

Hier schreibt CMF `theme.css` und `custom.css`. Nötig für: Theme speichern und Custom CSS.

### Für das System-Update

Zusätzlich `app/` und `version.json`. Das Update ersetzt außerdem `index.php`, `admin.php`, `api.php` und `public/assets/js/`.

## 5. Konfiguration: site.json

Anpassen musst du nur eine Datei: `config/site.json`. Eine Oberfläche dafür gibt es im Admin nicht – du bearbeitest sie direkt oder per API (`site_update`). Das Minimum:

```
{
  "name": "Meine Website",
  "lang": "de",
  "baseUrl": "https://meine-domain.de"
}
```

Feld Wert Wirkung
`name` (Pflicht) Text Name der Website: Titel-Anhang (ohne `title_suffix`), `og:site_name`, strukturierte Daten, Überschrift von `llms.txt` und Feed
`lang` (Pflicht) Sprachcode, z. B. `de` `<html lang>`, `og:locale` (aus `de` wird `de_DE`), Sprache des Feeds
`baseUrl` Vollständige URL ohne Schrägstrich am Ende Canonical, Sitemap, Feed, `llms.txt`, absolute Bild-URLs, strukturierte Daten. Fehlt sie, fehlen Canonical und strukturierte Daten
`og_image` Pfad, z. B. `/media/…/vorschau.png` Vorschaubild beim Teilen, wenn eine Seite kein eigenes Bild hat
`logo` Pfad zum Logo Logo im Organization-Schema; eine SVG-Datei dient zusätzlich als Favicon, falls keine `favicon.svg` vorhanden ist
`title_suffix` Text, darf leer sein Anhang im Seitentitel („Seite | Anhang“). Ohne das Feld wird `name` angehängt, ein leerer Wert hängt nichts an
`software_schema` `true` / `false` Gibt auf der Startseite zusätzlich ein SoftwareApplication-Schema aus
`software_category` Text Kategorie im Schema (Standard: `DeveloperApplication`)
`software_version` Text Version im Schema (Standard: Version aus `version.json`)
`download_url` Pfad oder URL Download-Adresse im Schema (Standard: `/files/cmf_latest.zip`)
`csp` Objekt mit Listen: `script_src`, `connect_src`, `style_src`, `img_src`, `font_src`, `media_src`, `frame_src` Erlaubt zusätzliche fremde Quellen in der Content-Security-Policy, z. B. für ein externes Widget oder eine Schrift. Die strenge Basis bleibt bestehen

### Nach einer Änderung

Die `baseUrl` muss zur echten Domain passen. Sitemap, Feed und `llms.txt` schreibt CMF neu, sobald du eine Seite oder einen Beitrag speicherst. Hast du nur `site.json` geändert, rufst du dafür die API-Aktion `sitemap_generate` auf – oder speicherst einmal eine Seite.

### README_MASCHINEN.md ist keine Konfiguration

Die Datei `public/files/README_MASCHINEN.md` ist eine Kurzreferenz der API, die du einem Agenten als Kontext mitgibst. Für den Betrieb musst du sie nicht anpassen.

**Wichtig:** Trag dort nie deinen API-Token ein. Die Datei ist öffentlich abrufbar und wird in der automatisch erzeugten `llms.txt` verlinkt. Wenn du sie anpassen willst, dann als Kopie auf deinem Rechner.

## 6. Erster Login

Sobald die Dateien liegen und `site.json` angepasst ist, kann es losgehen:

### Admin aufrufen

Den Admin-Bereich erreichst du unter:

```
https://meine-domain.de/admin.php
```

Das Download-Paket enthält einen Zugang mit Benutzername `admin` und Passwort `admin`.

Nach Fehlversuchen wartet der Login jedes Mal länger: 5 Sekunden, dann 25, 125 … bis höchstens eine Stunde.

### Eigenes Passwort – Pflicht

Wer sich mit dem Standard-Passwort anmeldet, landet sofort bei **„Eigenes Passwort festlegen“**. Du gibst das neue Passwort zweimal ein, mindestens 8 Zeichen, dann „Passwort speichern“. Vorher ist keine andere Admin-Seite erreichbar.

In `config/users.json` steht kein lesbares Passwort, nur ein Hash. Später änderst du Passwörter unter *Benutzer → Passwort ändern*.

### Installation prüfen

- ✓ Startseite lädt über HTTPS
- ✓ Eine Seite lässt sich speichern (`content/`)
- ✓ Ein Bild lässt sich hochladen (`public/media/`)
- ✓ „Theme speichern“ wirkt (`public/assets/css/`)
- ✓ `/sitemap.xml` und `/llms.txt` zeigen deine Domain

Klappt etwas nicht, prüfe zuerst DocumentRoot und Schreibrechte.

## 7. API-Zugang anlegen

Skripte, Workflows und Agenten melden sich mit einem Token an. So legst du ihn an:

- Im Admin *Benutzer* öffnen.
- Unter **„Neuen Zugang anlegen“** Benutzername und Passwort (mindestens 8 Zeichen) eingeben.
- Das Häkchen **„API-Zugang anlegen“** setzen und „Benutzer anlegen“ klicken.
- Das Token (beginnt mit `cms_`) erscheint **genau einmal** unter „API-Token erzeugt“. Sofort kopieren und sicher ablegen – gespeichert wird nur ein Hash.

Die Maschine schickt es bei jedem Aufruf mit: `Authorization: Bearer cms_…`

**→****Gut zu wissen:** Ein API-Benutzer ist gleichzeitig ein vollwertiger Admin – eingeschränkte Rollen gibt es nicht. Ein Token lässt sich weder bearbeiten noch erneuern: Für ein neues legst du einen neuen Zugang an und löschst den alten. Am besten bekommt jeder Agent seinen eigenen Zugang. Wie es danach weitergeht, zeigt die [Maschinen-Anleitung](/maschinen-anleitung).

## 8. System-Update, Backup und Rollback

Updates spielst du im Admin ein. Unter *Einstellungen* vergleicht der Bereich „System-Update“ die installierte mit der verfügbaren Version.

1
**Vorher sichern**
Einstellungen → Export → „ZIP exportieren“ – das Update sichert nur Systemdateien, keine Inhalte

2
**Update starten**
„Update auf … installieren“ klicken und bestätigen

3
**Hinweise lesen**
Unter „Hinweise zum letzten Update“ steht, was nicht ersetzt wurde

4
**Notfalls zurück**
„Rollback zur vorherigen Version“ stellt den Stand vor dem Update wieder her

### Was beim Update passiert

CMF lädt das Update-Paket per HTTPS vom Update-Server, legt ein Backup aller Dateien an, die es ersetzt, und tauscht dann den Systemcode aus: `app/`, `index.php`, `admin.php`, `api.php` und das JavaScript. Behalten wird nur das jüngste Backup.

Unangetastet bleiben Inhalte, Medien, `public/files/`, `site.json`, Benutzer, Theme und `custom.css`.

### Angepasste Dateien bleiben (seit 1.17)

Seit Version 1.17 enthält das Update-Paket `public/.htaccess`, `base.css`, `admin.css` und die Schriften nicht mehr. Hast du diese Dateien angepasst – eigene Weiterleitungen, eigene CSP, Farben –, bleiben sie stehen. Unveränderte Standard-Dateien zieht CMF beim ersten Aufruf nach dem Update selbst nach, neue CSS-Regeln kommen dann über `cmf-compat.css` dazu. Eigene Schriften in `public/assets/fonts/` werden nicht gelöscht.

Aktualisierst du von einer Version vor 1.14, läuft die Content-Security-Policy zunächst nur beobachtend (Report-Only), bis in `site.json` ein `csp`-Eintrag steht. Auch darauf weist der Update-Bereich hin.

- [Anleitung für Menschen](/humanoid-anleitung)
- [Maschinen-Anleitung](/maschinen-anleitung)
- [API-Referenz](/api-anleitung)
- [Alle Anleitungen](/anleitungen)
- [Kontakt](/kontakt)
