> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Start migration candidate

> Web 1 inventory, staging-only fallback Worker and prerequisites for migrating the central Web origin.

Web 1 adds an unprovisioned TanStack Start / Vite+ / Void Worker candidate in
`apps/web`. The candidate builds from the isolated `cloudflare/` root;
`prepare-build.mjs` mirrors the owning package manifest there for Void framework
detection without loading or renaming Next environment files. Next retains API,
satellite URL bridge and service-worker ownership. The seven static public
legal documents and two security documents have independently default-off native rendering slices alongside the
native `GET /__web1` probe. `build` still builds Next; `build:candidate` builds the
candidate. The retired `apps/tanstack-web` app remains paused.

## Inventory and ownership

`apps/web/start-route-manifest.json` is generated by
`bun apps/web/cloudflare/generate-inventory.ts`; use `--check` to detect drift.
The committed file contains route-level ownership only; ordinary module edits do
not change it. CI generates the full source census with
`--artifact <output.json>` and uploads it as `web-start-census` for seven days.
The artifact inventories pages, API handlers, other handlers, metadata routes, proxy-only
satellite bridges, the four Next `beforeFiles` API rewrites and the root service
worker. Every entry has an owner and a `native-Start` or `Next-fallback`
disposition. The `summary` object gives reproducible counts.

In the CI artifact, for source routes, `roots` includes the route and inherited layouts/templates.
`boundaries` lists first-party modules containing server actions, `after()`,
`connection()` and `use cache` directives in that static import closure.
`modules` records source hashes, imports and signal line numbers. The global
proxy/config closure is separate. Workspace source exports are preferred;
published `dist` exports are mapped to source for this census. Unresolved
first-party imports stay visible and block declaring a slice native. This is a
conservative static inventory, including dynamic imports, rather than a proof
that every reachable operation executes on every request. Nonliteral imports,
third-party internals, aliases reexporting framework functions, and runtime
reachability need slice-specific review before migration. Proxy-only satellite
patterns are explicit census entries; review them whenever `src/proxy.ts` changes.

Except for explicitly enabled native legal or security document GET/HEAD requests, the Worker forwards every URL and method to the admitted Next origin,
including all `/api/*`, server-action POSTs, RSC requests, `_next` assets and
`/serwist/sw.js`. Calendar and Infrastructure API rewrites execute in Next;
they are not reimplemented in the candidate. Satellite redirects retain their
Next owner. The service worker keeps its existing root scope and `/meet` and
`/mail` bypasses; Web 1 does not emit a replacement service worker or copy Web's
public assets into the candidate.

## Native public page slices: legal documents

Scope: Privacy, Terms, Acceptable Use, DPA, SLA, Subprocessors and Community
Guidelines at `/privacy`, `/terms`, `/acceptable-use`, `/legal/dpa`, `/legal/sla`,
`/legal/subprocessors`, `/community-guidelines` and their `/en/` and `/vi/` forms. Each uses the
canonical `@tuturuuu/legal` document
as Next, including every section, summary, effective date and counsel-review
notice. The package remains framework-independent; Start owns its rendering
adapter and reuses the existing React-only legal Markdown renderer. No database,
session, server action, `after()`, `connection()` or cache-components boundary is
imported into that adapter. Inherited Next marketing layouts stay on Next;
the candidate uses Start's static HTML callback to render an accessible document
with document navigation and language links. The default streaming transformer
injects serialization scripts even without a Scripts component; this static
callback intentionally omits that hydration payload and cleans up the SSR state. It deliberately has no hydrated marketing shell or session UI.

The manifest remains **838 entries: 10 native-Start and 828 Next-fallback**.
The two existing security routes are reclassified; the total census stays constant.
Native entries retain their original Next source for migration provenance and
record `nativeSource` for the Start adapter. The full census separately records
its `nativeBoundaries`. Nine `/[locale]` entries count the native documents;
the tenth native entry is the probe. English unprefixed aliases do not add
census entries.

`WEB_START_NATIVE_LEGAL` defaults to `off`, independently of candidate
admission. Only `on` enables the 21 exact localized and unprefixed paths.
Unset, unknown and off values forward them to the attested Next deployment,
preserving path and query.
This is the immediate **route rollback**; disabling `WEB_START_CANDIDATE`
instead closes the entire candidate with 503. No production switch changes here.
POSTs, RSC/action/router-prefetch requests, trailing slashes, unsupported locales,
APIs, satellite URL bridges and the service worker retain Next ownership.
Signed-in legal reads neither inspect nor refresh the session and set no cookies.
Native documents are no-store/noindex, use a script-free CSP and expose
`x-tuturuuu-web-renderer: native-Start`; fallback requests expose
`Next-fallback`. The marker is routing evidence, not source identity.

The native heads read the canonical `marketingSeo` copy for all seven legal
documents. Next and Start share the framework-free
`@tuturuuu/utils/common/page-metadata` URL and social metadata helper; the
existing Next metadata import stays compatible. English aliases canonicalize to
the unprefixed URL, Vietnamese to `/vi/`, with `en-US`, `vi-VN` and `x-default`
alternates. Canonical and social URLs remain on `https://tuturuuu.com` while
staging retains `noindex, nofollow`. The document title applies the shared site
title template; social titles use the localized page title.

`legal-metadata.test.ts` compares all 21 actual Start route heads against the
seven Next legal layouts' metadata generators, including title, description,
canonical, hreflang, every Open Graph field and Twitter metadata. The emitted Worker suite
checks those tags in actual HTML against the canonical message bundles and URL
rules, alongside the existing no-egress and cookie-neutrality assertions.

Regression coverage: `native-legal.test.tsx` checks GET/HEAD, rollback,
canonical content for all seven documents in both languages, cookie neutrality
and fallback boundaries.
`emitted.runtime.mjs` exercises the actual emitted Start renderer and rollback.
`e2e-wave1-staging/web.spec.ts` extends the #7093 acceptance suite with EN/VI
reads of all seven public legal documents, robots noindex checks and a signed-in
workspace fallback round trip.
Shared suite infrastructure is seeded from #7093; reconcile those shared files
with that PR when integrating. Other app cases remain owned by #7093.

## Native security documents (slice 4)

Scope: `/security/policy` and `/security/bug-bounty`, plus their `/en/`
and `/vi/` aliases. `WEB_START_NATIVE_SECURITY=off` is a separate default-off
flag. Only `on` admits their six exact GET/HEAD paths; unset, unknown and off
values immediately restore attested Next fallback with the original query.
Legal rendering continues to follow `WEB_START_NATIVE_LEGAL` independently.
Candidate admission and all RSC/action/router-header, method, host and cookie
boundaries remain the same.

The static adapter reads the existing `security-policy` and `bug-bounty`
EN/VI namespaces. It preserves every current Next heading, report instruction,
scope rule, response commitment, metric, researcher credit and action link.
Vietnamese internal links retain `/vi/`. No Reveal/motion, hydration, session
refresh or database access is included. Content is immediately visible under the
existing document stylesheet and script-free CSP, with noindex and no-store.
This is document styling: Product acceptance of that presentation remains a
cutover prerequisite; it does not claim parity with the Next marketing shell.

Both page-level Next metadata generators override inherited metadata. The security
adapter therefore uses each namespace's `meta` copy rather than the bounty
layout's `marketingSeo` copy. The shared `publicHead` adapter calls the
framework-free page-metadata builder for canonical, hreflang and every social
field. `security-parity.test.tsx` compares actual native route heads with those
Next generators and compares the complete rendered text, headings, lists and
links with the current Next pages in EN and VI. Only the Next motion wrappers
are replaced in the body fixture. A without-fix control must fail when a policy
instruction is removed, then pass when restored.

`native-security.test.ts` covers both independent flags, GET/HEAD, every alias,
rollback, production-host refusal, candidate admission, cookie neutrality and
non-document request fallback. `emitted.runtime.mjs` extends the actual workerd
suite with security-only rendering and security-off/legal-on rollback.
`e2e-wave1-staging/web.spec.ts` adds EN/VI native reads and anonymous reloads.
For the second hosted pass, the release owner disables only the security flag
and runs the same suite with `WAVE1_STAGING_SECURITY_ROLLBACK=confirmed`;
the rollback case requires both security documents on Next while Privacy remains
native. These cases inherit the existing exact-host, distinct-Supabase and egress
guards. Listing them or passing policy unit tests is not hosted acceptance.

## Staging profile

`apps/web/cloudflare/profile.ts` declares the exact host
`web.staging.tutur3u.com`, zone `tutur3u.com`, cookie domain
`.staging.tutur3u.com`, HSTS `max-age=31536000`, and staging Supabase project
`fxefojytwimjiqxseqag`. There are no wildcard hosts. The checked Void config has
no routes, custom domains, schedules or external resource bindings;
`workers_dev` and preview URLs are disabled. No provider resources are created.

Requests fail closed unless all of these runtime settings are admitted:

* `WEB_START_CANDIDATE=staging` (checked default: `off`).
* `AUTH_COOKIE_DOMAIN=.staging.tutur3u.com` and
  `NEXT_PUBLIC_SUPABASE_URL=https://fxefojytwimjiqxseqag.supabase.co`.
* `NEXT_FALLBACK_ORIGIN` is one exact HTTPS staging Next deployment origin,
  with no path, credentials or query. The hostname filter accepts
  `tuturuuu.vercel.app`, the `tuturuuu-*.vercel.app` prefix, or a staging-domain
  hostname; canonical Tuturuuu hosts are rejected. The Vercel prefix is not
  bound to our Vercel team, project or deployment ID and does not prove ownership.
  It has no default. Before forwarding any request, the release owner must
  verify the exact origin's Vercel team, project, immutable deployment ID, actual
  Supabase project, API rewrites and satellite targets. Retain that admission
  receipt privately and repeat verification when the origin changes.
* `TRUSTED_PROXY_ORIGIN_SECRET` must be a staging-only secret of at least 32
  characters, shared only by this Worker and its staging Next origin, and
  **distinct from production's origin-IP-proof secret**. No secret is checked
  in or forwarded as a raw value. Proofs authenticate the IP and timestamp,
  without binding the environment, host or audience; reusing the production
  key would let staging proofs verify in production. Before staging publication
  and after either key rotates, the release owner must compare their SHA-256
  fingerprints privately through the approved secret store and record only a
  distinct-key verification receipt. Equal or unavailable fingerprints block
  publication. Runtime admission checks key presence and length only; it has
  no production fingerprint configured and cannot enforce key separation.

The staging Next deployment must return
`x-tuturuuu-staging-supabase-ref: fxefojytwimjiqxseqag` on **every** response,
including errors, redirects and the service-worker script. Configure that
attestation only after verifying its actual database identity. Missing or
mismatched attestation returns 502 without exposing the upstream body or cookies.
This identity attestation fails closed **after forwarding**; it does not bind
Vercel team ownership or prevent credentials and the signed proof from reaching
an incorrectly admitted origin. Verify ownership before any request is sent.
This requirement deliberately blocks enabling the candidate against an
unqualified Next deployment; Web 1 does not change the current Next deployment
or add its marker. Domain cookies must specify exactly
`.staging.tutur3u.com`; host-only cookies, including `__Host-` cookies, retain
host-only semantics on `web.staging.tutur3u.com`, the browser-facing response
host. They are not shared with central staging or other satellites; the proxy
does not widen them to the staging zone. Other or empty cookie domains return
502\. Each Set-Cookie may contain at most one Domain attribute; duplicates are
rejected even when their values match, regardless of attribute name casing or
whitespace. The entire response is rejected without its body or any cookies.
`handler.test.ts` covers multiple/mixed-case/equal domains, host-only cookies,
and preservation of a single staging domain.

The Worker preserves auth, session, webhook, `x-cron-secret`, RSC and action
headers. It removes hop-by-hop headers, `forwarded`, `true-client-ip`,
`cf-connecting-ip` and all `x-vercel-*` headers before forwarding, then replaces
forwarded identity with the trusted Cloudflare client IP and staging host.
The Cloudflare client IP is authenticated with the existing signed origin-IP
proof. Next must use `TRUSTED_PROXY=cloudflare-origin` to consume that proof;
ordinary Vercel mode identifies the Worker egress instead of the client.
Responses stream without buffering. Status, error body, security headers,
rewrite headers and individual cookies pass through. Upstream-origin Locations
map back to the candidate host; external satellite redirects remain unchanged.
Candidate responses are noindex and are never CDN cached.

## Dormant template and owner-provided staging blockers

`apps/web/cloudflare/staging.env.example` is a placeholder-only input inventory.
The Void config remains unbound, without routes, schedules, workers.dev or preview
URLs, and both gates remain off. Do not deploy the template or put credentials
in tracked files. No new crons, queues, retries or external operations are added
by legal rendering; all five current schedules retain their existing owner.

Release owner must provide and verify these before hosted acceptance:

| Resource or input | Required scope and admission |
| - | - |
| Cloudflare account and scoped token | Workers deployment permissions for the staging account plus only the `tutur3u.com` zone permissions needed for separately authorized ingress; no broad production token |
| Staging Worker and ingress | An owner-provisioned candidate and `web.staging.tutur3u.com` certificate/domain/route; none exists by virtue of this config |
| Staging Supabase keys | Confirm project `fxefojytwimjiqxseqag`, publishable key, fallback-only service secret, staging schema/fixtures, auth callback URLs and disposable account/workspace; never production keys |
| Immutable staging Next fallback | Verified team/project/deployment identity, build-info SHA/environment and per-response staging Supabase attestation; API rewrites and satellite destinations must point only to staging |
| Origin proof secret | Staging-only `TRUSTED_PROXY_ORIGIN_SECRET` and private evidence that it differs from production; matching trusted-proxy settings on Next |
| R2 storage | Separate staging account/bucket/binding or staging S3-compatible credentials and fallback storage-provider setup; legal routes need no R2 binding and do not authorize storage tests |
| Login providers | Staging Turnstile site/secret and allowed hosts; staging OAuth/email credentials and callbacks if the chosen login path requires them |
| Acceptance identity | Exact source SHA, non-production Supabase refs, owner-confirmed disposable account/workspace and credentials supplied through the approved secret store |

Run the #7093 Playwright config with `WAVE1_STAGING_APP=web`,
`WAVE1_STAGING_BASE_URL=https://web.staging.tutur3u.com` and its required
private inputs. Enable each slice flag only for an accepted staging deployment.
The central issuer remains `https://staging.tutur3u.com` for the existing
satellite suites; the Web suite signs in through its own candidate origin.
Production/canonical hosts still fail before Chromium launches; request and
WebSocket fences reject production egress and non-auth writes. The suite does
not provision anything. Hosted EN/VI/session acceptance, source identity, real
Next build and independent review remain release prerequisites.

## Five retained infrastructure blockers

These schedules stay in `apps/web/vercel.json` and run only on their current
Vercel owner. Web 1 neither invokes them on a schedule nor ports them.

| Route | Schedule | Later migration |
| - | - | - |
| `/api/cron/process-notification-batches` | `*/15 * * * *` | Notification batches |
| `/api/cron/process-post-email-queue` | `*/30 * * * *` | Post-email queue |
| `/api/cron/process-report-automation` | `*/15 * * * *` | Report automation |
| `/api/cron/process-topic-announcement-queue` | `*/15 * * * *` | Topic announcements |
| `/api/cron/tasks/deadline-reminders` | `*/5 * * * *` | Deadline reminders |

Each needs a separate bounded, idempotent Cloudflare schedule/queue port,
durable stop fence, duplicate/restart tests and a transfer with no overlapping
scheduler ownership before Next retirement. Reachable `after()` work also needs
explicit ownership before its source route is retired.

## Verification and cutover prerequisites

Run focused source tests from the repo root:

```sh theme={null}
ttr resources run -- bunx --no-install vitest run --config apps/web/cloudflare/vitest.config.mts
bun apps/web/cloudflare/generate-inventory.ts --check
```

On Studio (`skr-studio`) or
the i9, in a separate exact-source checkout with dependencies restored, run:

```sh theme={null}
ttr resources run -- bun run --cwd apps/web build:candidate
ttr resources run -- bun run --cwd apps/web type-check:candidate
ttr resources run -- bun run --cwd apps/web test:candidate:runtime
```

Do not run builds on the low-disk Mac. The candidate pins compatibility date `2026-10-19`, matching the locked
Wrangler/Miniflare supplier. The emitted suite loads every emitted JS module
through Miniflare 5's supported v4-options conversion; it does not rebundle or
substitute the Worker entry. The owning
`web-start-candidate.yaml` CI workflow qualifies source and bundle only, with no
publish job or provider credentials, and retains the existing CI admission gate.
Scoped Oxc checks supplement Biome; no shared formatter migration occurs here.
Wrangler remains the emitted Worker runtime tool. No cf operation is substituted
without authentication, target and emitted-artifact parity evidence.

Before routing even a small page slice: exact-commit CI (including the real Next
build and candidate build), independent security review, emitted Worker runtime
acceptance and sealed restore, verified staging Next/database identity, session
refresh/MFA, CSRF/action semantics, bilingual routes and satellite targets,
root service-worker acceptance, a route-specific ownership decision, and an
immediate rollback to the immutable Next deployment are required. The source
compatibility tests use injected Next/Start fixtures. The emitted runtime suite
uses the exact built Worker on workerd through Wrangler's pinned Miniflare supplier,
with outbound requests restricted to a synthetic Next fixture. It exercises the
actual native Start probe, legal and security document rendering, rollback and fallback boundaries; it does not prove hosted
authentication, provider staging or sealed restore acceptance. Missing checks remain prerequisites,
not inferred passes. Production traffic, DNS and provisioning remain held for this implementation PR.
The release owner holds the recorded automatic production cutover authority,
conditional on staging acceptance and instant rollback readiness. Disabling the candidate restores 503 here; the existing Next
production deployment continues to serve production throughout Web 1.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.