Clarify website static serving deployment posture

This commit is contained in:
axiomlogicnexus 2026-06-22 02:56:26 +00:00
parent 34e973b4eb
commit efcddb67f0
9 changed files with 58 additions and 1 deletions

View file

@ -160,6 +160,7 @@ for `hypertwist.app` when a built `website/dist/index.html` is present:
- static delivery auto-enables from `website/server -> ../dist`
- SPA fallback remains limited to public/app routes and excludes `/api/*`, `/auth*`, and `/health`
- `WEBSITE_DIST_PATH` and `SERVE_STATIC_WEBSITE` now expose explicit deployment control for that lane
- the runtime-readiness verifier now warns when same-origin public posture leaves that static-serving mode implicit
This is browser-based user access for the operator/account surface.

View file

@ -31,6 +31,7 @@ Recommended server env posture:
- `API_BASE_PATH=/auth`
- `WEBSITE_BASE_PATH=/auth`
- `COOKIE_SECURE=true`
- `SERVE_STATIC_WEBSITE=true` when the auth server owns same-origin public delivery
- real `PADDLE_WEBHOOK_SECRET`
- real `PADDLE_PRODUCT_PLAN_MAP` and/or `PADDLE_PRICE_PLAN_MAP`
@ -52,6 +53,8 @@ The repo now carries two distinct env-template families:
The production examples intentionally include `replace-me` placeholder values so
the readiness command still fails until real launch values are inserted.
They now also carry the explicit static-serving control variables for the
same-origin lane.
## Why same-origin is the clean default
@ -73,6 +76,7 @@ when a built website bundle exists:
- override with `WEBSITE_DIST_PATH` when deployment layout differs
- set `SERVE_STATIC_WEBSITE=false` to keep api-only mode explicit
- set `SERVE_STATIC_WEBSITE=true` to require bundle presence instead of silently staying api-only
- the readiness verifier now warns when same-origin public posture leaves this static-serving mode ambiguous
## If a split-host auth topology is chosen later

View file

@ -223,7 +223,9 @@ Current consolidated milestone snapshot:
auth-bootstrap normalization, and dashboard launch-readiness plus desktop-link
verify-url behavior, and the auth server can now auto-serve the built
`website/dist` bundle with bounded SPA fallback for same-origin `hypertwist.app`
deployment when that build output is present,
deployment when that build output is present, while the env templates and
runtime-readiness verifier now also make that static-serving posture explicit
instead of leaving it implicit,
persists a bounded first-party billing-state file, applies verified Paddle
events into account/download entitlement state, and surfaces that resolved
billing/download posture back through `/api/auth/me`, the protected browser

View file

@ -82,6 +82,7 @@ Use the runtime-readiness command before public launch or deployment approval:
- it fails if required public launch values are still missing
- it can optionally verify live `/api/auth/health` posture from the deployed site
- the auth server can now also serve the built `website/dist` bundle directly for same-origin `hypertwist.app` deployment when that build output is present
- it now warns when same-origin public deployment leaves static website serving mode ambiguous
The repo bootstrap CI now also validates this lane through:

View file

@ -222,6 +222,24 @@ function evaluateServerConfig(serverEnv) {
pushWarning(bucket, 'API_DOMAIN and WEBSITE_DOMAIN differ; same-origin public posture is the clean default.')
}
const serveStaticWebsite = normalizeTrimmed(serverEnv.SERVE_STATIC_WEBSITE).toLowerCase()
const websiteDistPath = normalizeTrimmed(serverEnv.WEBSITE_DIST_PATH) || '../dist'
const sameOriginPublicPosture = Boolean(
apiDomain
&& websiteDomain
&& apiDomain.origin === websiteDomain.origin
&& !isLoopbackHostname(apiDomain.hostname)
&& !isLoopbackHostname(websiteDomain.hostname),
)
if (sameOriginPublicPosture) {
if (serveStaticWebsite === 'false' || serveStaticWebsite === '0' || serveStaticWebsite === 'no') {
pushWarning(bucket, 'SERVE_STATIC_WEBSITE=false; ensure a separate same-origin web server serves the built website bundle.')
} else if (serveStaticWebsite !== 'true' && serveStaticWebsite !== '1' && serveStaticWebsite !== 'yes') {
pushWarning(bucket, `SERVE_STATIC_WEBSITE is not explicitly set; confirm ${websiteDistPath} is present for first-party same-origin serving or that an external same-origin web server serves the frontend.`)
}
}
if (
Boolean(normalizeTrimmed(serverEnv.GITHUB_CLIENT_ID))
!== Boolean(normalizeTrimmed(serverEnv.GITHUB_CLIENT_SECRET))

View file

@ -41,6 +41,7 @@ describe('buildRuntimeReadinessReport', () => {
WEBSITE_DOMAIN: 'https://hypertwist.app',
SUPERTOKENS_CORE_URI: 'https://auth-core.internal',
COOKIE_SECURE: 'true',
SERVE_STATIC_WEBSITE: 'true',
PADDLE_WEBHOOK_SECRET: 'secret',
PADDLE_PRICE_PLAN_MAP: '{"pri_operator":"operator"}',
},
@ -65,6 +66,31 @@ describe('buildRuntimeReadinessReport', () => {
expect(report.failures).toEqual([])
})
it('warns when same-origin public posture leaves static website serving ambiguous', () => {
const report = buildRuntimeReadinessReport({
frontendEnv: {
VITE_SUPERTOKENS_API_DOMAIN: 'https://hypertwist.app',
VITE_SUPERTOKENS_WEBSITE_DOMAIN: 'https://hypertwist.app',
VITE_AUTH_API_BASE_URL: 'https://hypertwist.app',
VITE_WINDOWS_DOWNLOAD_URL: 'https://downloads.hypertwist.app/windows.exe',
VITE_PADDLE_CHECKOUT_URL_OPERATOR: 'https://buy.paddle.com/operator',
VITE_MPL_SOURCE_URL: 'https://hypertwist.app/open-source/source',
VITE_OPEN_SOURCE_REPO_URL: 'https://git.scriptoriumai.io/scriptoriumadmin/hypertwist',
},
serverEnv: {
API_DOMAIN: 'https://hypertwist.app',
WEBSITE_DOMAIN: 'https://hypertwist.app',
SUPERTOKENS_CORE_URI: 'https://auth-core.internal',
COOKIE_SECURE: 'true',
PADDLE_WEBHOOK_SECRET: 'secret',
PADDLE_PRICE_PLAN_MAP: '{"pri_operator":"operator"}',
},
liveHealth: null,
})
expect(report.warnings).toContain('SERVE_STATIC_WEBSITE is not explicitly set; confirm ../dist is present for first-party same-origin serving or that an external same-origin web server serves the frontend.')
})
it('fails localhost-grade posture and missing public-launch configuration', () => {
const report = buildRuntimeReadinessReport({
frontendEnv: {

View file

@ -5,6 +5,8 @@ WEBSITE_DOMAIN=http://localhost:4273
API_BASE_PATH=/auth
WEBSITE_BASE_PATH=/auth
COOKIE_SECURE=false
SERVE_STATIC_WEBSITE=false
WEBSITE_DIST_PATH=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=

View file

@ -5,6 +5,8 @@ WEBSITE_DOMAIN=https://hypertwist.app
API_BASE_PATH=/auth
WEBSITE_BASE_PATH=/auth
COOKIE_SECURE=true
SERVE_STATIC_WEBSITE=true
WEBSITE_DIST_PATH=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=

View file

@ -81,6 +81,7 @@ The server now also supports a bounded first-party same-origin deployment mode:
- SPA fallback is limited to non-file public/app routes and does not intercept `/api/*`, `/auth*`, or `/health`
- `WEBSITE_DIST_PATH` can override the bundle location when deployment layout differs
- `SERVE_STATIC_WEBSITE=true` forces the server to expect a built bundle, while `SERVE_STATIC_WEBSITE=false` keeps api-only mode explicit
- the example env files now carry those static-serving controls directly so deployment posture is not implicit
The website package now also ships a deploy-time verification command: