Add shared public launch status banner

This commit is contained in:
axiomlogicnexus 2026-06-22 08:33:59 +00:00
parent 6b6a1ad9af
commit 95ccba35f2
8 changed files with 184 additions and 28 deletions

View file

@ -127,12 +127,14 @@ so external-facing copy no longer depends on operators opening the protected
dashboard to see whether checkout, download, or notice configuration is still
missing.
The homepage now also carries a first-party public-site-status section using
that same bounded launch-status lane, and the public launch-status component
now consumes live auth-health webhook/runtime truth in addition to the shared
release-manifest download readiness so public marketing copy does not imply
launch readiness when checkout/download config exists but the live billing
webhook or public-origin runtime posture is still incomplete.
The shared marketing shell now also carries a compact first-party
public-site-status banner across public pages, the homepage keeps a fuller
public-site-status section using that same bounded launch-status lane, and the
public launch-status component now consumes live auth-health webhook/runtime
truth in addition to the shared release-manifest download readiness so public
marketing copy does not imply launch readiness when checkout/download config
exists but the live billing webhook or public-origin runtime posture is still
incomplete.
The login and register pages now also surface those shared-auth runtime warnings
instead of silently behaving like production auth when the lane is still in

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 homepage now also exposes that same public-site-status lane directly, and the shared public launch-status component now consumes live auth-health webhook/runtime truth in addition to release-manifest download readiness so public marketing copy does not claim launch posture from static checkout/download config alone. 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. The same deployment lane now also distinguishes `launch` from `preview` posture so honest missing checkout/download/webhook/release values are accepted only for non-public rehearsal while placeholder strings still fail, `runtime.mode: mixed` plus `public_origin_ready: true` counts as valid preview-host proof, and the staging helper can archive either committed `HEAD` or the live worktree through `--archive-source worktree`. An isolated VPS-local staging proof then confirmed that both the committed HyperTwist website lane and the later preview-tier worktree packet can serve green health, release-manifest, and first-party shell responses on that real shared host behind `3011`, and a later root-owned cutover then replaced the public placeholder site with the live first-party same-origin preview deployment on `https://hypertwist.app`. The package now also ships a bounded root-owned live-deploy helper that stages the committed checkout, uploads the rendered bundle, installs env, rebuilds the site, replaces the live `systemd` plus `nginx` files, and validates the public origin; that helper has already been re-proved idempotently against the live host. The repo now also ships that host-proof flow as a first-party staging helper so future sessions can rerun the temp checkout/build/boot verification path directly before or after root-owned cutover. |
| 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 shared marketing shell now also carries a compact public-site-status banner across public pages, the homepage keeps a fuller status section, and the shared public launch-status component now consumes live auth-health webhook/runtime truth in addition to release-manifest download readiness so public marketing copy does not claim launch posture from static checkout/download config alone. 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. The same deployment lane now also distinguishes `launch` from `preview` posture so honest missing checkout/download/webhook/release values are accepted only for non-public rehearsal while placeholder strings still fail, `runtime.mode: mixed` plus `public_origin_ready: true` counts as valid preview-host proof, and the staging helper can archive either committed `HEAD` or the live worktree through `--archive-source worktree`. An isolated VPS-local staging proof then confirmed that both the committed HyperTwist website lane and the later preview-tier worktree packet can serve green health, release-manifest, and first-party shell responses on that real shared host behind `3011`, and a later root-owned cutover then replaced the public placeholder site with the live first-party same-origin preview deployment on `https://hypertwist.app`. The package now also ships a bounded root-owned live-deploy helper that stages the committed checkout, uploads the rendered bundle, installs env, rebuilds the site, replaces the live `systemd` plus `nginx` files, and validates the public origin; that helper has already been re-proved idempotently against the live host. The repo now also ships that host-proof flow as a first-party staging helper so future sessions can rerun the temp checkout/build/boot verification path directly before or after root-owned cutover. |
| 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. The same protected overview now also consumes the server-backed Windows packaged-validation summary that the release-manifest authority exposes, so operators can see current higher-dimensional desktop proof without drilling into the dedicated downloads screen. 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. That manifest now also carries first-party packaged-validation summary truth for the Windows higher-dimensional desktop lane, so the public `/download` page, the public `/resources` reference page, and the protected `/app/downloads` surface can project real package evidence for the dedicated-family `Magic120Cell` / `MagicCube5D` maps even while launch-tier release URLs remain unconfigured. 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

@ -296,7 +296,8 @@ Current consolidated milestone snapshot:
`Magic120Cell` and `MagicCube5D` even before the launch-tier release URLs are
populated,
and a follow-on same-family marketing-shell continuation now also exposes a
first-party public-site-status section on the homepage while tightening the
compact first-party public-site-status banner across the shared marketing
shell, keeps a fuller status section on the homepage, and tightens the
shared public launch-status component to consume live auth-health webhook and
public-origin runtime posture instead of treating static checkout/download
configuration alone as launch-ready,

View file

@ -155,9 +155,9 @@ Use the runtime-readiness command before public launch or deployment approval:
- same-origin preview proof may remain in runtime mode `mixed` when public
origin readiness is true and the only remaining warning is the loopback
SuperTokens-core notice
- the homepage now also carries a first-party public-site-status section so the
preview-versus-launch boundary is visible on the primary public route instead
of only on pricing, download, or notices surfaces
- the shared marketing shell now also carries a compact first-party
public-site-status banner across public pages, while the homepage keeps a
fuller public-site-status section for the same preview-versus-launch truth
- 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
- the repo now also includes first-party same-origin `nginx` and `systemd`
handoff templates under `website/deploy/`

View file

@ -1,4 +1,5 @@
import { cleanup, render, screen } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import userEvent from '@testing-library/user-event'
import { MemoryRouter, Route, Routes, useLocation } from 'react-router-dom'
import { beforeEach, describe, expect, it, vi } from 'vitest'
@ -7,11 +8,18 @@ import { ROUTER_FUTURE_FLAGS } from '../router/router-future'
const mockUsePlatformAuth = vi.fn()
const mockLogout = vi.fn()
const mockToggleColorMode = vi.fn()
const mockGetAuthHealth = vi.fn()
const mockGetReleaseManifest = vi.fn()
vi.mock('../auth/platform-auth', () => ({
usePlatformAuth: () => mockUsePlatformAuth(),
}))
vi.mock('../auth/auth-api', () => ({
getAuthHealth: (...args: unknown[]) => mockGetAuthHealth(...args),
getReleaseManifest: (...args: unknown[]) => mockGetReleaseManifest(...args),
}))
import { MarketingShell } from '../components/layout/MarketingShell'
import { AppShell } from '../components/layout/AppShell'
import { ProtectedRoute } from '../components/routes/ProtectedRoute'
@ -34,12 +42,46 @@ function renderProtectedRoute(initialEntry: string) {
)
}
function renderMarketingShell(isAuthenticated = false) {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
mockUsePlatformAuth.mockReturnValue({
isAuthenticated,
isLoading: false,
logout: () => mockLogout(),
toggleColorMode: () => mockToggleColorMode(),
colorMode: 'dark',
user: {
name: 'Operator',
plan: 'operator',
},
})
return render(
<QueryClientProvider client={queryClient}>
<MemoryRouter future={ROUTER_FUTURE_FLAGS}>
<MarketingShell eyebrow="Eyebrow" title="Title" lede="Lede">
<div>Body</div>
</MarketingShell>
</MemoryRouter>
</QueryClientProvider>,
)
}
describe('website route and shell behavior', () => {
beforeEach(() => {
cleanup()
mockUsePlatformAuth.mockReset()
mockLogout.mockReset()
mockToggleColorMode.mockReset()
mockGetAuthHealth.mockReset()
mockGetReleaseManifest.mockReset()
mockUsePlatformAuth.mockReturnValue({
isAuthenticated: false,
isLoading: false,
@ -51,6 +93,61 @@ describe('website route and shell behavior', () => {
plan: 'operator',
},
})
mockGetAuthHealth.mockResolvedValue({
ok: true,
service: 'hypertwist-auth-server',
supertokens: {
configured: true,
reachable: true,
ready: true,
apiVersion: '5.4',
error: null,
oauth: {
github: false,
google: false,
},
},
fallback: {
enabled: true,
active: false,
reason: null,
},
billing: {
statePath: '/var/lib/hypertwist/auth/hypertwist-billing-state.json',
processedEventCount: 0,
pricePlanMapConfigured: false,
productPlanMapConfigured: false,
webhookSecretConfigured: false,
},
runtime: {
mode: 'mixed',
public_origin_ready: true,
cookie_secure: true,
api_domain: 'https://hypertwist.app',
website_domain: 'https://hypertwist.app',
warnings: [],
errors: [],
},
})
mockGetReleaseManifest.mockResolvedValue({
ok: true,
manifest: {
generated_at: '2026-06-22T12:00:00.000Z',
support_email: 'hello@hypertwist.app',
public_docs_url: 'https://docs.hypertwist.app',
release_notes_url: 'https://notes.hypertwist.app',
corresponding_source_url: 'https://hypertwist.app/open-source/source.zip',
open_source_repo_url: 'https://github.com/hypertwist/hypertwist',
viewer: {
authenticated: false,
canDownload: false,
plan: null,
role: null,
accessStatus: null,
},
platforms: [],
},
})
})
it('shows the protected-route loader while auth state is still resolving', () => {
@ -82,16 +179,18 @@ describe('website route and shell behavior', () => {
})
it('switches the marketing-shell auth action between login and dashboard', () => {
const { rerender } = render(
<MemoryRouter future={ROUTER_FUTURE_FLAGS}>
<MarketingShell eyebrow="Eyebrow" title="Title" lede="Lede">
<div>Body</div>
</MarketingShell>
</MemoryRouter>,
)
const { rerender } = renderMarketingShell(false)
expect(screen.getByRole('link', { name: 'Log in' }).getAttribute('href')).toBe('/login')
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
mockUsePlatformAuth.mockReturnValue({
isAuthenticated: true,
isLoading: false,
@ -105,11 +204,13 @@ describe('website route and shell behavior', () => {
})
rerender(
<MemoryRouter future={ROUTER_FUTURE_FLAGS}>
<MarketingShell eyebrow="Eyebrow" title="Title" lede="Lede">
<div>Body</div>
</MarketingShell>
</MemoryRouter>,
<QueryClientProvider client={queryClient}>
<MemoryRouter future={ROUTER_FUTURE_FLAGS}>
<MarketingShell eyebrow="Eyebrow" title="Title" lede="Lede">
<div>Body</div>
</MarketingShell>
</MemoryRouter>
</QueryClientProvider>,
)
expect(screen.getByRole('link', { name: 'Open dashboard' }).getAttribute('href')).toBe('/app')

View file

@ -1,5 +1,6 @@
import { Link, NavLink } from 'react-router-dom'
import { usePlatformAuth } from '../../auth/platform-auth'
import { MarketingLaunchStatusBanner } from '../ui/PublicLaunchStatus'
import { brandConfig } from '../../site-config'
import { footerLinks } from '../../site-data'
@ -65,6 +66,7 @@ export function MarketingShell({
<h1>{title}</h1>
<p className="page-hero__lede">{lede}</p>
</section>
<MarketingLaunchStatusBanner />
{children}
</main>

View file

@ -11,11 +11,7 @@ import {
import { launchReadiness } from '../../site-config'
import { buildSupportPath } from '../../site-routes'
export function PublicLaunchStatus({
title = 'Current public launch status',
}: {
title?: string
}) {
function usePublicLaunchStatus() {
const authHealthQuery = useQuery({
queryKey: ['auth-health', 'public-launch-status'],
queryFn: getAuthHealth,
@ -62,6 +58,52 @@ export function PublicLaunchStatus({
return labels
}, [authHealthQuery.isError, authHealthQuery.isLoading, missingItems, runtimeReady, webhookReady])
return {
authHealthQuery,
checklist,
missingLabels,
ready,
releaseManifestQuery,
runtimeReady,
webhookReady,
}
}
export function MarketingLaunchStatusBanner() {
const { missingLabels, ready } = usePublicLaunchStatus()
return (
<article className="callout site-status-banner">
<div className="status-row">
<p className={`status-pill${ready ? ' status-pill--success' : ''}`}>
{ready ? 'Launch-ready posture' : 'Preview posture'}
</p>
<strong>Public site status</strong>
</div>
<p>
{ready
? 'Checkout, release, notices, and live auth-runtime posture are aligned for the current public lane.'
: `Live preview lane: remaining launch blockers are ${missingLabels.join(', ')}.`}
</p>
<div className="status-row">
<Link className="inline-link" to="/pricing">
View launch posture
</Link>
<Link className="inline-link" to={buildSupportPath('launch-readiness')}>
Get launch help
</Link>
</div>
</article>
)
}
export function PublicLaunchStatus({
title = 'Current public launch status',
}: {
title?: string
}) {
const { authHealthQuery, checklist, missingLabels, ready, releaseManifestQuery, runtimeReady, webhookReady } = usePublicLaunchStatus()
return (
<article className="callout">
<p className={`status-pill${ready ? ' status-pill--success' : ''}`}>

View file

@ -409,6 +409,14 @@ img {
margin-bottom: 0;
}
.site-status-banner {
margin-bottom: 1.4rem;
}
.site-status-banner .inline-link {
margin-top: 0;
}
.list {
margin: 0;
padding-left: 1.15rem;