# HyperTwist Website Same-Origin Deployment Handoff

Created on `2026-06-22`.

## Purpose

This note records the concrete cutover path for replacing the current
placeholder `hypertwist.app` page with the first-party HyperTwist website plus
auth-server lane already shipped in the repo.

## Latest live-origin truth on `2026-07-01`

The historical placeholder-era cutover notes below remain useful as deployment
history, but they are no longer the current live reading.

Current direct live checks now show:

- `https://hypertwist.app/` returns the first-party site shell, including meta
  marker `hypertwist-site-shell=first-party-website-v1`
- `https://hypertwist.app/api/auth/health` returns JSON with:
  - `ok: true`
  - `service: hypertwist-auth-server`
  - `runtime.public_origin_ready: true`
  - `launch.posture: preview`
- `https://hypertwist.app/api/releases/manifest` returns the first-party
  release manifest JSON and currently keeps the Windows lane at preview
  posture with packaged-validation summary attached but no public download URL

Current truthful interpretation:

- the placeholder rollout page is gone
- the first-party same-origin website/auth-server deployment is live
- the live public origin is still a preview deployment rather than full public
  launch because the current auth/launch JSON still reports missing Windows
  download, checkout, billing-map, and webhook-secret launch requirements
- as of `2026-07-02`, `.woodpecker/hypertwist-bootstrap.yml` includes a
  `website_live_deploy` step for `main` pushes that invokes
  `npm run run:vps-same-origin-live-deploy -- --manifest <file> --identity-file <key>`
  after website frontend and auth-server validation
- a repo push only counts as published after that helper-backed CI deploy, or a
  manual invocation of the same helper, completes and revalidates the public
  origin
- login/register overlay safety is now part of the browser launch invariant:
  the decorative auth backdrop may render, but the auth form must stay visibly
  above it on the live public origin

This is the next operator step after:

- the public website implementation packet
- the server-backed release-manifest authority packet
- the runtime-readiness placeholder-detection hardening

## Current live problem on `2026-06-22`

The public origin is still serving the older placeholder rollout page:

- `https://hypertwist.app/`
- `https://hypertwist.app/api/auth/health`
- `https://hypertwist.app/api/releases/manifest`

All three currently return the placeholder HTML page instead of the real
same-origin website/auth-server deployment.

## Intended production topology

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`

That gives one public origin for:

- the marketing shell
- auth routes
- dashboard routes
- `GET /api/auth/health`
- `GET /api/releases/manifest`
- `POST /api/billing/paddle/webhook`

## Repo-provided deployment templates

The repo now includes first-party same-origin templates under:

- `website/deploy/hypertwist.same-origin.bundle.example.json`
- `website/deploy/nginx/hypertwist.app.conf.example`
- `website/deploy/systemd/hypertwist-website-auth-server.service.example`
- `website/deploy/README.md`
- `website/scripts/render-same-origin-deployment.mjs`
- `website/scripts/render-same-origin-bundle.mjs`

These are operator handoff templates, not automatically applied machine state.

## Expected filesystem posture on the VPS

Example working checkout:

- repo root: `/srv/hypertwist/current`
- frontend: `/srv/hypertwist/current/website`
- server: `/srv/hypertwist/current/website/server`

Expected build output:

- `/srv/hypertwist/current/website/dist/index.html`

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

Ensure the current `main` branch includes:

- `f160b7e` `Harden live website deployment readiness probe`
- the earlier website/auth/distribution commits on `2026-06-22`

### 2. Fill real production env values

Preferred source of truth:

- copy `website/deploy/hypertwist.same-origin.bundle.example.json`
- save the real filled-in file as `website/deploy/hypertwist.same-origin.bundle.json`
- replace its placeholder values with the real production values
- set `publicOrigin` to `https://hypertwist.app` for the canonical live lane

Fallback direct-edit source:

- `website/.env.production.example`
- `website/server/.env.production.example`

Replace placeholders with real production values before launch.

For non-public rehearsal before those live values exist, the same bundle
renderer now also supports `deploymentTier: "preview"` with honest empty
operator checkout, Windows download, release-manifest
version/build/checksum/published-at, and `server.paddleWebhookSecret` values.
That preview posture is only for staging proof and validation; it does not
replace the real launch values above.

Minimum required live values include:

- `VITE_PUBLIC_DEPLOYMENT_TIER=launch`
- `VITE_SUPERTOKENS_API_DOMAIN=https://hypertwist.app`
- `VITE_SUPERTOKENS_WEBSITE_DOMAIN=https://hypertwist.app`
- `VITE_AUTH_API_BASE_URL=https://hypertwist.app`
- real operator checkout URL
- real Windows download URL
- real corresponding-source URL
- `DEPLOYMENT_TIER=launch`
- `API_DOMAIN=https://hypertwist.app`
- `WEBSITE_DOMAIN=https://hypertwist.app`
- `PORT=3011`
- `COOKIE_SECURE=true`
- real `PADDLE_WEBHOOK_SECRET`
- real billing product/price map
- real release-manifest metadata values

### 3. Install dependencies and build the frontend

```bash
cd /srv/hypertwist/current/website
npm ci
npm run build

cd /srv/hypertwist/current/website/server
npm ci
```

### 3A. Recommended non-root staging proof before cutover

From the local HyperTwist repo on the Linux side, run the first-party staging
proof helper against the real VPS before any root-owned vhost or service change:

```bash
cd /home/dev/src/HyperTwist/website
npm run run:vps-same-origin-staging-proof -- \
  --manifest deploy/hypertwist.same-origin.bundle.json \
  --identity-file /path/to/shared-vps-key \
  --archive-source worktree \
  --json
```

Current behavior of that helper:

- archives the current committed `website/` tree by default
- can instead archive the live local worktree through `--archive-source worktree`
- stages it into a temporary VPS checkout under `/tmp/`
- rewrites the manifest for temporary checkout ownership and port `3011`
- installs deps, renders the same-origin bundle, builds the frontend, and boots
  the auth server on the shared-host-safe staging port
- verifies:
  - `GET /health`
  - `GET /api/auth/health`
  - `GET /api/releases/manifest`
  - root shell marker plus `#root` mount
- cleans the temporary checkout and staging listener back up on success

Latest proof result on `2026-06-22`:

- passed on the real shared VPS
- confirmed green health/auth/release/shell behavior on `3011`
- preview-tier proof also passed while staging the live local worktree rather
  than `HEAD`, with `runtime.mode: mixed`, `public_origin_ready: true`, and no
  configured download or webhook-secret release lane yet
- left no retained temp checkout afterward

Live cutover result on `2026-06-22`:

- the committed HyperTwist `website/` tree is now installed at
  `/srv/hypertwist/current`
- service user `hypertwist` now owns the live checkout and auth-state path
- `hypertwist-website-auth-server.service` is active and NGINX now proxies
  `https://hypertwist.app` to `127.0.0.1:3011`
- live `GET /health`, `GET /api/auth/health`,
  `GET /api/releases/manifest`, and root-shell verification are green on the
  public origin
- the rendered preview-tier env pair now passes the real runtime-readiness CLI
  against `https://hypertwist.app`
- the remaining gap is launch-tier value completion, not root-owned website
  host cutover

### 3B. Scripted live deploy after proof

The repo now also carries a bounded root-owned deploy helper that codifies the
same checkout/env/build/systemd/nginx sequence that was used for the live
preview cutover:

```bash
cd /home/dev/src/HyperTwist/website
npm run run:vps-same-origin-live-deploy -- \
  --manifest /tmp/hypertwist.preview.bundle.json \
  --identity-file /path/to/shared-vps-key \
  --json
```

Current behavior of that helper:

- stages the committed `website/` tree into the live checkout root
- uploads the rendered bundle outputs
- creates the runtime user/group and owned state directories when needed
- installs `website/.env` plus `website/server/.env`
- runs `npm ci`, `npm --prefix server ci`, and `npm run build` as the service user
- verifies the rendered `systemd` unit, replaces the live `systemd` plus
  `nginx` files, restarts the service, reloads NGINX, and then validates the
  public origin through the runtime-readiness lane
- supports `--dry-run`, `--json`, `--archive-source worktree`,
  `--skip-live-validation`, and `--keep-bundle-dir`

Latest helper result on `2026-06-22`:

- passed against the already-live shared-VPS preview deployment
- confirmed the helper is idempotent for the current preview-tier host state

### 4. Install the systemd service

Preferred validated bundle-generation path:

```bash
cd /srv/hypertwist/current/website
npm run render:same-origin-bundle -- \
  --manifest deploy/hypertwist.same-origin.bundle.json \
  --out-dir /tmp/hypertwist-same-origin-bundle
```

That bundle emits:

- `/tmp/hypertwist-same-origin-bundle/website.env`
- `/tmp/hypertwist-same-origin-bundle/server.env`
- `/tmp/hypertwist-same-origin-bundle/hypertwist-website-auth-server.service`
- `/tmp/hypertwist-same-origin-bundle/hypertwist.app.conf`

Install the rendered env files into the live checkout before enabling the
service:

```bash
cp /tmp/hypertwist-same-origin-bundle/website.env /srv/hypertwist/current/website/.env
cp /tmp/hypertwist-same-origin-bundle/server.env /srv/hypertwist/current/website/server/.env
```

Fallback file-only generation path:

```bash
cd /srv/hypertwist/current/website
npm run render:same-origin-deployment -- \
  --checkout-root /srv/hypertwist/current \
  --service-user hypertwist \
  --service-group hypertwist \
  --server-names hypertwist.app,www.hypertwist.app \
  --systemd-out /tmp/hypertwist-website-auth-server.service \
  --nginx-out /tmp/hypertwist.app.conf
```

Systemd source:

- preferred: `/tmp/hypertwist-same-origin-bundle/hypertwist-website-auth-server.service`
- fallback: `/tmp/hypertwist-website-auth-server.service`

Before enabling it:

- confirm `User=` and `Group=` match the real runtime account
- confirm `WorkingDirectory=` matches the real checkout path
- confirm `EnvironmentFile=` matches the real server env path

Install sequence:

```bash
sudo cp /tmp/hypertwist-same-origin-bundle/hypertwist-website-auth-server.service \
  /etc/systemd/system/hypertwist-website-auth-server.service

sudo systemctl daemon-reload
sudo systemctl enable --now hypertwist-website-auth-server.service
sudo systemctl status hypertwist-website-auth-server.service --no-pager
```

### 5. Install the NGINX vhost

NGINX source:

- preferred: `/tmp/hypertwist-same-origin-bundle/hypertwist.app.conf`
- fallback: `/tmp/hypertwist.app.conf`

Install sequence:

```bash
sudo cp /tmp/hypertwist-same-origin-bundle/hypertwist.app.conf \
  /etc/nginx/sites-available/hypertwist.app

sudo ln -s /etc/nginx/sites-available/hypertwist.app /etc/nginx/sites-enabled/hypertwist.app
sudo nginx -t
sudo systemctl reload nginx
```

This config expects the existing Certbot-managed certificate paths already
described in:

- `docs/ops/HYPERTWIST_VPS_TLS_CERTIFICATE_AND_IONOS_SEPARATION_GUIDE_2026-05-30.md`

### 6. Confirm the placeholder page is gone

The current public-origin proof should flip from placeholder HTML to real
website/auth-server behavior:

```bash
curl --silent --show-error https://hypertwist.app/api/auth/health
curl --silent --show-error https://hypertwist.app/api/releases/manifest
curl --silent --show-error https://hypertwist.app/ | rg "hypertwist-site-shell|id=\"root\""
```

That `2026-06-22` placeholder-era gap is now historically closed: the live
origin currently serves the first-party shell and JSON health/manifest routes.
The remaining gap is no longer host cutover; it is launch-tier value
completion and keeping future site changes paired with the real live-deploy
helper instead of assuming a repo push alone is production publication.

### 7. Run the first-party readiness verifier

From the live checkout:

```bash
cd /srv/hypertwist/current/website
npm run check:runtime-readiness -- --frontend-env .env --server-env server/.env --health-url https://hypertwist.app
```

Expected live success conditions:

- `/api/auth/health` returns JSON and reports public-ready posture
- `/api/releases/manifest` returns JSON and keeps anonymous download URLs hidden
- `/` exposes the first-party shell marker and `#root` mount
- no placeholder rollout page text remains

## Fast rollback logic

If cutover breaks:

1. inspect `journalctl -u hypertwist-website-auth-server.service -n 200 --no-pager`
2. inspect `sudo nginx -t`
3. confirm `website/dist/index.html` exists
4. confirm `website/server/.env` has no placeholder values left
5. if necessary, disable the site vhost and restore the prior placeholder-only
   NGINX config while keeping the repo state intact for reattempt

## Authority links

- `website/README.md`
- `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`
