# HyperTwist Auth Server Self-hosted SuperTokens auth backend for the HyperTwist website. Responsibilities: - email/password sign-up and sign-in - optional GitHub and Google OAuth - session bootstrap for the browser dashboard - browser-to-desktop token handoff with exact-origin checks, bounded issuance, and one-time verification tokens - Paddle webhook endpoint with raw-body signature verification - verified billing-event application into first-party account/download entitlement state - public auth-health probe for the website with configured vs reachable core truth - runtime configuration diagnostics for local vs mixed vs public auth posture ## Quick start ```bash cd /home/dev/src/HyperTwist/website/server npm install cp .env.example .env npm run dev ``` The website frontend should point at this backend through: ```bash VITE_SUPERTOKENS_API_DOMAIN=http://localhost:3001 VITE_SUPERTOKENS_WEBSITE_DOMAIN=http://localhost:4273 VITE_AUTH_API_BASE_URL=http://localhost:3001 ``` ## Important note `/api/billing/paddle/webhook` now verifies the `Paddle-Signature` header against `PADDLE_WEBHOOK_SECRET` using the documented raw-body `ts:h1` HMAC-SHA256 flow before it accepts the event. The desktop-link lane is intentionally stricter than a generic session helper: - request origin must match the configured website origin exactly - token issuance is rate-limited per user - verification consumes the token on first successful use - verification now returns resolved download-entitlement posture alongside identity, plan, and role The remaining Paddle deployment work is now configuration, not missing server ownership: - real production checkout URLs - live notification-destination secret management - event-specific async processing after verification The server now also supports a small first-party entitlement store: - `BILLING_STATE_PATH` optionally relocates the persisted billing state file - `PADDLE_PRODUCT_PLAN_MAP` can map Paddle product IDs to `free` / `operator` / `studio` / `enterprise` - `PADDLE_PRICE_PLAN_MAP` can map Paddle price IDs to the same plan codes - if neither map is present, the verified webhook lane can still derive plan from `custom_data.plan` The auth-health route now probes the configured SuperTokens core non-mutatively: - `SUPERTOKENS_HEALTH_TIMEOUT_MS` bounds the probe duration - `/api/auth/health` now distinguishes configured, reachable, and ready state - the protected dashboard can now show when browser fallback posture is active because the shared auth core is unreachable Recommended public `hypertwist.app` posture: - `API_DOMAIN=https://hypertwist.app` - `WEBSITE_DOMAIN=https://hypertwist.app` - `COOKIE_SECURE=true` - real `PADDLE_WEBHOOK_SECRET` - real plan-map configuration or equivalent verified-event plan resolution That recommended same-origin production posture is recorded in: - `docs/ops/HYPERTWIST_WEBSITE_RUNTIME_CONFIGURATION_GUIDE_2026-06-22.md` The server now also supports a bounded first-party same-origin deployment mode: - if `website/dist/index.html` exists, the server auto-serves the built public site from `../dist` - 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 website package now also ships a deploy-time verification command: ```bash cd /home/dev/src/HyperTwist/website npm run check:runtime-readiness -- --frontend-env .env --server-env server/.env --health-url https://hypertwist.app ``` Use `--skip-live-health` when only file-level env verification is possible. Keep the example env files separated by posture: - `website/.env.example` and `website/server/.env.example` remain local-development defaults - `website/.env.production.example` and `website/server/.env.production.example` are public-posture scaffolds that still intentionally fail readiness until placeholder values are replaced The repo bootstrap CI now also validates this auth-server lane through: - `npm ci` - `npm run type-check` - `npm test`