# Protokoll v1 ## Überblick Die Schnittstelle zwischen Client und Manage-Server. Wer den mitgelieferten Client verwendet, braucht dieses Dokument nicht – es ist für eigene Clients, für Debugging und für die Fehlersuche mit `curl` gedacht. Basis-URL: `/api/v1/` ## Authentifizierung Jede Anfrage trägt zwei Header: ```http X-Manage-Instance: meinprojekt-prod X-Manage-Token: e4032c4dc51e9100… ``` Der Server speichert nur den SHA-256-Hash des Tokens und vergleicht in konstanter Zeit. Es gibt keine Sitzung, kein Cookie und kein gemeinsames Passwort. Fehlerantworten: | Status | Bedeutung | |---|---| | `401` | Header fehlen, Instanz unbekannt oder Token falsch – bewusst nicht unterscheidbar | | `403` | Instanz existiert, ist aber deaktiviert | | `405` | Falsche HTTP-Methode | | `429` | Zu viele fehlgeschlagene Authentifizierungen von dieser IP | Fehlgeschlagene Anmeldungen sind pro IP begrenzt, damit Instanz-Kennungen nicht durchprobiert werden können. Eine erfolgreiche Anmeldung setzt den Zähler zurück. Alle Fehlerantworten haben denselben Aufbau: ```json { "success": false, "error": "Authentifizierung fehlgeschlagen." } ``` ## GET manifest.php Liefert das Release, das die Instanz installieren soll. ```bash curl -s https://manage.example.org/api/v1/manifest.php \ -H "X-Manage-Instance: meinprojekt-prod" \ -H "X-Manage-Token: $TOKEN" ``` ```json { "success": true, "latest": "v1.3.0", "version": "v1.3.0", "package_url": "https://manage.example.org/api/v1/package.php?version=v1.3.0", "sha256": "70f17aae44a9afdd948de1767daa61f936bbecb52096753757791e230a22f024", "size": 2199, "published_at": "2026-08-20T09:20:43+00:00" } ``` `404`, wenn kein gültiges Release veröffentlicht ist. `package_url` wird aus der Serverkonfiguration (`MANAGE_PUBLIC_URL`) gebildet, nicht aus dem `Host`-Header der Anfrage. Ein gefälschter Header kann einen Client daher nicht auf einen fremden Server umlenken. ## GET package.php Liefert das Release-ZIP. ```bash curl -s -o release.zip \ "https://manage.example.org/api/v1/package.php?version=v1.3.0" \ -H "X-Manage-Instance: meinprojekt-prod" \ -H "X-Manage-Token: $TOKEN" ``` Antwort: `application/zip` mit `Content-Length` und `Cache-Control: private, no-store`. Bei Erfolg kein JSON. `400` bei ungültigem Versionsformat, `404`, wenn das Release nicht existiert. Der Client vergleicht Größe und SHA-256 mit dem Manifest und löscht die Datei bei Abweichung. Ein eigener Client muss das ebenso tun – ohne diese Prüfung wird beliebiger Code ausgerollt. ## POST backup.php Nimmt ein Backup-Archiv entgegen. `multipart/form-data`: | Feld | Pflicht | Bedeutung | |---|---|---| | `backup` | ja | die ZIP-Datei | | `filename` | nein | `backup-YYYYmmdd-HHMMSS[-N].zip`; ohne Angabe vergibt der Server einen Namen | | `sha256` | nein | Prüfsumme; wird serverseitig neu berechnet und verglichen | | `meta` | nein | JSON mit `trigger`, `file_count`, `source_bytes`, `app_version` | ```bash curl -s https://manage.example.org/api/v1/backup.php \ -H "X-Manage-Instance: meinprojekt-prod" \ -H "X-Manage-Token: $TOKEN" \ -F "filename=backup-20260820-092104.zip" \ -F "sha256=824f3f80…" \ -F 'meta={"trigger":"cron","file_count":3}' \ -F "backup=@backup-20260820-092104.zip" ``` ```json { "success": true, "instance": "meinprojekt-prod", "filename": "backup-20260820-092104.zip", "size": 427, "sha256": "824f3f80…", "retention": 30, "s3": { "enabled": false, "uploaded": false, "pending": 0 } } ``` Der Server prüft in dieser Reihenfolge: Upload-Fehlercode, `is_uploaded_file`, Größenlimit, ZIP-Signatur, Dateinamensmuster, Prüfsumme nach dem Speichern. Weicht die Prüfsumme ab, wird die Datei wieder gelöscht und `400` gemeldet. Ein vorhandener Dateiname wird nie überschrieben: Der Server hängt `-2`, `-3` an. Fehler beim S3-Archivieren lassen den Upload **nicht** fehlschlagen – die lokale Kopie ist gespeichert und wird später nachgezogen. ## POST heartbeat.php Statusmeldung. `application/json`: ```json { "version": "v1.3.0", "php_version": "8.3.6", "disk_free": 12884901888, "pending_migrations": 0, "last_backup_at": "2026-08-20T09:21:04+00:00" } ``` ```json { "success": true, "instance": "meinprojekt-prod", "latest": "v1.3.0", "update_available": false, "server_time": "2026-08-20T09:23:11+00:00" } ``` Alle Felder der Anfrage sind optional; fehlende Felder lassen den bisherigen Wert auf dem Server unverändert. Die Antwort ersetzt für einfache Überwachung einen eigenen Aufruf von `manifest.php`. ## Nebenwirkung jeder Anfrage Jede erfolgreich authentifizierte Anfrage aktualisiert `last_seen_at` und die letzte IP der Instanz. Die Übersicht im Manage-Server bleibt dadurch aktuell, auch wenn nur Backups laufen und nie ein Heartbeat gesendet wird. ## Weiter - [09_TROUBLESHOOTING](09_TROUBLESHOOTING.md) – was einzelne Fehlermeldungen bedeuten - [10_SECURITY](10_SECURITY.md) – Umgang mit dem Token