Web-Client-Deployment & Channel-Modell
Wie eine neue Web-Client-Version auf die Tenant-Hosts kommt — ohne dass jeder
git pushdirekt auf Produktion landet. Dokumentation des Channel-Modells (seit 12.05.2026) und des Promote-Workflows.
Dieses Kapitel ergänzt die Tenant-Spec & Reconcile-Doku um die Web-Client-Schicht.
Wann ist das relevant?
- Operator der eine neue Web-Client-Version freigeben will → Promote
latest → stable - Operator der nachsehen will, welche Version gerade live ist → Admin-Portal oder CLI
- Bug-Report „Mein Handy zeigt eine alte UI" → Drift-Check + ggf. Reconcile
Das Problem
Bis 12.05.2026 gab es keinen klaren Promote-Schritt. CI baute bei jedem main-Push ein Tarball ins prilog-artifacts-Repo, aber Deploy auf die Tenant-Hosts war manuell (scp + tar xzf). Die zwei realistischen Wege wären beide unbefriedigend:
- Manuell bleiben → bei jedem Web-Client-Update muss ein Mensch dran denken; oft passierte das nicht und die Hosts liefen wochenlang mit veraltetem Bundle (siehe Connector-Drift 10.05.).
- Auto-Deploy bei jedem Push → jeder Komma-Bug landet 3 Min später auf einem produktiven Tenant. Zu scharf.
Lösung: Channel-Modell mit explizitem Promote-Schritt.
Channel-Modell
Drei Tarball-Sorten in /var/www/prilog-artifacts/prilog-web-client/ (auf api.prilog.chat):
| Datei | Bedeutung | Wer schreibt? |
|---|---|---|
prilog-web-client-latest.tar.gz | HEAD vom letzten CI-Build (Entwicklung) | CI bei jedem main-Push |
prilog-web-client-stable.tar.gz | Symlink auf die freigegebene Version (Production) | Operator manuell |
prilog-web-client-<sha>.tar.gz | Immutables Versions-Archiv | CI bei jedem main-Push |
Reconcile + Smoke vergleichen ausschließlich gegen stable. latest wird absichtlich ignoriert.
Push to main
│
▼
┌─────────────────────────────────┐
│ CI (GitHub Actions) │
│ ├─ npm run build │
│ ├─ tar czf prilog-web-client- │
│ │ <sha>.tar.gz dist/ │
│ └─ git commit + push │
│ ins prilog-artifacts-Repo │
└────────────┬────────────────────┘
│
▼
prilog-artifacts/prilog-web-client/
├─ prilog-web-client-<sha>.tar.gz (neu, immutable)
└─ prilog-web-client-latest.tar.gz (ueberschrieben)
── HIER PASSIERT NICHTS WEITER. ──
Operator entscheidet:
┌─────────────────────┐
│ promote-web-client │ ← manueller Schritt
│ <sha> │ (siehe unten)
└──────────┬──────────┘
│
▼
prilog-web-client-stable
.tar.gz → prilog-web-
client-<sha>.tar.gz
Reconcile-Cron 03:30 UTC (oder
Admin-Portal "Reconcile"-Knopf)
swappt die Tenant-Hosts auf die
neue stable-Version.Promote-Workflow
1. CI-Build pruefen
Nach git push main:
gh run list --limit 1
# completed success ui(hubs): ... Build main push ... 2m56s ...Status success heißt: Tarball wurde gebaut und ins Artifact-Repo gepusht. Auf api.prilog.chat ist es typischerweise innerhalb von 30 Sekunden via git pull da.
2. (Optional) Auf Test-Tenant ausprobieren
Bevor du stable umbiegst, kannst du eine Version selektiv auf einem Test-Tenant (z.B. demo) testen. Manueller Override:
ssh -i ~/.ssh/prilog lee@api.prilog.chat
ARTIFACT=/var/www/prilog-artifacts/prilog-web-client/prilog-web-client-<sha>.tar.gz
scp -i ~/.ssh/prilog "$ARTIFACT" root@<host>:/tmp/test.tar.gz
ssh -i ~/.ssh/prilog root@<host> "cd /var/www/prilog-web-client && rm -rf assets && tar xzf /tmp/test.tar.gz && rm /tmp/test.tar.gz"Test im Browser. Beim nächsten Reconcile wird das wieder auf stable zurückgesetzt — du machst keine bleibende Beschädigung.
3. Promote latest → stable
Wenn du sicher bist:
CLI (auf api.prilog.chat):
bash /var/www/backend-api/scripts/promote-web-client.sh <sha>
# oder
bash /var/www/backend-api/scripts/promote-web-client.sh latestOptionen:
promote-web-client.sh --list # Verfuegbare Versionen
promote-web-client.sh --current # Welche Version ist gerade stable?HTTP (vom Admin-Portal oder per curl):
POST /api/admin/web-client/promote
Body: { "sha": "4d031d3" } # oder { "sha": "latest" }
GET /api/admin/web-client/versions
# → { versions: [...], stable: "4d031d3", latestMatchesSha: "4d031d3" }4. Rollout
Nach dem Promote stehen drei Wege offen:
| Wenn… | Dann… |
|---|---|
| Du willst sofort | npx tsx scripts/reconcile-tenant.ts --all auf api.prilog.chat |
| Du willst kontrolliert pro Tenant | Admin-Portal → /tenant-health → „Reconcile" pro Tenant-Zeile |
| Es kann warten | Reconcile-Cron 03:30 UTC zieht automatisch nach |
Reconcile vergleicht sha256(index.html) auf dem Host mit dem aus stable.tar.gz. Bei Mismatch → atomic swap (extract nach .new, mv).
Wie der Drift-Check funktioniert
Der Inspector liest beim Reconcile:
sha256sum /var/www/prilog-web-client/index.htmlDer web-client-artifact.ts-Helper liest:
prilog-web-client-stable.tar.gz(aufapi.prilog.chat)- Extrahiert nur den
./index.html-Eintrag aus dem Tarball - Bildet
sha256(index.html-content)— cached 60 s
Der Drift-Detector vergleicht und reportet bei Mismatch:
{
"driftType": "wrong_version",
"component": "web-client",
"severity": "warning",
"expected": { "sha256": "..." },
"actual": { "sha256": "..." }
}severity: warning ist hier kein Zufall — der alte Bundle läuft weiter, kein Outage. Im Gegensatz dazu ist Connector-Drift critical, weil dann Flurfunk schweigt.
Warum index.html-Hash und nicht Bundle-Hash?index.html ist klein (~3 KB) und enthält die Filename-Hashes aller assets/*.js-Dateien. Wenn sich ein Asset ändert, ändert sich auch sein Hash → ändert sich die Referenz in index.html → ändert sich der index.html-Hash. Eine Datei zu vergleichen reicht für vollständige Bundle-Konsistenz.
Smoke-Probe web_client_current
Ist die 8. Probe im Smoke-Test (required: false — degraded statt failed bei Mismatch):
test -f /var/www/prilog-web-client/index.html && sha256sum index.htmlVergleicht mit getExpectedWebClientIndexSha256(). Drei Resultate:
| Lage | Probe-Ergebnis |
|---|---|
| Host-Hash == stable-Hash | ✅ Web-Client aktuell (4f6bb035… == stable) |
| Host-Hash != stable-Hash | ❌ Web-Client veraltet: host hat …, stable ist … |
| Kein stable-Tarball vorhanden | ✅ aber Warnhinweis im message |
Rollback
Wenn nach einem Promote eine fatale Regression auftaucht:
# stable auf vorherige Version zuruecksetzen
promote-web-client.sh <vorherige-sha>
# Sofort-Rollout
npx tsx scripts/reconcile-tenant.ts --allBrowser-Tabs ziehen via Service-Worker-Auto-Update (60 s Polling) automatisch die zurückgerollte Version nach.
Beziehung zu Spec v5
Spec v5 (tenant-box-v5.yaml) definiert web-client als Pflicht-Komponente vom Typ host_static_files mit channel: stable:
- name: web-client
type: host_static_files
scope: host # nicht pro Tenant — alle Tenants auf dem Host teilen sich
artifact:
source: /var/www/prilog-artifacts/prilog-web-client/prilog-web-client-stable.tar.gz
channel: stable
extract_to: /var/www/prilog-web-client
config:
drift_check: sha256_of_index_html
severity: warningDas macht den Web-Client zu einer erstklassigen Komponente neben Postgres, MinIO, Synapse und Matrix-Connector — Reconcile prüft ihn automatisch, Smoke testet ihn automatisch.
Was das Modell NICHT macht
- Kein Auto-Rollback bei kaputtem Bundle. Wenn
stableauf einen Build mit JS-Crash zeigt, müssen die User crash-loopen, bis der Operator zurückrollt. Lösung wäre eine zusätzliche Smoke-Probe „HTTP GET /, parse HTML, kein Console-Error in einem Headless-Browser" — Aufwand ~1 Tag, heute nicht implementiert. - Kein automatisches Promote von
latestnach X Stunden „Soak". Wenn du das willst: Cron-Jobif latest älter als 24h und kein Push: promote latest → stable. Auch nicht implementiert. - Keine Multi-Channel-Strategie (z.B.
beta,canary). Heute nur zwei Kanäle (latest+stable). Erweiterbar wenn nötig.