Zum Inhalt springen
Farbschema wählenSprache wählen

App-Hosting auf separater Origin

Gehostete App-Frontends (React/Svelte/Plain-mit-Source) müssen von einer anderen registrierbaren Domain (eTLD+1) als der Control Plane ausgeliefert werden. Würden Apps auf einer Subdomain des Control-Plane-Hosts (supacloud.run) bereitgestellt, wären sie same-site mit der API – das JavaScript einer deployten App könnte dann den Session-Cookie des Betreibers gegen /api verwenden. Die Auslieferung über supacloud.net macht jeden Request cross-site, sodass SameSite=Lax den Session-Cookie zurückhält, Cookie-Tossing unmöglich ist und die CORS-Allowlist die Apps-Origin von Natur aus ausschließt. Das ist dasselbe Isolationsmodell, das GitHub mit githubusercontent.com verwendet.

Das Feature ist zusätzlich durch SUPACLOUD_APPS_HOSTED_FRONTEND (Standard: false) abgesichert. Das Flag darf nicht aktiviert werden, ohne dass eine separate Apps-Domain konfiguriert ist – der Server verweigert in dieser Kombination den Start.

  • Eine registrierbare Domain (eTLD+1), die sich von deiner Control-Plane-Domain unterscheidet. Für SupaCloud SaaS ist das supacloud.net; Self-Hosting-Betreiber sollten eine eigene Entsprechung bereitstellen (z. B. myapp-content.example).
  • Ein Wildcard-TLS-Zertifikat für *.yourapps.domain sowie DNS-Routing aller Subdomains auf den SupaCloud-Server. Das bw-infra-Referenz-Deployment verwendet ein Traefik-Wildcard-Zertifikat via DNS-01.
  • Das Runner-Image muss mit vite --base=./ neu gebaut worden sein, damit Artefakte unter beliebigen Subdomain-Roots gehostet werden können. Bis dieses Image ausgerollt ist, sollte das Flag deaktiviert bleiben.

Jeder gehostete Frontend-Deployment-Eintrag erhält eine stabile, opake 80-Bit-Hex- Subdomain (gespeichert in app_deployments.subdomain, Migration 172). Die Subdomain wird einmalig vergeben und über Redeploys hinweg wiederverwendet, sodass die URL stabil bleibt.

Wenn SUPACLOUD_APPS_BASE_DOMAIN gesetzt ist, wird eine Host-Dispatch-Middleware (interfaces/http/apps_host.rs) als äußerste Schicht des Servers eingebunden. Jeder Request, dessen Host-Header zur Apps-Domain oder einer ihrer Subdomains passt, wird als App-Inhalt ausgeliefert und erreicht nie /api, die SPA oder die Login-Seite. Auf der Apps-Origin werden ausschließlich GET- und HEAD-Requests akzeptiert; alle anderen Methoden erhalten 405. Die pfadbasierte Route (/a/{workspace}/{route}) lehnt gehostete Frontends ab, wenn eine Apps-Domain konfiguriert ist – jede gehostete App ist daher ausschließlich über ihre opake Subdomain erreichbar.

Der Boot-Guard in secret_hydration.rs lehnt einen apps_base_domain-Wert ab, der eine eTLD+1 mit der Control Plane teilt (in beiden Richtungen geprüft via der psl-Public-Suffix-Liste). Eine falsch konfigurierte Domain führt also zu einem deutlichen Fehler beim Start, anstatt die Isolationsgrenze still zu durchbrechen.

  1. Apps-Content-Domain festlegen.

    Füge SUPACLOUD_APPS_BASE_DOMAIN zu deiner Umgebung hinzu und setze sie auf deine separate registrierbare Domain:

    SUPACLOUD_APPS_BASE_DOMAIN=supacloud.net

    Der Wert muss eine reine Domain sein – kein Schema, kein Port, kein Pfad und keine Leerzeichen. supacloud.net ist korrekt; https://supacloud.net wird beim Start abgelehnt.

  2. Gehostetes Frontend-Serving aktivieren.

    SUPACLOUD_APPS_HOSTED_FRONTEND=true

    Der Server verweigert den Start, wenn dieses Flag gesetzt, aber keine Apps-Domain konfiguriert ist.

  3. Wildcard-DNS auf deinen Server leiten.

    Alle *.yourapps.domain-Requests müssen SupaCloud erreichen. Im bw-infra-Referenz-Deployment verarbeitet Traefik ein Wildcard-Zertifikat *.supacloud.net (InternetBS DNS-01) und leitet alle Subdomains an den Server weiter.

  4. Server neu starten.

    Der Boot-Guard validiert beide Variablen. Bei erfolgreichem Start enthält der /config-API-Endpunkt "apps_base_domain": "yourapps.domain".

Backend-Aufrufe einer gehosteten App (z. B. ein über ein Formular ausgelöster Workflow-Trigger) sollten keine Benutzer-Session einbetten. Erstelle stattdessen einen Deployment-spezifischen App-API-Token (Präfix scwa_, gespeichert als Hash in app_deployments.api_token_hash, Migration 173):

POST /api/apps/{id}/deployment/token
Authorization: Bearer <owner-session>

Die Antwort gibt den rohen Token-Wert einmalig zurück. Speichere ihn serverseitig im Backend deiner App – bette ihn niemals in ein öffentliches Frontend-Bundle ein. Der Token authentifiziert sich als workspace-scoped Service-Identity und ist CSRF-exempt (kein Cookie-Credential; CORS-Preflight verhindert das cross-site Anhängen eines benutzerdefinierten Authorization-Headers). Widerrufe ihn, wenn er nicht mehr benötigt wird:

DELETE /api/apps/{id}/deployment/token
Authorization: Bearer <owner-session>

Der Token wird außerdem automatisch beim Undeploy gelöscht, sodass ein Redeploy-Zyklus kein veraltetes Credential stillschweigend reaktivieren kann.

Deploye nach dem Neustart eine React- oder Svelte-App über die SupaCloud-Oberfläche. Die Deployment-Detailseite zeigt die zugewiesene Subdomain-URL (https://<opaque>.yourapps.domain). Das Öffnen dieser URL sollte das gebaute index.html mit folgenden Eigenschaften ausliefern:

  • Einem Content-Security-Policy-Header (Default-Deny; frame-ancestors enthält die Control-Plane-Origin, damit der Live-Preview-iFrame cross-origin funktioniert).
  • X-Content-Type-Options: nosniff.
  • <base href="/"> in index.html eingefügt.

Requests von der Apps-Origin an die Control-Plane-Origin (/api) werden durch SameSite=Lax am Session-Cookie und den Ausschluss aus der CORS-Allowlist blockiert.