refactor: update frontend state handling and adjust related documentation

Kim committed May 29, 2026 at 17:47 UTC 9bf89268ed24c9626f7b9024ecf176703dc0dfba
8 files changed +16 -15
.dockerignore
-1
@@ -13,7 +13,6 @@ docs/
13
14 # Local relay/tunnel state
15 .portal-certs/
16 -.portal-frontend-state/
16 *identity.json
17
18 # IDE
.env.example
+1
@@ -58,6 +58,7 @@ TRUST_PROXY_HEADERS=false
58 TRUSTED_PROXY_CIDRS=
59
60 # Frontend-owned presentation state.
61 +# Bundled Compose stores the saved override under ./.portal-certs/frontend-state/state.json.
62 LANDING_PAGE_ENABLED=false
63
64 # Optional: auto-generated thumbnail screenshots for tunnel apps without a thumbnail.
.gitignore
-1
@@ -147,5 +147,4 @@ keyless_tls/
147
148 # Local relay/tunnel state
149 .portal-certs/
150 -.portal-frontend-state/
150 *identity.json
docker-compose.yml
+2 -2
@@ -19,12 +19,12 @@ services:
19 PORT: 8081
20 PORTAL_API_BASE_URL: https://portal:4017
21 LANDING_PAGE_ENABLED: ${LANDING_PAGE_ENABLED:-false}
22 - PORTAL_FRONTEND_STATE_PATH: /portal-frontend-state/state.json
22 + PORTAL_FRONTEND_STATE_PATH: /frontend-state/state.json
23 # Leave empty to disable generated screenshots without removing the service.
24 HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-}
25 # HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-ws://headless-shell:9222}
26 volumes:
27 - - ./.portal-frontend-state:/portal-frontend-state
27 + - ./.portal-certs/frontend-state:/frontend-state
28 restart: unless-stopped
29
30 portal-frontend:
docs/src/routes/configuration/+page.md
+1 -1
@@ -80,7 +80,7 @@ variables:
80 | `PORT` | `8081` | int | Frontend API HTTP listen port |
81 | `PORTAL_API_BASE_URL` | `https://portal:4017` | string | Relay API base URL used to compose frontend-owned state |
82 | `LANDING_PAGE_ENABLED` | `false` | bool | Default landing page flag when no frontend state has been saved yet |
83 -| `PORTAL_FRONTEND_STATE_PATH` | `""` | string | Optional JSON file path for persisted frontend-owned state |
83 +| `PORTAL_FRONTEND_STATE_PATH` | `""` | string | Optional JSON file path for persisted frontend-owned state; bundled Compose stores this under `./.portal-certs/frontend-state/state.json` |
84 | `HEADLESS_SHELL_URL` | `""` | string | Headless Chrome CDP WebSocket URL; leave empty to disable generated thumbnails |
85
86 ### Cloudflare
docs/src/routes/deployment/+page.md
+2 -2
@@ -178,7 +178,7 @@ Leave `TRUSTED_PROXY_CIDRS` empty for the default private and loopback proxy ran
178 Create the state directories:
179
180 ```bash
181 -mkdir -p ./.portal-certs ./.portal-frontend-state ./certs
181 +mkdir -p ./.portal-certs/frontend-state ./certs
182 sudo chown 65532:65532 ./.portal-certs
183 chmod 755 ./.portal-certs
184 ```
@@ -350,7 +350,7 @@ It owns:
350 - `/policy/*` composition, while relay-enforced policy changes are still forwarded to `portal`.
351 - `/service/status`, derived from relay state for quick-start UI checks.
352 - `/thumbnail/<hostname>`, when optional screenshot generation is enabled.
353 -- The landing-page flag persisted at `PORTAL_FRONTEND_STATE_PATH`.
353 +- The landing-page flag persisted at `PORTAL_FRONTEND_STATE_PATH`; the bundled Compose files store it under `./.portal-certs/frontend-state/state.json`.
354
355 The Go relay remains the owner of authentication, policy enforcement, lease state, tunnel ingress, install scripts, discovery, and x402 facilitator paths.
356
docs/static/examples/nginx-proxy-multi-service/docker-compose.yaml
+5 -4
@@ -17,8 +17,9 @@
17 # - app_a_fullchain.pem, app_a_privkey.pem for app-a.example.com
18 # - app_b_fullchain.pem, app_b_privkey.pem for app-b.example.com
19 # Portal also needs API TLS material in ./.portal-certs unless ACME_DNS_PROVIDER is configured.
20 -# 3. Create the .portal-certs directory with correct ownership (UID 65532 = nonroot):
21 -# mkdir -p ./.portal-certs
20 +# 3. Create the .portal-certs directory with correct ownership (UID 65532 = nonroot).
21 +# Frontend presentation state is stored under ./.portal-certs/frontend-state:
22 +# mkdir -p ./.portal-certs/frontend-state
23 # sudo chown 65532:65532 ./.portal-certs
24 # chmod 755 ./.portal-certs
25 # 4. Start all services:
@@ -119,11 +120,11 @@ services:
120 PORT: 8081
121 PORTAL_API_BASE_URL: "https://portal:${API_PORT:-4017}"
122 LANDING_PAGE_ENABLED: ${LANDING_PAGE_ENABLED:-false}
122 - PORTAL_FRONTEND_STATE_PATH: /portal-frontend-state/state.json
123 + PORTAL_FRONTEND_STATE_PATH: /frontend-state/state.json
124 HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-}
125 # HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-ws://headless-shell:9222}
126 volumes:
126 - - ./.portal-frontend-state:/portal-frontend-state
127 + - ./.portal-certs/frontend-state:/frontend-state
128 restart: unless-stopped
129
130 app-a-api:
docs/static/examples/nginx-proxy/docker-compose.yaml
+5 -4
@@ -16,8 +16,9 @@
16 # 2. Place the root-domain TLS certificate files for nginx in ./certs/:
17 # ./certs/fullchain.pem
18 # ./certs/privkey.pem
19 -# 3. Create the .portal-certs directory with correct ownership (UID 65532 = nonroot):
20 -# mkdir -p ./.portal-certs
19 +# 3. Create the .portal-certs directory with correct ownership (UID 65532 = nonroot).
20 +# Frontend presentation state is stored under ./.portal-certs/frontend-state:
21 +# mkdir -p ./.portal-certs/frontend-state
22 # sudo chown 65532:65532 ./.portal-certs
23 # chmod 755 ./.portal-certs
24 # Portal also needs API TLS material in ./.portal-certs unless ACME_DNS_PROVIDER is configured.
@@ -115,9 +116,9 @@ services:
116 PORT: 8081
117 PORTAL_API_BASE_URL: "https://portal:${API_PORT:-4017}"
118 LANDING_PAGE_ENABLED: ${LANDING_PAGE_ENABLED:-false}
118 - PORTAL_FRONTEND_STATE_PATH: /portal-frontend-state/state.json
119 + PORTAL_FRONTEND_STATE_PATH: /frontend-state/state.json
120 HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-}
121 # HEADLESS_SHELL_URL: ${HEADLESS_SHELL_URL:-ws://headless-shell:9222}
122 volumes:
122 - - ./.portal-frontend-state:/portal-frontend-state
123 + - ./.portal-certs/frontend-state:/frontend-state
124 restart: unless-stopped