Align website deployment defaults with VPS host state

This commit is contained in:
axiomlogicnexus 2026-06-22 07:16:20 +00:00
parent 7a7fddf279
commit 24e780e018
15 changed files with 118 additions and 6 deletions

View file

@ -356,6 +356,11 @@ Current live-origin evidence on `2026-06-22`:
instead of live auth-health JSON
- `https://hypertwist.app/api/releases/manifest` returned that same
placeholder HTML instead of the public release-manifest JSON route
- shared-VPS inspection also confirmed that the current host has no
`/srv/hypertwist/current` checkout yet, no `hypertwist-website-auth-server`
service installed, and an existing FamiliarOS auth process already occupying
`*:3001`, so the HyperTwist same-origin deployment defaults now use `3011`
instead
That means the remaining gap above this packet is live deployment cutover and
runtime configuration on the public host, not another missing first-party

View file

@ -28,6 +28,8 @@ Recommended server env posture:
- `API_DOMAIN=https://hypertwist.app`
- `WEBSITE_DOMAIN=https://hypertwist.app`
- `PORT=3011` on the current shared VPS, because the live host already has a
FamiliarOS auth process occupying `3001`
- `API_BASE_PATH=/auth`
- `WEBSITE_BASE_PATH=/auth`
- `COOKIE_SECURE=true`
@ -144,6 +146,8 @@ Current live evidence on `2026-06-22`:
against env files emitted by the manifest-driven bundle renderer, so the
remaining gap is live host cutover rather than repo-side deployment bundle
ownership
- the shared VPS also already had FamiliarOS bound to `*:3001`, so the
HyperTwist same-origin lane now defaults to `3011` for host-safe deployment
This means the remaining public-launch gap is current deployment cutover and
runtime configuration on the live host, not missing first-party website or
@ -156,6 +160,7 @@ auth-server ownership in the repo.
- `website/deploy/README.md`
- `docs/ops/HYPERTWIST_PUBLIC_WEBSITE_AUTH_BILLING_AND_DISTRIBUTION_PACKET_2026-06-22.md`
- `docs/ops/HYPERTWIST_WEBSITE_SAME_ORIGIN_DEPLOYMENT_HANDOFF_2026-06-22.md`
- `docs/ops/HYPERTWIST_WEBSITE_VPS_PRECUTOVER_HOST_STATE_2026-06-22.md`
- `docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md`
- `docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md`
- `docs/ops/HYPERTWIST_VPS_TLS_CERTIFICATE_AND_IONOS_SEPARATION_GUIDE_2026-05-30.md`

View file

@ -31,6 +31,7 @@ The simplest honest deployment is:
1. build the frontend in `website/`
2. run `website/server` as the long-lived local process on the VPS
on a free host port such as `3011`
3. let the server auto-serve `../dist`
4. put NGINX in front of that process on `https://hypertwist.app`
@ -73,6 +74,14 @@ Expected runtime env files:
- `/srv/hypertwist/current/website/.env`
- `/srv/hypertwist/current/website/server/.env`
Current live-host reality note:
- `/srv/hypertwist/current` does not exist yet on the shared VPS
- the current live vhost still serves `/var/www/hypertwist/index.html`
- the current shared host also already has FamiliarOS occupying `3001`
- current host-state authority:
`docs/ops/HYPERTWIST_WEBSITE_VPS_PRECUTOVER_HOST_STATE_2026-06-22.md`
## Cutover sequence
### 1. Sync the current repo to the VPS
@ -108,6 +117,7 @@ Minimum required live values include:
- real corresponding-source URL
- `API_DOMAIN=https://hypertwist.app`
- `WEBSITE_DOMAIN=https://hypertwist.app`
- `PORT=3011`
- `COOKIE_SECURE=true`
- real `PADDLE_WEBHOOK_SECRET`
- real billing product/price map
@ -256,3 +266,4 @@ If cutover breaks:
- `website/server/README.md`
- `docs/ops/HYPERTWIST_WEBSITE_RUNTIME_CONFIGURATION_GUIDE_2026-06-22.md`
- `docs/ops/HYPERTWIST_PUBLIC_WEBSITE_AUTH_BILLING_AND_DISTRIBUTION_PACKET_2026-06-22.md`
- `docs/ops/HYPERTWIST_WEBSITE_VPS_PRECUTOVER_HOST_STATE_2026-06-22.md`

View file

@ -0,0 +1,77 @@
# HyperTwist Website VPS Precutover Host State
Created on `2026-06-22`.
## Purpose
This note records the actual shared-VPS state observed immediately before the
HyperTwist same-origin website/auth-server cutover.
It exists so future deployment work starts from current host truth rather than
from the idealized checkout layout used by the new deployment templates.
## Current inspected host facts
Read-only inspection on `212.227.13.220` confirmed:
- the intended checkout path `/srv/hypertwist/current` does not exist yet
- the tracked repo checkout currently present for the `dev` user is
`/home/dev/src/HyperTwist`
- there is no `hypertwist-website-auth-server.service` unit currently installed
- the live `hypertwist.app` NGINX vhost is still
`/etc/nginx/sites-available/hypertwist-app.conf`
- that live vhost still serves a static root:
- `/var/www/hypertwist`
- the live static root currently contains:
- `/var/www/hypertwist/index.html`
- the live public site is therefore still the older placeholder rollout page
## Current shared-port reality
The same inspection also confirmed that port `3001` is already occupied on the
shared VPS:
- listener:
- `*:3001`
- owning process:
- `/usr/bin/node --require /home/dev/src/FamiliarOS/website/server/node_modules/tsx/dist/preflight.cjs --import file:///home/dev/src/FamiliarOS/website/server/node_modules/tsx/dist/loader.mjs src/index.ts`
- related env posture:
- `/home/dev/src/FamiliarOS/website/server/.env` contains `PORT=3001`
Operational consequence:
- HyperTwist same-origin deployment on this shared VPS must not reuse `3001`
as its default upstream port
- the current first-party HyperTwist deployment templates and bundle renderer
therefore now use `3011` as the shared-host default instead
## Current live-route evidence
At inspection time:
- `https://hypertwist.app/` still resolved to the placeholder rollout page
- local `curl http://127.0.0.1:3001/api/auth/health` returned Express
`Cannot GET /api/auth/health`
- local `curl http://127.0.0.1:3001/api/releases/manifest` returned Express
`Cannot GET /api/releases/manifest`
This confirms that the current process on `3001` is not the HyperTwist
same-origin website/auth-server lane.
## Practical next cutover implications
Before the real cutover can happen, the host still needs:
- a chosen live checkout path for HyperTwist website deployment
- rendered `.env` files copied into that checkout
- a real long-lived HyperTwist website/auth-server process bound to a free port
such as `3011`
- the NGINX vhost for `hypertwist.app` rewritten from static-root serving to
reverse-proxy the HyperTwist website/auth-server process
- root-owned service enablement and NGINX reload
## Related authorities
- `docs/ops/HYPERTWIST_WEBSITE_SAME_ORIGIN_DEPLOYMENT_HANDOFF_2026-06-22.md`
- `docs/ops/HYPERTWIST_WEBSITE_RUNTIME_CONFIGURATION_GUIDE_2026-06-22.md`
- `docs/ops/HYPERTWIST_PUBLIC_WEBSITE_AUTH_BILLING_AND_DISTRIBUTION_PACKET_2026-06-22.md`

View file

@ -263,7 +263,7 @@ repo.
| Feature | Status | Primary authority | Notes |
|---|---|---|---|
| Public `hypertwist.app` marketing shell | Implemented now | first-party `website/` app + feature registry/roadmap authority | HyperTwist now has a dedicated first-party public web surface for homepage, about, resources, pricing, download, support, and legal routes. This lane is separate from the embedded Unreal browser runtime under `Content/Browser/` and does not claim browser-simulator parity. The same package now also carries a first-party external runtime-readiness verifier so deploy-time env and live health posture can be checked outside the dashboard, plus separated local-versus-production env templates whose placeholder values are intentionally rejected until real launch config is in place, bootstrap CI now validates both the frontend and auth-server website commands directly, and the auth server can now auto-serve the built `website/dist` bundle with bounded SPA fallback for same-origin public deployment. Request-level server coverage now also proves that public/app shell delivery does not shadow `/api/*`, `/auth*`, `/health`, or missing asset paths, while the pricing/download/notices routes now surface first-party preview-versus-launch posture from the same bounded launch checklist instead of relying on hidden operator-only status. The live website lane now also owns route-aware title/description/canonical/Open-Graph/Twitter metadata for the real `hypertwist.app` marketing surface so deployed public pages no longer remain on a single generic SPA title/description, plus first-party `robots.txt` and `sitemap.xml` assets for the public route set while keeping `/app`, `/login`, and `/register` out of crawler posture. The real `check-runtime-readiness` CLI is now also exercised against the checked-in production example env files, and a spawned `website/server` bootstrap proof now verifies the live same-origin process path from production-shaped env into `/health`, `/api/auth/health`, built-shell serving, and the public anonymous release-manifest posture for the shared desktop release lane. The same verifier now also probes the deployed root-shell marker and can explicitly fail when the public origin is still serving the older placeholder rollout page instead of the first-party website/auth-server lane, while the repo now also carries first-party `website/deploy/` `nginx` plus `systemd` handoff templates, a concrete same-origin public-host cutover guide, a deployment-file renderer that emits resolved operator outputs from real checkout paths, and a manifest-driven bundle renderer that lets one authoritative input own the public origin while emitting validated env plus install artifacts together. |
| Public `hypertwist.app` marketing shell | Implemented now | first-party `website/` app + feature registry/roadmap authority | HyperTwist now has a dedicated first-party public web surface for homepage, about, resources, pricing, download, support, and legal routes. This lane is separate from the embedded Unreal browser runtime under `Content/Browser/` and does not claim browser-simulator parity. The same package now also carries a first-party external runtime-readiness verifier so deploy-time env and live health posture can be checked outside the dashboard, plus separated local-versus-production env templates whose placeholder values are intentionally rejected until real launch config is in place, bootstrap CI now validates both the frontend and auth-server website commands directly, and the auth server can now auto-serve the built `website/dist` bundle with bounded SPA fallback for same-origin public deployment. Request-level server coverage now also proves that public/app shell delivery does not shadow `/api/*`, `/auth*`, `/health`, or missing asset paths, while the pricing/download/notices routes now surface first-party preview-versus-launch posture from the same bounded launch checklist instead of relying on hidden operator-only status. The live website lane now also owns route-aware title/description/canonical/Open-Graph/Twitter metadata for the real `hypertwist.app` marketing surface so deployed public pages no longer remain on a single generic SPA title/description, plus first-party `robots.txt` and `sitemap.xml` assets for the public route set while keeping `/app`, `/login`, and `/register` out of crawler posture. The real `check-runtime-readiness` CLI is now also exercised against the checked-in production example env files, and a spawned `website/server` bootstrap proof now verifies the live same-origin process path from production-shaped env into `/health`, `/api/auth/health`, built-shell serving, and the public anonymous release-manifest posture for the shared desktop release lane. The same verifier now also probes the deployed root-shell marker and can explicitly fail when the public origin is still serving the older placeholder rollout page instead of the first-party website/auth-server lane, while the repo now also carries first-party `website/deploy/` `nginx` plus `systemd` handoff templates, a concrete same-origin public-host cutover guide, a deployment-file renderer that emits resolved operator outputs from real checkout paths, and a manifest-driven bundle renderer that lets one authoritative input own the public origin while emitting validated env plus install artifacts together, with the shared-VPS-safe default upstream moved to `3011` after live host inspection confirmed `3001` is already occupied by FamiliarOS. |
| Browser-based operator/account dashboard | Implemented now | first-party `website/` app + shared auth/dashboard packet | A protected browser dashboard is now live for operator access, account state, download posture, browser-access boundary explanation, notices review, and bounded billing/entitlement status. It reuses the shared SuperTokens auth posture proven in FamiliarOS and ScriptoriumAI while remaining HyperTwist-specific in product content and boundary claims, the current auth-health surface now truthfully distinguishes configured versus reachable or ready shared-core posture while exposing fallback-active reason instead of hardcoding readiness, and the same dashboard now also surfaces launch-readiness truth for download URLs, checkout links, source/notices URLs, billing-secret/map configuration, and local-versus-public runtime deployment posture. Focused frontend coverage now also protects deep-link login redirect preservation, safe `next`-path normalization across auth entry points, fallback/email auth-bootstrap normalization, login/register continuation behavior, public download-gating behavior, protected-route/shell behavior, real lazy-route tree behavior for key public and protected paths, top-level app-bootstrap and SuperTokens-wrapper posture, login/register unhappy-path and OAuth-button behavior, support-topic fallback routing when live checkout is not configured, desktop-link verify-url/dashboard readiness behavior, and explicit `noindex,nofollow` posture on protected/auth browser surfaces. The validation lane now also has a bounded signed test-session harness under `TEST_MODE=testing` that proves `/api/auth/me` and `/api/auth/desktop-link` behavior through the live spawned auth-server process without widening production auth posture. |
| Desktop download posture and browser-to-desktop pairing | Implemented now | first-party `website/` app + `website/server` desktop-link endpoints | Public download targets, dashboard-side release posture, and short-lived desktop-link token generation/verification are now first-party owned. The current server posture now enforces exact website-origin matching, bounded per-user issuance, one-time token consumption, and billing-backed plan/download entitlement resolution with focused `website/server` tests green on `2026-06-22`, and the verify handshake now returns the same resolved download-entitlement posture the dashboard sees instead of only identity plus plan/role. The same lane now also owns a shared `GET /api/releases/manifest` runtime authority for release version/channel/build/published/file-size/checksum/docs/source metadata, with anonymous callers intentionally denied raw download URLs while entitled session-backed callers receive the configured direct platform URL. The public `/download` page now keeps raw download URLs behind the protected dashboard instead of exposing them directly, preserves requested platform continuity through `/app/downloads?platform=...`, and surfaces that requested target again after auth handoff inside the protected release lane. Both the public and protected download surfaces now also carry first-party rollout steps plus release/notices/source references so the desktop setup lane is more than a generic link bucket, and the dashboard plus public launch-status callouts now consume the same manifest-backed Windows download truth instead of only static frontend config. Actual release URLs remain deployment configuration rather than hardcoded product truth. |
| Paddle-ready pricing and billing webhook seam | Implemented now | first-party `website/` app + `website/server` billing endpoint | The public pricing surface now exists with plan structure, checkout-link configuration seams, and the same `/api/billing/paddle/webhook` endpoint family used by the broader product website lane. The current server now verifies `Paddle-Signature` against `PADDLE_WEBHOOK_SECRET` using the documented raw-body HMAC flow, persists a bounded first-party billing state file, and applies verified Paddle events into account/download entitlement state that the browser dashboard consumes, with focused `website/server` tests green on `2026-06-22`. A spawned live-process proof now also verifies that a real signed webhook updates processed-event health and persisted billing state through the actual auth-server runtime, not only helper-level store tests, and transaction events no longer leak their id into stored `subscriptionId` state. Production checkout URLs, secret management, and broader operator/admin billing workflows remain deployment/application tasks, not shipped-code omissions. |

View file

@ -269,7 +269,9 @@ Current consolidated milestone snapshot:
examples by hand, and the next same-family continuation now also ships a
manifest-driven deployment-bundle renderer so one authoritative input can own
the public origin and emit validated frontend env, server env, `systemd`,
and `nginx` outputs together before live-host installation,
and `nginx` outputs together before live-host installation, with the shared
VPS default now moved to `3011` after live host inspection confirmed that
FamiliarOS already occupies `3001`,
and the live public website lane now also owns route-aware
title/description/canonical/Open-Graph/Twitter metadata for the real
`hypertwist.app` marketing surface while marking protected/auth routes as

View file

@ -140,6 +140,8 @@ Use the runtime-readiness command before public launch or deployment approval:
- the repo now also includes `npm run render:same-origin-bundle` so one
deployment manifest can own the public origin and emit validated frontend
env, server env, `systemd`, and `nginx` outputs together before VPS cutover
- the shared-VPS deployment defaults now use upstream port `3011` because the
current host already has FamiliarOS bound to `3001`
- it now warns when same-origin public deployment leaves static website serving mode ambiguous
- request-level server tests now also pin that same-origin shell behavior instead of relying only on helper-level assertions
- the public pricing/download/notices pages now also surface preview-versus-launch posture directly from the same bounded launch checklist

View file

@ -18,6 +18,8 @@ Expected deployment posture:
`website/server` Express process
- the Express process serves both the built `website/dist` bundle and the
`/api/*` plus `/auth*` route families on the same origin
- the shared VPS default upstream port for this lane is now `3011`, not
`3001`, because the live host already has FamiliarOS occupying `3001`
Before using these templates:
@ -27,6 +29,8 @@ Before using these templates:
- set `publicOrigin` to the exact live origin that should own both the website
and auth cookies; for the canonical production lane that value is
`https://hypertwist.app`
- keep `server.port` on a free shared-host port; the checked-in example uses
`3011` because the current shared VPS already has FamiliarOS on `3001`
- either render resolved files with
`npm run render:same-origin-deployment -- --checkout-root /srv/hypertwist/current --systemd-out ... --nginx-out ...`
or replace filesystem paths manually inside the example files

View file

@ -27,6 +27,7 @@
"publishedAt": "2026-06-22T00:00:00.000Z"
},
"server": {
"port": "3011",
"superTokensCoreUri": "http://127.0.0.1:3567",
"cookieSecure": true,
"serveStaticWebsite": true,

View file

@ -4,7 +4,7 @@ map $http_upgrade $connection_upgrade {
}
upstream hypertwist_website_auth_server {
server 127.0.0.1:3001;
server 127.0.0.1:3011;
keepalive 32;
}

View file

@ -113,7 +113,7 @@ export function resolveBundleManifest(manifest = {}) {
publishedAt: requireNonEmpty(windowsRelease.publishedAt, 'windowsRelease.publishedAt'),
},
server: {
port: normalizeTrimmed(server.port || '3001'),
port: normalizeTrimmed(server.port || '3011'),
superTokensCoreUri: normalizeTrimmed(server.superTokensCoreUri || 'http://127.0.0.1:3567'),
apiBasePath: normalizeTrimmed(server.apiBasePath || '/auth'),
websiteBasePath: normalizeTrimmed(server.websiteBasePath || '/auth'),

View file

@ -69,6 +69,7 @@ describe('buildServerEnvEntries', () => {
it('derives consistent server env from the bundle manifest', () => {
const env = buildServerEnvEntries(createValidManifest())
expect(env.PORT).toBe('3011')
expect(env.API_DOMAIN).toBe('https://hypertwist.app')
expect(env.WINDOWS_RELEASE_BUILD_ID).toBe('win64-1000')
expect(env.PADDLE_PRODUCT_PLAN_MAP).toContain('prod_operator')
@ -103,10 +104,12 @@ describe('renderSameOriginBundle', () => {
const rendered = renderSameOriginBundle(createValidManifest())
expect(rendered.validationReport.ok).toBe(true)
expect(rendered.serverEnvContent).toContain('PORT=3011')
expect(rendered.frontendEnvContent).toContain('VITE_WINDOWS_DOWNLOAD_URL=https://downloads.hypertwist.app/windows.exe')
expect(rendered.serverEnvContent).toContain('RELEASE_MANIFEST_VERSION=1.0.0')
expect(rendered.systemdContent).toContain('WorkingDirectory=/srv/hypertwist/current/website/server')
expect(rendered.nginxContent).toContain('server_name hypertwist.app www.hypertwist.app;')
expect(rendered.nginxContent).toContain('server 127.0.0.1:3011;')
})
})

View file

@ -39,7 +39,7 @@ export function resolveDeploymentRenderOptions(options = {}) {
const serviceGroup = normalizeTrimmed(options.serviceGroup || serviceUser)
const serverNames = normalizeServerNames(options.serverNames || ['hypertwist.app', 'www.hypertwist.app'])
const certificateName = normalizeTrimmed(options.certificateName || serverNames[0])
const upstreamPort = normalizePositiveInteger(options.upstreamPort || 3001, 'upstreamPort')
const upstreamPort = normalizePositiveInteger(options.upstreamPort || 3011, 'upstreamPort')
const upstreamName = normalizeTrimmed(options.upstreamName || 'hypertwist_website_auth_server')
const websiteRoot = normalizeTrimmed(options.websiteRoot || path.join(checkoutRoot, 'website'))

View file

@ -19,7 +19,7 @@ function parseArgs(argv) {
nodeEnv: 'production',
serverNames: '',
certificateName: '',
upstreamPort: '3001',
upstreamPort: '3011',
upstreamName: 'hypertwist_website_auth_server',
systemdOut: '',
nginxOut: '',

View file

@ -68,6 +68,8 @@ Recommended public `hypertwist.app` posture:
- `API_DOMAIN=https://hypertwist.app`
- `WEBSITE_DOMAIN=https://hypertwist.app`
- `PORT=3011` on the current shared VPS so the lane does not collide with the
existing FamiliarOS auth server on `3001`
- `COOKIE_SECURE=true`
- real `PADDLE_WEBHOOK_SECRET`
- real plan-map configuration or equivalent verified-event plan resolution