# Startklar `.klar.yml` – Referenz v1 Startklar ist eine Extension für [mittwald mStudio](https://www.mittwald.de/mstudio). Eine Datei `.klar.yml` im Projekt beschreibt, was ein Projekt braucht: App, Datenbanken, Container, Cronjobs, Mail, Backups und Domains. Startklar richtet das im mStudio ein und hält es laufend so, wie es in der Datei steht. Diese Seite beschreibt Version 1 des Dateiformats (`version: 1`). Sie liegt dauerhaft unter **https://startklar.cyber.house/v1/**, das JSON Schema unter **https://startklar.cyber.house/v1/schema.json**. > **Status:** Startklar ist in Entwicklung. Das Format ist festgelegt und wird geprüft, das Einrichten im mStudio entsteht gerade. Fragen, Fehler oder Wünsche: [Kontakt](https://www.cyberhouse.at/contact/). ## Schnellstart 1. Startklar in der Organisation im mStudio installieren und im Projekt aktivieren. 2. Eine `.klar.yml` ins Repository legen und deployen, z.B. nach `~/html/prod/current/`. 3. Den neuen Fundort im Startklar-Menü des Projekts freigeben. Ab dann richtet Startklar alles ein und gleicht Änderungen automatisch ab. Die kleinste gültige Datei: ```yaml # Startklar config — docs: https://startklar.cyber.house/v1/ # $schema: https://startklar.cyber.house/v1/schema.json version: 1 ``` Die zwei Kommentarzeilen sind optional, aber empfohlen: Die erste zeigt Menschen und KI-Agents, wo die Doku liegt, die zweite schaltet im Editor Autovervollständigung, Erklärungen beim Hovern und Prüfung ein – in VS Code und Cursor mit der YAML-Extension von Red Hat, in PhpStorm direkt. Was eine Datei anlegt, zeigt der [Editor](https://startklar.cyber.house/editor/): Datei einfügen, und daneben erscheinen Apps, Umgebungen, Datenbanken, Container und Domains – geprüft mit demselben Parser wie in der Extension. ## Wie Startklar arbeitet - **Fundorte:** Startklar sucht an diesen Stellen: - `~/html/.klar.yml` - `~/html/*/.klar.yml` - `~/html/*/current/.klar.yml` (Deployments mit Releases, z.B. Magallanes oder Deployer) - zusätzlich in allen Verzeichnissen, die eine gefundene Datei unter [`targets`](#target) angibt, auch tiefer, jeweils mit und ohne `current` - **Zeitplan:** Bekannte Dateien prüft Startklar alle 15 Sekunden auf Änderungen; nur wenn sich eine geändert hat, wird abgeglichen. Nach neuen Fundorten sucht es alle 5 Minuten – und sofort, wenn das Startklar-Menü im Projekt geöffnet wird. - **Fehler beim Anwenden:** Schlägt bei mittwald etwas fehl, versucht Startklar es selbst erneut, mit wachsendem Abstand. Den Stand zeigt das Menü. - **Freigabe:** Ein neuer Fundort wird erst eingerichtet, wenn er im Startklar-Menü des Projekts freigegeben wurde. - **Fehlerhafte Datei:** Ist die Datei ungültig, ändert Startklar gar nichts und zeigt die Fehler mit Zeilennummer im Projekt-Menü. - **Fehlende Abschnitte:** Was nicht in der Datei steht, fasst Startklar nicht an. Ohne `backups` bleiben z.B. die Backup-Einstellungen des Projekts, wie sie sind. Alles außer `version` ist optional. ## apps Die Apps an diesem Fundort, nach frei wählbarem Namen (`a-z`, `0-9`, `-`). Meist ist es eine, z.B. `web`. Ein Repository kann aber auch mehrere enthalten, etwa ein PHP-Backend und zwei Node-Frontends – mit `target` auch unterschiedlich pro Umgebung: ```yaml apps: backend: type: php # php, node, python, static version: "8.3" # nicht bei static documentRoot: public envFile: ../../shared/.env.local envValues: # eigene Werte für die envFile TYPO3_CONTEXT: prod: Production stage: Production/Staging software: composer: "2" imagemagick: latest phpExtensions: [excimer, apcu] phpIni: memory_limit: 512M session.save_handler: redis session.save_path: "tcp://${REDIS_HOST}:${REDIS_PORT}" shop: type: node version: "22" path: frontend/shop command: node server.js landing: type: node version: "22" path: frontend/landing command: node server.js target: prod # nur auf prod ``` Auf `prod` entstehen so drei Apps, auf `stage` zwei. | Feld | Beschreibung | | --------------- | --------------------------------------------------------------------------------------------------------- | | `type` | `php`, `node`, `python` oder `static` | | `version` | Version **in Anführungszeichen**, sonst liest YAML `8.10` als Zahl `8.1` | | `path` | Verzeichnis der App relativ zur Datei; Standard: das Verzeichnis der Datei | | `target` | nur an diesen Fundorten, siehe [target](#target) | | `documentRoot` | relativ zur Datei, z.B. `public`; Pflicht bei `php` und `static`, bei `node` und `python` nicht vorhanden | | `command` | nur `node` und `python`: Startbefehl | | `envFile` | Pfad der `.env`-Datei der App, siehe [Variablen](#variablen) | | `envValues` | eigene Werte zusätzlich zu den Zugangsdaten, siehe [Variablen](#envvalues-eigene-werte) | | `software` | zusätzliche Systemsoftware mit Version; entfernte Einträge werden deinstalliert | | `phpExtensions` | nur `php`: einzuschaltende Erweiterungen | | `phpIni` | nur `php`: php.ini-Werte, `${NAME}` wird ersetzt | Zwei Apps dürfen am selben Fundort nicht im selben Verzeichnis liegen. Gibt es mehr als eine App, geben `routing` und `cronjobs` mit `app` an, welche gemeint ist. **php.ini:** Die Werte landen in `/tmp/php/php.ini`. Diese Datei gilt für das **ganze Projekt**: Setzen zwei Fundorte unterschiedliche Werte für denselben Schlüssel, meldet Startklar einen Konflikt und schreibt nichts. Startklar schreibt die Datei nur, wenn sich der Inhalt ändert, weil jedes Schreiben PHP neu startet. ## databases MySQL- und Redis-Datenbanken, nach frei wählbarem Namen (`a-z`, `0-9`, `-`). ```yaml databases: db: type: mysql version: "8.4" charset: utf8mb4 collation: utf8mb4_unicode_ci target: [prod, stage] envValues: DB_HOST: ${host} DB_PORT: ${port} DB_NAME: ${name} DB_USER: ${user} DB_PASSWORD: ${password} cache: type: redis version: "7.2" shared: true maxMemory: 512mb maxMemoryPolicy: allkeys-lru persistent: false envValues: REDIS_HOST: ${host} REDIS_PORT: ${port} ``` | Feld | MySQL | Redis | Beschreibung | | ---------------------- | -------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `type` | `mysql` | `redis` | | | `version` | ✓ | ✓ | in Anführungszeichen | | `charset`, `collation` | ✓ | | | | `maxMemory` | | ✓ | z.B. `512mb` | | `maxMemoryPolicy` | | ✓ | `noeviction`, `allkeys-lru`, `allkeys-lfu`, `allkeys-random`, `volatile-lru`, `volatile-lfu`, `volatile-random`, `volatile-ttl` | | `persistent` | | ✓ | Daten auf Platte sichern | | `shared`, `target` | ✓ | ✓ | siehe [target](#target) und [shared](#shared) | | `envValues` | `${…}`: `host`, `port`, `name`, `user`, `password` | `${…}`: `host`, `port` | siehe [Variablen](#an-ressourcen) | ## containers Container nach Name. ```yaml containers: solr: image: ghcr.io/acme/solr:9.6 port: 8983 shared: true volumes: - solr-data:/var/solr limits: cpus: "1" memory: 2g secrets: - SOLR_PASSWORD envValues: SOLR_HOST: ${host} SOLR_PORT: ${port} SOLR_PASSWORD: ${SOLR_PASSWORD} worker: image: ghcr.io/acme/worker:1.4 target: prod # nur auf prod command: ["php", "bin/worker", "--queue=default"] environment: DATABASE_URL: "mysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}" LOG_LEVEL: info ``` | Feld | Beschreibung | | ------------------ | --------------------------------------------------------------------------------------------------------------------- | | `image` | Docker-Image; private Registries siehe [registries](#registries) | | `port` | Port, auf dem der Container lauscht | | `command` | Befehl als Liste, statt dem Standardbefehl des Images | | `environment` | Variablen **im** Container, für das Programm darin; `${NAME}` meint eine Variable aus den `envValues` einer Ressource | | `volumes` | `":"` | | `limits` | `cpus`, `memory` | | `secrets` | siehe unten | | `envValues` | Variablen **für die Apps**, in deren `envFile`; `${…}`: `host`, `port` und Namen von Secrets | | `shared`, `target` | siehe [target](#target) und [shared](#shared) | `environment` und `envValues` gehen in entgegengesetzte Richtungen: `environment` konfiguriert den Container selbst (z.B. bekommt der Worker die `DATABASE_URL`), `envValues` sagt den Apps, wie sie den Container erreichen (z.B. bekommt die TYPO3-App `SOLR_HOST` in ihre `.env`). Ein Container kann beides brauchen. **An eine gemeinsame Instanz hängen:** Liegen mehrere Repositories im selben Projekt – jedes mit eigener `.klar.yml` –, kann eines einen Container mit `shared: true` definieren und die anderen hängen sich nur an. Ein Eintrag **ohne** `image` legt nichts an, sondern nutzt die gleichnamige Instanz und bekommt deren Zugangsdaten in die eigene `envFile`. Erlaubt sind dann nur `shared`, `target` und `envValues`: ```yaml # ~/html/website/.klar.yml – definiert die Instanz containers: solr: image: solr:9.6 port: 8983 shared: true secrets: [SOLR_PASSWORD] envValues: SOLR_HOST: ${host} SOLR_PASSWORD: ${SOLR_PASSWORD} ``` ```yaml # ~/html/shop/.klar.yml – hängt sich nur an containers: solr: shared: true envValues: SOLR_HOST: ${host} SOLR_PASSWORD: ${SOLR_PASSWORD} ``` Definiert keine Datei im Projekt die Instanz, wird der Eintrag nicht angewendet und das Startklar-Menü zeigt es an. ### Secrets Startklar erzeugt Secrets einmalig, speichert sie verschlüsselt, übergibt sie dem Container als Umgebungsvariable und schreibt sie über die `envValues` des Containers (z.B. `SOLR_PASSWORD: ${SOLR_PASSWORD}`) in die `envFile` jeder App, die den Container nutzt – auch bei gemeinsamen Instanzen. ```yaml secrets: - SOLR_PASSWORD # 32 Zeichen, A-Z a-z 0-9 - name: LEGACY_API_PASSWORD length: 12 # 8 bis 256 chars: "A-Za-z0-9!#%" # wie eine Regex-Zeichenklasse pattern: "^[A-Za-z].*[0-9]" # wird neu erzeugt, bis es passt ``` ## smtp Ein Postfach zum Versenden von Mails aus der App – für alles, was die App selbst verschickt, z.B. Kontaktformulare oder Passwort-Links. Startklar legt das Postfach an und schreibt die Zugangsdaten in die `envFile`. ```yaml smtp: envValues: SMTP_HOST: ${host} SMTP_PORT: ${port} SMTP_USER: ${user} SMTP_PASSWORD: ${password} ``` Mit [`shared`](#shared) und [`target`](#target) wie bei Datenbanken. ## cronjobs Cronjobs nach Name, ausgeführt in einer App des Fundorts. Gibt es mehrere Apps, sagt `app`, in welcher. ```yaml cronjobs: scheduler: schedule: "*/5 * * * *" command: vendor/bin/typo3 scheduler:run app: backend # nur nötig bei mehreren Apps target: prod timeout: 600 timezone: Europe/Vienna email: ops@acme.at ``` | Feld | Beschreibung | | ---------- | ------------------------------------------------- | | `schedule` | Cron-Zeitplan mit 5 Feldern, in Anführungszeichen | | `command` | Befehl, im Verzeichnis der App | | `app` | App aus `apps`; nur nötig bei mehreren Apps | | `timeout` | Sekunden | | `timezone` | z.B. `Europe/Vienna` | | `email` | Benachrichtigung bei Fehlern | ## backups Backup-Zeitpläne für das **ganze Projekt**. Definieren mehrere Dateien Backups, müssen sie übereinstimmen, sonst meldet Startklar einen Konflikt. Ohne `backups` bleiben die Einstellungen des Projekts unverändert. ```yaml backups: - schedule: "0 3 * * *" keep: 14d ``` ## registries Nur nötig, wenn Container-Images aus einer **privaten** Registry kommen, z.B. eigene Images auf `ghcr.io`; öffentliche Images wie auf Docker Hub brauchen keinen Eintrag. Angegeben wird nur der Hostname. Die Zugangsdaten stehen **nicht** in der Datei, sondern werden einmal pro Organisation im Startklar-Menü hinterlegt. ```yaml registries: - ghcr.io ``` ## routing Domains als Liste von Einträgen. Jeder Eintrag zeigt auf **eine** App, **einen** Container oder leitet weiter. Auch Hunderte Domains bleiben so übersichtlich. TLS-Zertifikate kommen automatisch von Let's Encrypt. ```yaml routing: - domains: [www.acme.at, www.acme.de] target: prod - domains: [stage.acme.at] target: stage - domains: [shop.acme.at] app: shop # nur nötig bei mehreren Apps target: prod - domains: [www.acme.at/search] container: solr - domains: [acme.at, acme.de] redirect: https://www.{domain} - domains: [acme-online.at] redirect: https://www.acme.at ``` | Art | Felder | Regeln | | ------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------- | | App | `target`, bei mehreren Apps `app` | genau ein Verzeichnis; die App muss dort existieren | | Container | `container`, optional `target`, `port` | `target` ist Pflicht, wenn der Container nicht `shared` ist; `port`, wenn der Container keinen hat | | Weiterleitung | `redirect` | nicht mit `target`, `app` oder `container` kombinierbar | - Domains dürfen einen Pfad haben: `www.acme.at/search`. - Jede Domain darf nur einmal vorkommen, auch über mehrere Einträge. - Im `redirect` wird `{domain}` durch die jeweilige Domain ersetzt, `{domain-without-www}` durch dieselbe ohne führendes `www.`. ## target Meist liegt dieselbe Datei in mehreren Verzeichnissen, z.B. für prod und stage. Jeder Fundort bekommt eigene Apps und eigene Ressourcen. Mit `target` gilt ein Eintrag nur für bestimmte Fundorte: ```yaml cronjobs: scheduler: schedule: "*/5 * * * *" command: vendor/bin/typo3 scheduler:run target: prod # oder eine Liste: [prod, stage] ``` Ohne `target` gilt ein Eintrag überall. Im `routing` ist genau ein Ziel erlaubt, weil eine Domain nur auf eine App an einem Fundort zeigen kann. ### targets Welcher Fundort welches target ist, lässt sich auf zwei Arten festlegen: **Ohne `targets`:** Ein target ist das gleichnamige Verzeichnis direkt unter `~/html`. `target: prod` passt auf `~/html/prod/.klar.yml` und `~/html/prod/current/.klar.yml` – nicht auf `preprod`. **Mit `targets` (optional, empfohlen):** Die Umgebungen stehen oben in der Datei, jeweils mit ihrem Verzeichnis: ```yaml targets: prod: ~/html/path/to/prod stage: ~/html/path/to/stage-new ``` - Jeder Pfad beginnt mit `~/html` und meint das Deploy-Verzeichnis, mit oder ohne `current`. - Pfade dürfen auch tiefer liegen; Startklar sucht dort zusätzlich. - Jedes `target` in der Datei muss einer dieser Namen sein, Tippfehler wie `prdo` fallen sofort auf. - An Fundorten, die zu keinem Pfad passen, wendet Startklar nichts an und meldet das im Menü. ## shared Standardmäßig bekommt jeder Fundort eigene Datenbanken, Container und SMTP-Postfächer. Mit `shared: true` gibt es **eine** Instanz für das ganze Projekt, verbunden über den Namen: ```yaml databases: cache: type: redis version: "7.2" shared: true # ein Redis für prod und stage ``` `shared` ist nie der Standard. Eine Datei kann sich an eine gemeinsame Instanz hängen, die in einer anderen Datei definiert ist, siehe [Container](#containers). ## Pfade Alle Pfade sind relativ zum **echten** Ort der `.klar.yml`, so wie sie im Dateisystem liegt. Bei Deployments mit Releases ist das `~/html/prod/releases//`, erreicht über den Symlink `current`. | Angabe | Bedeutung | | ---------------------------------- | --------------------------------------------------------------------------------- | | `documentRoot: public` | `~/html/prod/current/public` – Pfade im Release werden auf `current/…` abgebildet | | `envFile: ../../shared/.env.local` | `~/html/prod/shared/.env.local` – Pfade außerhalb bleiben, wie sie sind | Eine App ohne `path` wird im Verzeichnis oberhalb von `current` installiert, also `~/html/prod`. Mit `path: frontend/shop` liegt sie in `~/html/prod/current/frontend/shop`. ## Variablen Jede App kann eine `.env`-Datei bekommen. Was hineinkommt, steht überall gleich als **`envValues`** – immer `NAME: wert`: | Wo | Was | Landet in | | ------------------------------------------------------ | --------------------------------------------------------- | ---------------------------------------------------------------------- | | `envFile` bei der App | **wohin:** Pfad der `.env`-Datei, relativ zur `.klar.yml` | – | | `envValues` bei Datenbanken, Redis, Containern, `smtp` | Zugangsdaten und Werte dieser Ressource | der `envFile` **jeder** App an den targets der Ressource – automatisch | | `envValues` bei der App | eigene Werte | nur der `envFile` **dieser** App | ```text .env der App = envValues aller Ressourcen, die an diesem Fundort gelten (automatisch) + envValues der App ``` Startklar verwaltet in der Datei nur den Abschnitt zwischen `#startklar` und `#endklar`. Alles andere bleibt unverändert: ```dotenv APP_ENV=prod #startklar DB_HOST=mysql-abc123.pg-s-xyz.db.project.host DB_PASSWORD=… TYPO3_CONTEXT=Production #endklar ``` ### An Ressourcen In den `envValues` einer Ressource meint `${…}` ein **Feld dieser Ressource**. So kommen Zugangsdaten unter dem Namen in die `.env`, den die App erwartet – auch zusammengesetzt oder als fester Wert: ```yaml databases: db: type: mysql version: "8.4" envValues: DB_HOST: ${host} DB_PASSWORD: ${password} DATABASE_URL: "mysql://${user}:${password}@${host}/${name}" ``` | Ressource | Felder für `${…}` | | --------- | -------------------------------------------------------------- | | MySQL | `host`, `port`, `name`, `user`, `password` | | Redis | `host`, `port` | | Container | `host`, `port` (wenn angegeben) und die Namen seiner `secrets` | | `smtp` | `host`, `port`, `user`, `password` | ### An Apps Eigene Werte für die `envFile` einer App – z.B. den Kontext pro Umgebung, sodass in der `.env.local` nichts mehr von Hand gepflegt werden muss: ```yaml apps: backend: type: php version: "8.3" documentRoot: public envFile: ../../shared/.env.local envValues: TYPO3_CONTEXT: # ein Wert pro target prod: Production stage: Production/Staging LOG_LEVEL: info # überall gleich ``` ### Regeln - **`${…}` außerhalb von Ressourcen** – in den `envValues` einer App, in `phpIni` und im `environment` von Containern – meint eine Variable, die eine Ressource in ihren `envValues` definiert, z.B. `tcp://${REDIS_HOST}:${REDIS_PORT}`. - **Werte pro target:** Ein einfacher Wert gilt überall, eine Angabe pro target nur dort – und nur an targets, an denen es die Ressource bzw. App gibt. - **Namen:** Jeder Name der Ressourcen darf nur einmal vorkommen; die `envValues` einer App dürfen keinen davon wiederholen. Verschiedene Apps dürfen denselben Namen verwenden. - **`envFile`:** Eine App mit eigenen `envValues` braucht eine `envFile`. Ohne `envFile` bekommt eine App auch keine Zugangsdaten. ### Reihenfolge Platzhalter brauchen die echten Werte. Startklar geht deshalb bei jedem Abgleich in dieser Reihenfolge vor: 1. Secrets erzeugen 2. Datenbanken, Redis und SMTP-Postfach anlegen und warten, bis sie bereit sind 3. Container anlegen – mit aufgelösten Platzhaltern in `environment` 4. Apps anlegen, samt Software und PHP-Erweiterungen 5. `envFile` schreiben – `envValues` der Ressourcen und der App 6. php.ini schreiben – mit aufgelösten Platzhaltern 7. Cronjobs, Routing und Backups 8. `STARTKLAR_HASH` in die `envFile` schreiben (siehe [Im Deploy warten](#im-deploy-warten)) Fehlt für einen Platzhalter der Wert, weil die zugehörige Ressource noch nicht bereit ist, schreibt Startklar weder die php.ini noch den betroffenen Container noch den Hash und versucht es später erneut. Eine halb aufgelöste Einstellung wie `tcp://:` entsteht so nie. ## Was Startklar ändert und löscht | Fall | Verhalten | | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | | Neuer Eintrag | wird angelegt | | Geänderter Eintrag | wird angepasst | | Entfernte Einstellung ohne Daten (Software, PHP-Erweiterung, Cronjob, Variable, Container-Konfiguration) | wird automatisch entfernt | | Entfernte Ressource mit Daten oder sichtbarer Wirkung (Datenbank, Redis, Container mit Volume, Domain, App) | wird erst nach Bestätigung im Startklar-Menü entfernt | | Ungültige Datei | nichts wird geändert, Fehler mit Zeilennummer im Menü | | Konflikt zwischen Dateien (Domain doppelt, php.ini, Backups) | betroffener Teil wird nicht angewendet, Konflikt im Menü | Startklar ändert nur, was es selbst angelegt hat. ### Aufräumen (Projekt-Einstellung) Wer es aufgeräumt haben will, kann im Startklar-Menü des Projekts einstellen, dass Ressourcen ohne Eintrag in einer `.klar.yml` automatisch entfernt werden – pro Art einzeln, standardmäßig ist alles aus. Die Einstellung steht bewusst nicht in der Datei: Ein Commit allein soll nie Daten löschen können. | Einstellung | Verhalten | | --------------------------------------------- | ---------------------------------------------------------------------------------------- | | Domains | werden entfernt | | Container ohne Volumes | werden entfernt | | Container mit Volumes, Datenbanken, Redis | werden zum Löschen vorgemerkt und nach 24 Stunden entfernt; bis dahin im Menü abbrechbar | | Auch nicht von Startklar angelegte Ressourcen | gilt zusätzlich für Ressourcen, die von Hand angelegt wurden | Unabhängig davon gilt immer: - Aufgeräumt wird nur, wenn im selben Durchlauf **alle** freigegebenen Fundorte gelesen wurden und gültig sind. - Sieht eine Änderung wie eine Umbenennung aus (eine Ressource verschwindet, eine gleichartige kommt dazu), fragt Startklar immer nach. - Apps werden nie automatisch entfernt. - Ressourcen, die in keiner Datei stehen, zeigt das Menü an – zum Entfernen oder zum Übernehmen in die Datei. ## Prüfung und Fehler Startklar prüft die Datei vollständig, bevor es etwas tut. Jeder Fehler kommt mit Pfad und Zeile, z.B.: ```text routing[1].domains[0] (Zeile 84): www.acme.at kommt schon in routing[0] vor ``` Unbekannte Schlüssel sind Fehler, damit Tippfehler wie `docRoot` statt `documentRoot` auffallen. ## Im Deploy warten Startklar bemerkt eine geänderte `.klar.yml` innerhalb von 15 Sekunden nach dem Deploy und gleicht sie dann ab, einen neuen Fundort erst nach der Freigabe im Menü. Damit z.B. Datenbank-Migrationen beim ersten Aufsetzen erst laufen, wenn die Datenbank existiert, wartet Startklar nach dem Anlegen, bis mittwald jede Ressource als bereit meldet (Datenbanken und Redis `ready`, Apps `ready`, Container `running`), und schreibt erst dann – als **letzten** Schritt – den Hash der angewendeten Datei in den eigenen Block der `envFile`. Auf Domains und TLS-Zertifikate wird dabei nicht gewartet, ihren Stand zeigt das Startklar-Menü: ```dotenv #startklar DB_HOST=… DB_PASSWORD=… STARTKLAR_HASH=3f9a… #endklar ``` Steht dort der Hash der aktuellen `.klar.yml`, ist alles angewendet – auch die Zugangsdaten in derselben Datei. Warten lässt sich darauf mit Bordmitteln, im Verzeichnis der `.klar.yml` (z.B. im Deploy nach dem Umschalten von `current`, vor den Migrationen): ```bash timeout 99 sh -c 'until grep -q $(sha256sum .klar.yml | cut -c-64) ../../shared/.env.local; do sleep 5; done' ``` - `99` ist die maximale Wartezeit in Sekunden; danach endet der Befehl mit Exit-Code `124` und der Deploy bricht ab. - `../../shared/.env.local` ist die `envFile` der App, relativ zur `.klar.yml`. - Blockiert wird nur, was die App zum Laufen braucht: Datenbanken, Redis, Apps, Software, PHP und die `envFile`. Probleme mit Domains, Zertifikaten oder Backups und ausstehende Bestätigungen erscheinen als Warnung im Menü, der Hash wird trotzdem geschrieben. - Ist die Datei ungültig oder schlägt etwas dauerhaft fehl, kommt der Hash nicht und der Befehl endet nach der Wartezeit; die Fehler stehen im Startklar-Menü. Fehler in der Datei selbst lassen sich schon vor dem Deploy erkennen – im Editor dank der `$schema`-Zeile oder in der Pipeline mit einem beliebigen JSON-Schema-Validator, z.B.: ```bash pipx run check-jsonschema --schemafile https://startklar.cyber.house/v1/schema.json .klar.yml ``` Ändert sich ein Dienst, kann es während des Abgleichs kurz eine Downtime geben – das Warten verhindert nur, dass der Deploy in dieser Zeit weiterläuft. ## Berechtigungen und Sicherheit - **Installation:** Startklar wird einmal pro Organisation im mStudio installiert. Dabei bestätigt man, worauf es zugreifen darf: Apps, Datenbanken, Container, Domains, Cronjobs, Mail, Backups, Registries, Projekte und SSH-Benutzer. - **Aktivierung:** Startklar arbeitet nur in Projekten, in denen es im Startklar-Menü des Projekts aktiviert ist, und richtet neue Fundorte erst nach Freigabe ein. - **Lesen und Schreiben:** Die `.klar.yml` liest Startklar über die mStudio-API. Schreiben – den eigenen Block in der `envFile` und die php.ini – erledigt es über einen eigenen SSH-Benutzer, den es im Projekt anlegt. Andere Dateien fasst es nicht an. - **Secrets:** Erzeugte Passwörter und Registry-Zugangsdaten werden verschlüsselt gespeichert und im Menü nie angezeigt. - **Sichtbarkeit:** Im Menü sieht jede Person nur Projekte, auf die sie im mStudio selbst Zugriff hat. Organisationen sind strikt voneinander getrennt. ## Was Startklar nicht macht - **Code deployen** – das bleibt bei Magallanes, Deployer, Git oder deiner Pipeline. - **Postfächer für Menschen** – nur das Versand-Postfach der App (`smtp`). - **DNS-Einträge** – Domains müssen bereits auf mittwald zeigen. - **Eigene TLS-Zertifikate** – Zertifikate kommen von Let's Encrypt. - **SSH-Benutzer und Projekt-Mitglieder für Menschen.** - **KI-Hosting.** ## Für KI-Agents Wenn du als KI-Agent eine `.klar.yml` bearbeitest: - Das Format ist hier vollständig beschrieben; die maschinenlesbare Form ist https://startklar.cyber.house/v1/schema.json, die ganze Doku als Text https://startklar.cyber.house/llms-full.txt. - Versionen, Zeitpläne und Werte mit `:` oder `{` immer in Anführungszeichen. - Laufen im Deploy Migrationen, davor auf `STARTKLAR_HASH` in der `envFile` warten (siehe „Im Deploy warten“). - Keine Zugangsdaten in die Datei schreiben: Passwörter erzeugt Startklar (`secrets`), Registry-Zugangsdaten kommen ins Startklar-Menü. - Bei mehreren Umgebungen `targets` mit den Pfaden angeben und nur diese Namen in `target` verwenden. - `shared: true` nur setzen, wenn eine Ressource wirklich von mehreren Fundorten genutzt werden soll. - Pfade relativ zum echten Ort der Datei angeben (bei Releases: `releases//`). - Fehlende Abschnitte bedeuten „nicht verwalten“, nicht „löschen“. ## Beispiel Ein vollständiges Beispiel mit allen Funktionen: TYPO3-Backend und zwei Node-Frontends, deployt nach `~/html/prod` und `~/html/stage`. Fahre über einen Eintrag – oder tippe ihn an –, um zu sehen, was er bewirkt. Zum Herunterladen: [example.klar.yml](https://startklar.cyber.house/v1/example.klar.yml). ```yaml # Startklar config — docs: https://startklar.cyber.house/v1/ # $schema: https://startklar.cyber.house/v1/schema.json # # Example with every feature: TYPO3 backend plus two # Node frontends, deployed via Magallanes. # Everything except "version" is optional, missing # sections are not touched. Paths are relative to # the real location of this file (releases/). version: 1 # Optional: environments and their deploy directories. # Without it, "target: prod" means ~/html/prod. targets: prod: ~/html/prod stage: ~/html/acme/staging apps: backend: type: php version: "8.3" documentRoot: public # Startklar only writes its own block in this file envFile: ../../shared/.env.local # Own values for this envFile, in addition to the credentials envValues: TYPO3_CONTEXT: prod: Production stage: Production/Staging LOG_LEVEL: info software: composer: "2" imagemagick: latest phpExtensions: - excimer - apcu # Global for the project; ${NAME} from the envValues of a resource phpIni: memory_limit: 512M max_execution_time: 120 upload_max_filesize: 64M post_max_size: 64M session.save_handler: redis session.save_path: "tcp://${REDIS_HOST}:${REDIS_PORT}" shop: type: node version: "22" path: frontend/shop command: node server.js envFile: ../../shared/frontend/shop/.env.local landing: type: node version: "22" path: frontend/landing command: node server.js target: prod # only on prod databases: db: type: mysql version: "8.4" charset: utf8mb4 collation: utf8mb4_unicode_ci envValues: DB_HOST: ${host} DB_PORT: ${port} DB_NAME: ${name} DB_USER: ${user} DB_PASSWORD: ${password} cache: type: redis version: "7.2" shared: true # one Redis for prod and stage maxMemory: 512mb maxMemoryPolicy: allkeys-lru persistent: false envValues: REDIS_HOST: ${host} REDIS_PORT: ${port} # Credentials go into the Startklar UI, not here registries: - ghcr.io containers: solr: image: ghcr.io/acme/solr:9.6 port: 8983 shared: true # one instance for prod and stage volumes: - solr-data:/var/solr limits: cpus: "1" memory: 2g secrets: - SOLR_PASSWORD # generated once, 32 chars envValues: SOLR_HOST: ${host} SOLR_PORT: ${port} SOLR_PASSWORD: ${SOLR_PASSWORD} worker: image: ghcr.io/acme/worker:1.4 target: prod command: ["php", "bin/worker", "--queue=default"] environment: DATABASE_URL: "mysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}" REDIS_URL: "redis://${REDIS_HOST}:${REDIS_PORT}" LOG_LEVEL: info legacy-api: image: ghcr.io/acme/legacy-api:2.1 port: 8080 target: prod secrets: - name: LEGACY_API_PASSWORD length: 12 chars: "A-Za-z0-9!#%" pattern: "^[A-Za-z].*[0-9]" envValues: LEGACY_API_HOST: ${host} LEGACY_API_PORT: ${port} LEGACY_API_PASSWORD: ${LEGACY_API_PASSWORD} mailpit: image: axllent/mailpit:v1.21 port: 8025 target: stage smtp: target: prod envValues: SMTP_HOST: ${host} SMTP_PORT: ${port} SMTP_USER: ${user} SMTP_PASSWORD: ${password} cronjobs: scheduler: schedule: "*/5 * * * *" command: vendor/bin/typo3 scheduler:run app: backend target: prod timeout: 600 timezone: Europe/Vienna email: ops@acme.at cache-warmup: schedule: "0 6 * * *" command: vendor/bin/typo3 cache:warmup app: backend target: [prod, stage] # Project-wide: all files must agree backups: - schedule: "0 3 * * *" keep: 14d - schedule: "0 4 * * 0" keep: 90d routing: - domains: - www.acme.at - www.acme.de app: backend target: prod - domains: - stage.acme.at app: backend target: stage - domains: - shop.acme.at app: shop target: prod - domains: - shop.stage.acme.at app: shop target: stage - domains: - start.acme.at app: landing target: prod - domains: - www.acme.at/search container: solr - domains: - api.acme.at container: legacy-api target: prod port: 8080 - domains: - mail.stage.acme.at container: mailpit target: stage - domains: - acme.at - acme.de redirect: https://www.{domain} - domains: - www.acme-online.at redirect: https://{domain-without-www} ``` ## Änderungen am Format | Datum | Änderung | | ------- | --------- | | 2026-11 | Version 1 | Neue, optionale Felder kommen in Version 1 dazu. Inkompatible Änderungen bekommen eine neue Version mit eigener Doku; diese Seite bleibt erhalten. --- Quelle: https://startklar.cyber.house/v1/ · Startklar 0.1.0