Ports and hosting domains
Service ports
Section titled “Service ports”The Docker Compose deployment (docker-compose.yml) exposes these ports:
| Service | Internal port | Host-mapped port | Protocol |
|---|---|---|---|
web (SvelteKit) |
3000 | 3001 | HTTP |
server (Rust/axum) |
8080 | 8080 | HTTP / WebSocket |
postgres |
5432 | not published | TCP (internal only) |
The web container proxies requests to server at http://server:8080 over the
internal supacloud bridge network. PostgreSQL is not reachable from the host in
the default Compose configuration.
Networks
Section titled “Networks”| Network | Purpose |
|---|---|
supacloud |
Internal bridge: web, server, and postgres communicate here. |
supacloud-agents |
Isolated bridge used by agent containers spawned by server (Docker socket mounted at /var/run/docker.sock). |
Control-plane origin vs. apps origin
Section titled “Control-plane origin vs. apps origin”SupaCloud uses two distinct registrable domains (ADR 0041 / issue #410):
| Domain role | Env variable | Example | Serves |
|---|---|---|---|
| Control plane | PUBLIC_URL |
https://app.supacloud.run |
API, web UI, login, SPA |
| Hosted App frontends | SUPACLOUD_APPS_BASE_DOMAIN |
supacloud.net |
Deployed plain/React/Svelte Apps on opaque subdomains |
The apps domain must not share an eTLD+1 with the control-plane host. A boot
guard in secret_hydration.rs rejects any SUPACLOUD_APPS_BASE_DOMAIN value that
is a sibling subdomain, superset, or subset of the control-plane origin (checked
via the psl public-suffix list). See
Why separate-origin App hosting for the rationale.
Each deployed hosted frontend receives a stable 80-bit hex subdomain
(app_deployments.subdomain, migration 172), e.g.
<opaque>.supacloud.net. The apex domain (supacloud.net) returns 404.
The #113 form-renderer App is not subject to this separation — it is served on
the control-plane path (/a/{workspace_slug}/{route_path}) and has no subdomain.
Host-dispatch middleware
Section titled “Host-dispatch middleware”interfaces/http/apps_host.rs is mounted as the outermost axum middleware. Any
request whose Host header matches the apps domain (or a subdomain) is handled as
App content:
- Only
GETandHEADare accepted; other methods return405. - The request never reaches the control-plane router (
/api, SPA, login). HEADresponses have their body stripped in this middleware (axum’s automatic stripping does not apply at middleware level).
Content-Security-Policy for hosted Apps
Section titled “Content-Security-Policy for hosted Apps”Every file served from the apps origin carries the DEFAULT_CSP header defined in
services/app_deployments/artifact.rs:
default-src 'none';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' blob:;manifest-src 'self';worker-src 'self' blob:;base-uri 'self';form-action 'self';frame-ancestors 'self'When SUPACLOUD_APPS_BASE_DOMAIN is set, the served CSP extends frame-ancestors
to additionally allow the control-plane origin (csp_for_separate_origin), so the
WebIDE live-preview iframe can load the App across origins. The base directive
frame-ancestors 'self' is retained, so the App’s own origin plus that single
control-plane origin are the only permitted framers; arbitrary sites still cannot
frame the App.
X-Content-Type-Options: nosniff is set on every served file.
Enabling hosted App frontends
Section titled “Enabling hosted App frontends”The hosted plain/React/Svelte frontend path is behind a flag:
| Variable | Default | Effect when enabled |
|---|---|---|
SUPACLOUD_APPS_HOSTED_FRONTEND |
false |
Serves built dist/ artifacts on the apps origin. |
The flag defaults off pending the build runner image shipping with vite --base=./.
The form-renderer App is unaffected by this flag.
Related
Section titled “Related”- Environment variables — full variable reference
- Self-host with Docker Compose
- ADR index — ADR 0041 for the two-origin design