Harden protected website operator surfaces

This commit is contained in:
axiomlogicnexus 2026-06-23 01:53:22 +00:00
parent 00f25338d8
commit 9c93020c69
6 changed files with 615 additions and 44 deletions

View file

@ -63,6 +63,24 @@ into a more practical public operator reference:
operational language, and shipping/payment now carries the real digital
delivery workflow instead of generic storefront filler
## Protected-surface continuation
The protected browser shell is no longer only one strong dashboard route plus
thin adjacent placeholders.
The later continuation also hardened:
- `/app/browser-access` with live auth-health posture, browser-to-desktop
handoff guidance, bounded branch truth, and release-reference links
- `/app/account` with live release-manifest viewer posture, configured target
visibility, and explicit operator follow-through links
- `/app/notices` with protected release-target notice posture, operator duties,
and linked protected/public notices plus corresponding-source references
This keeps the protected app shell aligned with the public manual packet so the
operator surface remains honest and useful after sign-in rather than becoming a
drop in information quality.
## Public pages widened by this packet
- homepage
@ -107,6 +125,11 @@ The same-day public-manual continuation passed:
- `npm run type-check` in `website/server`
- `npm test -- --run` in `website/server`
The protected-surface continuation also passed:
- `npm test -- --run src/__tests__/protected-app-pages.test.tsx src/__tests__/app-route-tree.test.tsx`
- `npm test -- --run` in `website/` after the protected-route widening
The same validation hardening also gives the live-spawned
`website/server` bootstrap proof an explicit `15s` timeout so real child-process
boot plus same-origin HTTP verification does not fail spuriously on a loaded

View file

@ -209,6 +209,17 @@ Follow-up tool refresh on `2026-06-22`:
is still isolated to the same Unreal validator seams rather than the browser
or website lane
Additional follow-up after the protected website hardening continuation:
- `scripts/run-hypertwist-sentrux-source-only.sh` improved slightly to
`Quality: 5902`
- the remaining structural debt still stayed isolated to the same Unreal
validator seams:
- two `HyperTwistSkillTypes.h` `IsStructurallyValid()` functions
- one `HyperTwistRecognitionTypes.h` `IsStructurallyValid()` function
- the protected website/account/notices/browser-access widening did not create
any new cycle debt or large-function debt in the browser or website lane
Important nuance from the `2026-06-22` follow-up:
- a helper-only readability pass on `HyperTwistSkillTypes.h` was tested and

View file

@ -4,6 +4,7 @@ First-party `hypertwist.app` surface for HyperTwist:
- public homepage, about, resources, pricing, support, and legal pages
- browser-facing operator/account dashboard
- protected browser-access, account, and notices routes backed by live auth-health and release-manifest authority
- shared SuperTokens auth posture reused from the FamiliarOS and ScriptoriumAI website lane
- desktop download posture and desktop-link handshake endpoints
- server-backed release-manifest authority shared by public and protected download surfaces
@ -54,6 +55,9 @@ manual:
- the docs and resources pages now also carry a practical simulator-use manual
for recognition, replay, higher-dimensional runtime ownership, and operator
diagnostics without overclaiming browser or VR parity
- the protected app shell now also carries richer operator-facing browser
boundary, account, entitlement, and notices guidance instead of treating
those routes as thin placeholders beside the main dashboard
## Local development

View file

@ -320,4 +320,97 @@ describe('AppRouteTree', () => {
})
expect(screen.getByText('Windows')).toBeTruthy()
})
it('renders the protected browser-access route through the real route tree when authenticated', async () => {
mockUsePlatformAuth.mockReturnValue({
isAuthenticated: true,
isLoading: false,
login: vi.fn(),
register: vi.fn(),
logout: vi.fn(),
toggleColorMode: vi.fn(),
colorMode: 'dark',
superTokensConfigured: true,
user: {
id: 'operator-1',
name: 'Operator',
email: 'operator@hypertwist.app',
plan: 'operator',
role: 'operator',
canDownload: true,
billing: {
source: 'session',
accessStatus: 'active',
canDownload: true,
lastEventType: 'transaction.completed',
},
},
})
renderRoute('/app/browser-access')
expect(await screen.findByText('Current browser posture')).toBeTruthy()
expect(screen.getByText('Browser-to-desktop handoff')).toBeTruthy()
})
it('renders the protected account route through the real route tree when authenticated', async () => {
mockUsePlatformAuth.mockReturnValue({
isAuthenticated: true,
isLoading: false,
login: vi.fn(),
register: vi.fn(),
logout: vi.fn(),
toggleColorMode: vi.fn(),
colorMode: 'dark',
superTokensConfigured: true,
user: {
id: 'operator-1',
name: 'Operator',
email: 'operator@hypertwist.app',
plan: 'operator',
role: 'operator',
canDownload: true,
billing: {
source: 'session',
accessStatus: 'active',
canDownload: true,
lastEventType: 'transaction.completed',
},
},
})
renderRoute('/app/account')
expect(await screen.findByText('Session profile')).toBeTruthy()
expect(screen.getByText('Account follow-through')).toBeTruthy()
})
it('renders the protected notices route through the real route tree when authenticated', async () => {
mockUsePlatformAuth.mockReturnValue({
isAuthenticated: true,
isLoading: false,
login: vi.fn(),
register: vi.fn(),
logout: vi.fn(),
toggleColorMode: vi.fn(),
colorMode: 'dark',
superTokensConfigured: true,
user: {
id: 'operator-1',
name: 'Operator',
email: 'operator@hypertwist.app',
plan: 'operator',
role: 'operator',
canDownload: true,
billing: {
source: 'session',
accessStatus: 'active',
canDownload: true,
lastEventType: 'transaction.completed',
},
},
})
renderRoute('/app/notices')
expect(await screen.findByText('Distribution notices')).toBeTruthy()
expect(screen.getByText('Reference surfaces')).toBeTruthy()
})
})

View file

@ -0,0 +1,215 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter } from 'react-router-dom'
const mockUsePlatformAuth = vi.fn()
const mockGetAuthHealth = vi.fn()
const mockGetReleaseManifest = vi.fn()
vi.mock('../auth/platform-auth', () => ({
usePlatformAuth: () => mockUsePlatformAuth(),
}))
vi.mock('../auth/auth-api', () => ({
buildAuthApiBaseUrls: () => ['https://hypertwist.app'],
createDesktopLinkToken: vi.fn(),
getAuthHealth: (...args: unknown[]) => mockGetAuthHealth(...args),
getReleaseManifest: (...args: unknown[]) => mockGetReleaseManifest(...args),
}))
import { AccountPage, BrowserAccessPage, NoticesPage } from '../pages/app-pages'
function renderPage(page: React.ReactNode, initialEntry: string) {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
return render(
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[initialEntry]}>
{page}
</MemoryRouter>
</QueryClientProvider>,
)
}
function byExactTextContent(expected: string, tagName?: string) {
return (_content: string, node: Element | null) => (
node?.textContent === expected
&& (tagName ? node.tagName === tagName.toUpperCase() : true)
)
}
describe('protected app pages', () => {
beforeEach(() => {
cleanup()
mockUsePlatformAuth.mockReset()
mockGetAuthHealth.mockReset()
mockGetReleaseManifest.mockReset()
mockUsePlatformAuth.mockReturnValue({
user: {
id: 'operator-1',
name: 'Operator',
email: 'operator@hypertwist.app',
authMethod: 'email',
plan: 'operator',
role: 'operator',
canDownload: true,
billing: {
source: 'session',
accessStatus: 'active',
canDownload: true,
lastEventType: 'transaction.completed',
},
},
superTokensConfigured: true,
})
mockGetAuthHealth.mockResolvedValue({
ok: true,
service: 'hypertwist-auth-server',
supertokens: {
configured: true,
reachable: true,
ready: true,
apiVersion: '5.0',
oauth: {
github: false,
google: false,
},
},
fallback: {
enabled: true,
active: false,
reason: null,
},
billing: {
statePath: '/tmp/hypertwist-billing.json',
processedEventCount: 1,
pricePlanMapConfigured: true,
productPlanMapConfigured: true,
webhookSecretConfigured: true,
},
runtime: {
mode: 'public',
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: true,
canDownload: true,
plan: 'operator',
role: 'operator',
accessStatus: 'active',
},
platforms: [
{
platform_key: 'windows',
platform: 'Windows',
subtitle: 'Primary shipping lane',
details: 'Current packaged validation is strongest on the Windows Unreal lane.',
configured: true,
channel: 'candidate',
version: '1.0.0',
build_id: 'win64-1000',
published_at: '2026-06-22T00:00:00.000Z',
file_name: 'HyperTwist-Windows.zip',
file_size_bytes: 1048576,
checksum_sha256: 'abc123',
download_url: 'https://downloads.hypertwist.app/windows.exe',
download_available: true,
validation_summary: {
lane: 'Windows Unreal packaged validation',
result: 'passed',
generated_at: '2026-06-22T01:43:08.7625247Z',
configuration: 'Development',
skip_build: true,
smoke_map_count: 2,
smoke_maps: [
{
map_url: '/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining',
label: 'Magic120Cell dedicated-family training map',
result: 'passed',
},
{
map_url: '/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining',
label: 'MagicCube5D dedicated-family training map',
result: 'passed',
},
],
},
},
{
platform_key: 'macos',
platform: 'macOS',
subtitle: 'Planned distribution surface',
details: 'List a signed desktop build here when the package lane is opened.',
configured: false,
channel: 'preview',
version: null,
build_id: null,
published_at: null,
file_name: null,
file_size_bytes: null,
checksum_sha256: null,
download_url: null,
download_available: false,
},
],
},
})
})
it('surfaces live browser-shell posture and browser-to-desktop guidance', async () => {
renderPage(<BrowserAccessPage />, '/app/browser-access')
expect(await screen.findByText('Current browser posture')).toBeTruthy()
expect(await screen.findByText(byExactTextContent('Auth runtime mode: public', 'LI'))).toBeTruthy()
expect(await screen.findByText(byExactTextContent('Configured release targets: 1/2', 'LI'))).toBeTruthy()
expect(screen.getByRole('link', { name: 'Open dashboard' }).getAttribute('href')).toBe('/app')
expect(screen.getByRole('link', { name: 'Protected notices' }).getAttribute('href')).toBe('/app/notices')
})
it('surfaces account release access and protected follow-through links', async () => {
renderPage(<AccountPage />, '/app/account')
expect(await screen.findByText('Session profile')).toBeTruthy()
expect(screen.getByText(byExactTextContent('Billing source: session', 'LI'))).toBeTruthy()
expect(await screen.findByText(byExactTextContent('Manifest viewer access: active', 'LI'))).toBeTruthy()
expect(await screen.findByText(byExactTextContent('Windows: Primary shipping lane (Version 1.0.0, Channel candidate)', 'LI'))).toBeTruthy()
expect(screen.getByRole('link', { name: 'Open downloads' }).getAttribute('href')).toBe('/app/downloads')
expect(screen.getByRole('link', { name: 'Get help' }).getAttribute('href')).toBe('/support?topic=operator-access')
})
it('surfaces protected notices references and release-target notice posture', async () => {
renderPage(<NoticesPage />, '/app/notices')
expect(await screen.findByText('Distribution notices')).toBeTruthy()
expect(await screen.findAllByText(byExactTextContent('Configured release targets: 1/2', 'LI'))).toHaveLength(1)
expect(screen.getByText(byExactTextContent('Public repository/notices URL: https://github.com/hypertwist/hypertwist', 'LI'))).toBeTruthy()
expect(screen.getByText(byExactTextContent('Corresponding-source URL: https://hypertwist.app/open-source/source.zip', 'LI'))).toBeTruthy()
expect(screen.getByText(byExactTextContent('Windows: configured, validation passed', 'LI'))).toBeTruthy()
expect(screen.getByRole('link', { name: 'Public notices' }).getAttribute('href')).toBe('/open-source-notices')
})
})

View file

@ -1,14 +1,14 @@
import { useMemo } from 'react'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useSearchParams } from 'react-router-dom'
import { Link, useSearchParams } from 'react-router-dom'
import { buildAuthApiBaseUrls, createDesktopLinkToken, getAuthHealth, getReleaseManifest } from '../auth/auth-api'
import { usePlatformAuth } from '../auth/platform-auth'
import { SiteMetadata } from '../components/seo/SiteMetadata'
import { ReleaseValidationSummary } from '../components/ui/ReleaseValidationSummary'
import { resolvePublicLaunchReadiness } from '../public-launch'
import { downloadTargets, launchReadiness, mplSourceUrl, openSourceRepoUrl, planCatalog, publicDocsUrl, releaseNotesUrl } from '../site-config'
import { buildReleaseMetadataItems, resolveReleaseCommerceView, resolveReleaseManifestView } from '../release-manifest'
import { getDownloadPlatformLabel, normalizeDownloadPlatform } from '../site-routes'
import { buildReleaseMetadataItems, resolveReleaseCommerceView, resolveReleaseManifestView, type ReleaseManifestView } from '../release-manifest'
import { buildSupportPath, getDownloadPlatformLabel, normalizeDownloadPlatform } from '../site-routes'
import { desktopDownloadSteps, roadmapHonestyCards } from '../site-data'
function Panel({
@ -49,17 +49,84 @@ function buildProtectedReleaseManifestFallback(supportEmail: string) {
}
}
export function DashboardOverviewPage() {
const { user, superTokensConfigured } = usePlatformAuth()
const healthQuery = useQuery({
queryKey: ['auth-health'],
function useProtectedAuthHealthQuery(scope: string) {
return useQuery({
queryKey: ['auth-health', scope],
queryFn: getAuthHealth,
retry: false,
})
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'dashboard', user?.id || 'anonymous', user?.canDownload === true ? 'download' : 'nodownload'],
}
function useProtectedReleaseManifestQuery(scope: string, userId: string | undefined, canDownload: boolean) {
return useQuery({
queryKey: ['release-manifest', scope, userId || 'anonymous', canDownload ? 'download' : 'nodownload'],
queryFn: getReleaseManifest,
retry: false,
})
}
function ReleaseAuthorityLinks({
releaseManifest,
includePublicNoticesLink = false,
includeProtectedNoticesLink = false,
}: {
releaseManifest: ReleaseManifestView
includePublicNoticesLink?: boolean
includeProtectedNoticesLink?: boolean
}) {
const hasLinks = Boolean(
releaseManifest.public_docs_url
|| releaseManifest.release_notes_url
|| releaseManifest.corresponding_source_url
|| releaseManifest.open_source_repo_url
|| includePublicNoticesLink
|| includeProtectedNoticesLink,
)
if (!hasLinks) {
return null
}
return (
<div className="button-row top-gap">
{releaseManifest.public_docs_url ? (
<a className="button button--ghost" href={releaseManifest.public_docs_url} target="_blank" rel="noreferrer">
Public docs
</a>
) : null}
{releaseManifest.release_notes_url ? (
<a className="button button--ghost" href={releaseManifest.release_notes_url} target="_blank" rel="noreferrer">
Release notes
</a>
) : null}
{releaseManifest.corresponding_source_url ? (
<a className="button button--ghost" href={releaseManifest.corresponding_source_url} target="_blank" rel="noreferrer">
Corresponding source
</a>
) : null}
{releaseManifest.open_source_repo_url ? (
<a className="button button--ghost" href={releaseManifest.open_source_repo_url} target="_blank" rel="noreferrer">
Repository/notices
</a>
) : null}
{includeProtectedNoticesLink ? (
<Link className="button button--ghost" to="/app/notices">
Protected notices
</Link>
) : null}
{includePublicNoticesLink ? (
<Link className="button button--ghost" to="/open-source-notices">
Public notices
</Link>
) : null}
</div>
)
}
export function DashboardOverviewPage() {
const { user, superTokensConfigured } = usePlatformAuth()
const healthQuery = useProtectedAuthHealthQuery('dashboard')
const releaseManifestQuery = useProtectedReleaseManifestQuery('dashboard', user?.id, user?.canDownload === true)
const desktopLinkMutation = useMutation({
mutationFn: createDesktopLinkToken,
@ -281,11 +348,7 @@ export function DownloadCenterPage() {
const [searchParams] = useSearchParams()
const canDownload = user?.canDownload === true
const requestedPlatform = normalizeDownloadPlatform(searchParams.get('platform'))
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'protected', user?.id || 'anonymous', canDownload ? 'download' : 'nodownload'],
queryFn: getReleaseManifest,
retry: false,
})
const releaseManifestQuery = useProtectedReleaseManifestQuery('protected', user?.id, canDownload)
const releaseManifest = useMemo(
() => resolveReleaseManifestView(
releaseManifestQuery.data?.manifest,
@ -425,6 +488,21 @@ export function DownloadCenterPage() {
}
export function BrowserAccessPage() {
const { user } = usePlatformAuth()
const healthQuery = useProtectedAuthHealthQuery('browser-access')
const releaseManifestQuery = useProtectedReleaseManifestQuery('browser-access', user?.id, user?.canDownload === true)
const releaseManifest = useMemo(
() => resolveReleaseManifestView(
releaseManifestQuery.data?.manifest,
buildProtectedReleaseManifestFallback(user?.email || 'hello@hypertwist.app'),
),
[releaseManifestQuery.data?.manifest, user?.email],
)
const configuredPlatformCount = useMemo(
() => releaseManifest.platforms.filter((platform) => platform.configured).length,
[releaseManifest.platforms],
)
return (
<>
<SiteMetadata
@ -435,18 +513,69 @@ export function BrowserAccessPage() {
/>
<div className="panel-grid">
<Panel title="Current browser posture" kicker="Shipping lane">
<ul className="list">
<li>The embedded browser/CEF shell is live and owned as part of the current shipping posture.</li>
<li>Browser runtime status, runtime-ready typing, and native operator diagnostics are already surfaced in the desktop runtime.</li>
<li>The public browser dashboard is for operator/account/release access, not a claim of full simulator parity.</li>
</ul>
</Panel>
<ul className="list">
<li>The embedded browser/CEF shell is live and owned as part of the current shipping posture.</li>
<li>Browser runtime status, runtime-ready typing, and native operator diagnostics are already surfaced in the desktop runtime.</li>
<li>The public browser dashboard is for operator/account/release access, not a claim of full simulator parity.</li>
</ul>
{healthQuery.isLoading ? <p>Checking current browser-shell auth posture...</p> : null}
{healthQuery.isError ? (
<p className="form-error">
Runtime auth-health could not be loaded. The page is still using the bounded dashboard posture rather than claiming browser-simulator parity.
</p>
) : null}
{healthQuery.data ? (
<ul className="list top-gap">
<li>Auth runtime mode: {healthQuery.data.runtime.mode}</li>
<li>Public auth origin ready: {healthQuery.data.runtime.public_origin_ready ? 'yes' : 'no'}</li>
<li>Fallback currently active: {healthQuery.data.fallback.active ? 'yes' : 'no'}</li>
<li>Session plan: {user?.plan || 'not resolved yet'}</li>
<li>Desktop download entitlement: {user?.canDownload ? 'enabled' : 'not yet entitled'}</li>
</ul>
) : null}
</Panel>
<Panel title="Browser-to-desktop handoff" kicker="Operator workflow">
<p>
The browser shell exists to resolve account, release, and pairing state before the desktop runtime takes over the real simulator job.
</p>
<ul className="list top-gap">
{desktopDownloadSteps.map((step) => (
<li key={step}>{step}</li>
))}
</ul>
<div className="button-row top-gap">
<Link className="button button--ghost" to="/app">
Open dashboard
</Link>
<Link className="button button--ghost" to="/app/downloads">
Open downloads
</Link>
</div>
</Panel>
<Panel title="Release authority around the browser shell" kicker="Distribution context">
{releaseManifestQuery.isError ? (
<p className="form-error">
Live release-manifest lookup failed. Browser-shell release context is currently using fallback metadata instead of current auth-server authority.
</p>
) : null}
<ul className="list">
<li>Configured release targets: {configuredPlatformCount}/{releaseManifest.platforms.length}</li>
<li>Manifest viewer access: {releaseManifest.viewer.accessStatus || 'not resolved yet'}</li>
<li>Support contact: {releaseManifest.support_email}</li>
</ul>
<ReleaseAuthorityLinks releaseManifest={releaseManifest} includeProtectedNoticesLink />
</Panel>
<Panel title="Optional branches remain bounded" kicker="Not widened here">
<ul className="list">
<li>The optional full-browser client remains spec-only.</li>
<li>MagicTile native renderer widening remains explicit No-Go until a real browser-host gap is proven.</li>
<li>This dashboard intentionally stays above those boundaries instead of silently crossing them.</li>
</ul>
<ul className="list">
<li>The optional full-browser client remains spec-only.</li>
<li>MagicTile native renderer widening remains explicit No-Go until a real browser-host gap is proven.</li>
<li>This dashboard intentionally stays above those boundaries instead of silently crossing them.</li>
</ul>
<ul className="list top-gap">
{roadmapHonestyCards.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</Panel>
</div>
</>
@ -455,6 +584,18 @@ export function BrowserAccessPage() {
export function AccountPage() {
const { user, superTokensConfigured } = usePlatformAuth()
const releaseManifestQuery = useProtectedReleaseManifestQuery('account', user?.id, user?.canDownload === true)
const releaseManifest = useMemo(
() => resolveReleaseManifestView(
releaseManifestQuery.data?.manifest,
buildProtectedReleaseManifestFallback(user?.email || 'hello@hypertwist.app'),
),
[releaseManifestQuery.data?.manifest, user?.email],
)
const configuredPlatforms = useMemo(
() => releaseManifest.platforms.filter((platform) => platform.configured),
[releaseManifest.platforms],
)
return (
<>
@ -466,14 +607,63 @@ export function AccountPage() {
/>
<div className="panel-grid">
<Panel title="Session profile" kicker="Account details">
<ul className="list">
<li>Email: {user?.email}</li>
<li>Name: {user?.name}</li>
<li>Plan: {user?.plan}</li>
<li>Desktop downloads: {user?.canDownload ? 'enabled' : 'not yet entitled'}</li>
<li>Billing status: {user?.billing?.accessStatus || 'session-default'}</li>
<li>Auth stack: {superTokensConfigured ? 'SuperTokens-backed' : 'Local fallback mode'}</li>
</ul>
<ul className="list">
<li>Email: {user?.email}</li>
<li>Name: {user?.name}</li>
<li>Plan: {user?.plan}</li>
<li>Role: {user?.role || 'operator'}</li>
<li>Desktop downloads: {user?.canDownload ? 'enabled' : 'not yet entitled'}</li>
<li>Billing status: {user?.billing?.accessStatus || 'session-default'}</li>
<li>Billing source: {user?.billing?.source || 'session'}</li>
<li>Auth stack: {superTokensConfigured ? 'SuperTokens-backed' : 'Local fallback mode'}</li>
</ul>
</Panel>
<Panel title="Entitlement and release access" kicker="Protected release lane">
{releaseManifestQuery.isLoading ? <p>Refreshing release-access posture from the live manifest...</p> : null}
{releaseManifestQuery.isError ? (
<p className="form-error">
Live release-manifest lookup failed. This account view is using bounded fallback metadata until auth-server release authority returns.
</p>
) : null}
<ul className="list">
<li>Manifest viewer authenticated: {releaseManifest.viewer.authenticated ? 'yes' : 'no'}</li>
<li>Manifest viewer plan: {releaseManifest.viewer.plan || user?.plan || 'not resolved yet'}</li>
<li>Manifest viewer access: {releaseManifest.viewer.accessStatus || 'not resolved yet'}</li>
<li>Configured release targets: {configuredPlatforms.length}/{releaseManifest.platforms.length}</li>
</ul>
{configuredPlatforms.length > 0 ? (
<ul className="list top-gap">
{configuredPlatforms.map((platform) => {
const metadataItems = buildReleaseMetadataItems(platform)
const compactMetadata = metadataItems.slice(0, 2).map((item) => `${item.label} ${item.value}`).join(', ')
return (
<li key={platform.platform_key}>
{platform.platform}: {platform.subtitle}{compactMetadata ? ` (${compactMetadata})` : ''}
</li>
)
})}
</ul>
) : null}
</Panel>
<Panel title="Account follow-through" kicker="Next actions">
<p>
Use the dashboard when you need a desktop-link token, the download center when you need an entitled package, and the notices lane when you need distribution/legal references.
</p>
<div className="button-row top-gap">
<Link className="button button--ghost" to="/app">
Open dashboard
</Link>
<Link className="button button--ghost" to="/app/downloads">
Open downloads
</Link>
<Link className="button button--ghost" to="/app/notices">
Review notices
</Link>
<Link className="button button--ghost" to={buildSupportPath('operator-access')}>
Get help
</Link>
</div>
<ReleaseAuthorityLinks releaseManifest={releaseManifest} includeProtectedNoticesLink />
</Panel>
</div>
</>
@ -482,11 +672,7 @@ export function AccountPage() {
export function NoticesPage() {
const { user } = usePlatformAuth()
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'protected', user?.id || 'anonymous', user?.canDownload === true ? 'download' : 'nodownload'],
queryFn: getReleaseManifest,
retry: false,
})
const releaseManifestQuery = useProtectedReleaseManifestQuery('notices', user?.id, user?.canDownload === true)
const releaseManifest = useMemo(
() => resolveReleaseManifestView(
releaseManifestQuery.data?.manifest,
@ -494,6 +680,10 @@ export function NoticesPage() {
),
[releaseManifestQuery.data?.manifest, user?.email],
)
const configuredPlatforms = useMemo(
() => releaseManifest.platforms.filter((platform) => platform.configured),
[releaseManifest.platforms],
)
return (
<>
@ -505,12 +695,47 @@ export function NoticesPage() {
/>
<div className="panel-grid">
<Panel title="Distribution notices" kicker="Legal posture">
<p>
Pricing, checkout, and download surfaces must expose open-source notices and corresponding-source guidance whenever shipped builds contain MPL-covered material.
</p>
<p>
Current corresponding-source URL: {releaseManifest.corresponding_source_url || 'configure the corresponding-source URL before public launch'}
</p>
<p>
Pricing, checkout, and download surfaces must expose open-source notices and corresponding-source guidance whenever shipped builds contain MPL-covered material.
</p>
{releaseManifestQuery.isLoading ? <p>Refreshing notices posture from the live release manifest...</p> : null}
{releaseManifestQuery.isError ? (
<p className="form-error">
Live release-manifest lookup failed. This page is using bounded fallback notice metadata until auth-server release authority returns.
</p>
) : null}
<ul className="list top-gap">
<li>Configured release targets: {configuredPlatforms.length}/{releaseManifest.platforms.length}</li>
<li>Viewer download access: {releaseManifest.viewer.canDownload ? 'enabled' : 'not yet enabled'}</li>
<li>Public repository/notices URL: {releaseManifest.open_source_repo_url || 'configure the public repository/notices URL before launch'}</li>
<li>Corresponding-source URL: {releaseManifest.corresponding_source_url || 'configure the corresponding-source URL before public launch'}</li>
</ul>
</Panel>
<Panel title="Operator obligations" kicker="Keep aligned">
<ul className="list">
<li>Keep pricing, download, and notices surfaces aligned whenever a public downloadable build is offered.</li>
<li>Keep release notes and package proof reachable so public audiences can see what build posture is actually being distributed.</li>
<li>Do not present browser access as simulator parity just because distribution/legal pages are live.</li>
</ul>
{releaseManifest.platforms.length > 0 ? (
<ul className="list top-gap">
{releaseManifest.platforms.map((platform) => (
<li key={platform.platform_key}>
{platform.platform}: {platform.configured ? 'configured' : 'not configured'}{platform.validation_summary ? `, validation ${platform.validation_summary.result}` : ''}
</li>
))}
</ul>
) : null}
</Panel>
<Panel title="Reference surfaces" kicker="Release references">
<p>
Operators should keep public notices, protected notices, release notes, and corresponding source reachable from the same release story.
</p>
<ReleaseAuthorityLinks
releaseManifest={releaseManifest}
includeProtectedNoticesLink
includePublicNoticesLink
/>
</Panel>
</div>
</>