Deployment auf Shared-PHP-Hosting ohne SSH, ohne
curl, ohneexec, ohne Composer. Pfad-isolierte Schreib-API mit SHA-256-Optimistic-Locking, Single-File-Snapshots, content-adressierter Deduplikation und Ein-Klick-Rollback.
v0.1.0. Im produktiven Einsatz unter netzhandwerker.de seit April 2026. Verarbeitet rund 500 Deploys pro Woche aus einer Mischung aus menschlichen Commits, agent-getriebenen Edits und geplanten Batch-Syncs. Spitzen-Speicher: 0,6 MB. Bisher kein Datenverlust.
Für regulierte Produktion (Behörden, Gesundheit) ohne eigene Sicherheitsprüfung nicht empfohlen.
- Single-File-PHP-Gateway, in jeden Webroot droppbar
- Pfad-Traversal-Schutz:
validate_path()blockiert..,., Null-Bytes, gefährliche Endungen (.phar,.phtml,.php[3-7]) - Endungs-Whitelist: konfigurierbare Liste schreibbarer Endungen, alles andere wird abgewiesen
- Optimistic Locking via
If-Match: <sha256>-Header — Write bricht mit409 Conflictab, wenn die Datei sich geändert hat - Snapshots bei jedem Write, Rollback per Snapshot-ID
- Content-adressierte Deduplikation: gleiche Dateien über mehrere Snapshots werden einmal gespeichert (SHA-256-keyed)
- Rate-Limiting: global (30/min) + pro IP (10/min), file-basiert, kein Redis nötig
- Audit-Log: append-only mit Zeitstempel, Key-Fingerprint, IP, Pfad, Byte-Anzahl
- Atomare Batch-Deploys:
deploy_batchschreibt N Dateien unter einem Snapshot - Memory-Decke: streamt Dateien via
copy(), Peak-Memory unter 1 MB selbst bei vollen Site-Snapshots - Binary-Upload-Helfer: separater
bin-upload.phpfür Dateien über 800 KB (umgehtARG_MAXund POST-Size-Limits)
- API-Key hat vollen Schreibzugriff auf das Deploy-Verzeichnis — wie ein SSH-Key behandeln
- Kein Multi-Tenant-Setup: ein Key, ein Webroot
- Snapshots werden nach
max_snapsStück automatisch verworfen (Standard: 20) - Rate-Limit-State liegt im Filesystem — bei sehr hoher Last (>100 req/s) wird das eng
- Kein OAuth, kein User-Management — Authentifizierung ist API-Key-only
- Keine Audit-Log-Rotation eingebaut, manuelles Trimmen nötig
- Kein eingebautes Backup der Snapshots — falls Hoster die Webroot verliert, sind auch Snapshots weg
Die meisten „Deploy zu Shared Hosting"-Tools setzen voraus:
- SSH-Zugang
gitauf dem Servercomposerinstalliert- Funktionierendes
curl/openssl/shell_exec
Günstige deutsche und EU-Shared-Hoster (All-Inkl, Strato, IONOS, df.eu, host.de) deaktivieren das typisch alles. Was bleibt: FTP, ein Webroot, PHP 8.x mit minimalem Extension-Set — das war's.
Dieses Kit deployt in genau diese Umgebung. Das Gateway ist eine einzelne PHP-Datei, die in den Webroot gelegt wird. Ab dann kann jeder Client (Python, Node, Bash, GitHub Actions, ein KI-Agent) Dateien per HTTPS lesen, schreiben, auflisten und zurückrollen — ohne Shell, ohne FTP, ohne git.
- Keine CI/CD-Plattform. Es ist die Plumbing darunter.
- Keine Datei-Manager-UI. Nur API.
- Kein Ersatz für git. Es ergänzt git: Push zu git → CI ruft diese API → Dateien landen.
- Kein Zero-Trust. Der API-Key hat vollen Schreibzugriff. Wie SSH-Key behandeln.
scp src/deploy-gateway.php dein-host:~/public_html/Oder per FTP hochladen. Das ist die komplette Server-Installation.
deploy-gateway.php editieren und $API_KEY auf eine lange Zufalls-Zeichenkette setzen, oder eine Geschwister-Datei gateway.config.php anlegen:
<?php
return [
'api_key' => 'hier-32-zufalls-bytes',
'base_path' => __DIR__, // Wurzel des Deploy-Jails
'snap_dir' => __DIR__ . '/_snapshots',
'max_snaps' => 20,
];curl -X POST https://example.com/deploy-gateway.php \
-d "key=YOUR_KEY" \
-d "action=ping"Erwartete Antwort:
{"status":"ok","version":3,"snapshots":0,"memory_peak_mb":0.5}curl -X POST https://example.com/deploy-gateway.php \
-d "key=YOUR_KEY" \
-d "action=write" \
-d "path=index.html" \
--data-urlencode "content@./local-index.html"# examples/python_client.py
from deploy_client import Gateway
gw = Gateway("https://example.com/deploy-gateway.php", api_key="...")
current = gw.read("index.html")
new_content = current["content"].replace("alt", "neu")
gw.write("index.html", new_content, if_match=current["sha256"])
# Wirft ConflictError, wenn die Datei zwischen Read und Write geändert wurde| Action | Methode | Zweck |
|---|---|---|
ping |
POST | Health-Check, Snapshot-Anzahl, Memory-Stats |
read |
POST | Datei-Inhalt + SHA-256 + mtime lesen |
list |
POST | Verzeichnis listen (rekursiv optional) |
write / deploy |
POST | Datei schreiben (optional mit If-Match) |
write_b64 |
POST | Base64-codierte Binär-Datei schreiben |
deploy_batch |
POST | N Dateien atomar unter einem Snapshot schreiben |
delete |
POST | Datei löschen (Snapshot wird vorher erstellt) |
snapshot |
POST | Manuell Voll-Site-Snapshot erstellen |
snapshots |
POST | Alle Snapshots auflisten |
rollback |
POST | Snapshot per ID wiederherstellen |
Vollständige Referenz: docs/api.md
HTTPS POST
┌─────────────┐ (form-encoded) ┌──────────────────────┐
│ Client │ ─────────────────────────────▶ │ deploy-gateway.php │
│ (beliebig) │ │ (Webroot) │
└─────────────┘ ◀─────── JSON-Antwort ────── └──────────┬───────────┘
│
│ write/read/copy
▼
┌──────────────────────────────────────────────────────────────────┐
│ Webroot (pfad-isoliert) │
│ ├── index.html │
│ ├── style.css │
│ └── _snapshots/ │
│ ├── snap_20260519_104530/ │
│ │ └── manifest.json ← {Pfad → SHA-256} │
│ ├── snap_20260519_104612/ │
│ │ └── manifest.json │
│ └── _objects/ ← content-adressierter Speicher │
│ ├── 1a/2b3c4d... ← per SHA-256 dedupliziert │
│ └── ef/01234567... │
└──────────────────────────────────────────────────────────────────┘
Ein Snapshot ist nur ein JSON-Manifest, das relative Pfade auf SHA-256-Hashes mappt. Die eigentlichen Datei-Blobs liegen einmal in _objects/. Wenn 50 Snapshots dieselbe style.css referenzieren, ist sie einmal gespeichert.
Siehe docs/threat-model.md. Kurzfassung:
- Schützt gegen: Pfad-Traversal, gefährliche Endungen, parallele Überschreibungen, versehentliches Löschen
- Mildert: Brute-Force auf Key-Erraten (Rate-Limit), einfache Replays (Audit-Log)
- Schützt nicht gegen: gestohlenen API-Key, server-seitige RCE in eigenen PHP-Dateien, Supply-Chain-Angriffe
- Key wie SSH-Key behandeln. Quartalsweise rotieren.
- Continuous Deployment zu All-Inkl / Strato / IONOS — GitHub Action, die diese API bei jedem Push aufruft
- KI-Agent-Code-Edit-Loops — Claude / GPT schreibt Code → liest Server-Datei mit
If-Match→ wendet Patch an → schreibt zurück. Konflikt-sicher. - Multi-Umgebungs-Promotion — derselbe Client deployt Staging → Produktion durch Tausch der Base-URL
- Eingeschränkte PHP-Umgebungen — funktioniert auf Hostern, wo
curl,openssl_streams,shell_exec,mbstring,iconvdeaktiviert sind - Air-Gapped Einweg-Deploys — Server hat keine ausgehende Konnektivität, nimmt nur Pushes über dieses Gateway entgegen
examples/python_client.py— vollständige Python-Client-Klasse mitIf-Match-Round-Tripexamples/bash_deploy.sh— Curl-Einzeiler mit Rate-Limit-aware Sleepexamples/github-actions.yml— Push-to-Deploy aus GitHubexamples/agent_loop.py— Read → Edit → Write-Pattern für KI-Agenten
- v0.2: Pro-Pfad-Zugriffsrechte (mehrere Keys mit unterschiedlichen Schreibbereichen), Audit-Log-Rotation, Webhook-Notifications bei Rollback
- v0.3: OpenTelemetry-kompatibles Metric-Export, Snapshot-Sync zu externem Storage (S3/B2)
- v0.4: Web-UI-Companion (separate Repo) für Snapshot-Browsing und manuellen Rollback
MIT — siehe LICENSE
Gebaut von Daniel Wesseling — IT-Manufaktur „Die Netzhandwerker", Gronau (Westfalen), Deutschland.
shared-hosting-php-cookbook— Begleit-Doku: PHP-Workarounds für Umgebungen ohnecurl, ohnesession, ohnembstring, ohneiconv