Skip to content

Web-Client-Deployment & Channel-Modell

Wie eine neue Web-Client-Version auf die Tenant-Hosts kommt — ohne dass jeder git push direkt 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):

DateiBedeutungWer schreibt?
prilog-web-client-latest.tar.gzHEAD vom letzten CI-Build (Entwicklung)CI bei jedem main-Push
prilog-web-client-stable.tar.gzSymlink auf die freigegebene Version (Production)Operator manuell
prilog-web-client-<sha>.tar.gzImmutables Versions-ArchivCI 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:

bash
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:

bash
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
bash /var/www/backend-api/scripts/promote-web-client.sh <sha>
# oder
bash /var/www/backend-api/scripts/promote-web-client.sh latest

Optionen:

bash
promote-web-client.sh --list     # Verfuegbare Versionen
promote-web-client.sh --current  # Welche Version ist gerade stable?

HTTP (vom Admin-Portal oder per curl):

bash
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 sofortnpx tsx scripts/reconcile-tenant.ts --all auf api.prilog.chat
Du willst kontrolliert pro TenantAdmin-Portal → /tenant-health → „Reconcile" pro Tenant-Zeile
Es kann wartenReconcile-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:

bash
sha256sum /var/www/prilog-web-client/index.html

Der web-client-artifact.ts-Helper liest:

  • prilog-web-client-stable.tar.gz (auf api.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:

json
{
  "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.html

Vergleicht mit getExpectedWebClientIndexSha256(). Drei Resultate:

LageProbe-Ergebnis
Host-Hash == stable-HashWeb-Client aktuell (4f6bb035… == stable)
Host-Hash != stable-HashWeb-Client veraltet: host hat …, stable ist …
Kein stable-Tarball vorhanden✅ aber Warnhinweis im message

Rollback

Wenn nach einem Promote eine fatale Regression auftaucht:

bash
# stable auf vorherige Version zuruecksetzen
promote-web-client.sh <vorherige-sha>

# Sofort-Rollout
npx tsx scripts/reconcile-tenant.ts --all

Browser-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:

yaml
- 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: warning

Das 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 stable auf 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 latest nach X Stunden „Soak". Wenn du das willst: Cron-Job if 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.