@tuturuuu/start-bridge. next build (package
build) remains the production path on Vercel. The native build is
build:cloudflare and nothing publishes it; production stays held until an
explicit cutover PR.
Layout
build-info is owned by the Worker. The serwist/[path] route builds the
offline worker with esbuild at request time and stays on the Next build until
the native offline port; the native shell does not register a service worker.
Build notes
- Run Vite with
--configLoader runner(the package scripts do). The bridge’svitepreset is TypeScript with extensionless imports, which the default native-ESM config loader cannot load. - The Worker (
ssr) bundle sets rolldownstrictExecutionOrder: circular server chunks otherwise ran a CommonJS interop wrapper before its chunk initialized and the Worker failed at startup. debugandsupports-color7 are CommonJS andrequire('tty')in their Node entry, which cannot run in the ESM Worker. A resolve plugin uses each package’s ownbrowserentry; ESM releases withexportsmaps are untouched.- Proxy rewrites (crawler link preview, root fallback) reach the page loader
through the
x-tuturuuu-page-rewriterequest header, because the bridge renders the browser-visible URL. The Worker strips any client-sent copy. - The server reads Supabase public configuration from Worker vars at runtime
(
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY); build environment only reaches the client bundle.
Hosts and response headers
Tasks goes live on its own subdomain first (root mode).tasksMounts() in
src/runtime/worker-request.ts admits tasks.tuturuuu.com plus any staging
hostnames an operator lists in the TUTURUUU_STAGING_HOSTS Worker var
(*.workers.dev or *.tuturuuu.com only). It uses mountHosts: [] and
unknownHost: 'reject', so every other host, including the tuturuuu.com
apex, gets a 404 before any app code runs. Request headers never admit a host.
Mounting at tuturuuu.com/tasks waits for the shared-session decision and the
shared auth-boundary guard.
Every response carries x-tuturuuu-runtime: native and the Next build’s
production headers: X-Robots-Tag from the Tasks SEO policy
(PRIVATE_ROBOTS_HEADER), Content-Security-Policy: frame-ancestors 'none',
X-Frame-Options: DENY (except on the Next build’s framable WebGL asset path,
/api/v1/workspaces/*/external-projects/assets/*/webgl) and HSTS. A
response’s own CSP is kept, but
frame-ancestors 'none' replaces any ancestor directive in each of its
policies. It also sends X-Content-Type-Options: nosniff and Referrer-Policy: strict-origin-when-cross-origin. Every
dynamic response is Cache-Control: private, no-store, max-age=0, replacing
any policy the handler set (a private, max-age would keep personalized data
in the browser cache); only GET/HEAD of hashed assets and public files keep
their own caching. GET/HEAD /api/build-info is public on both
builds: the Worker answers it first, and the Next proxy admits it without
session work or ambient credentials. The Worker uses
global_fetch_strictly_public.
Cutover is direct (no staged canary): exact-head gates, cross-family review
and a staging smoke where staging exists, then the flip with the prior Vercel
deployment and captured routing ready as a one-step rollback, a post-deploy
smoke and a 30–60 minute watch.
Local emitted smoke
On a Studio host, afterbun run build:cloudflare, run the emitted Worker in
local workerd with loopback-only fixtures:
/api/build-info, a static file, an unauthenticated page (redirect to
the central login with a verify-token return URL), an API route without a
session (401), the server-function base (415 for a non-JSON POST) and the
runtime header. --local-upstream makes the Worker see the admitted host; with
--local-upstream tuturuuu.com every path must answer 404. Or run
scripts/native-cutover/smoke.mjs against the same origin. This smoke does not replace exact-commit CI, sealed-artifact
checks or signed-in acceptance on staging.
Sealed artifact and signed-in browser gate
scripts/ci/tasks-native-artifact.py seals and restores the Tasks staging
artifact with the shared Apps helper. The Worker identity it requires is the
one void.config.ts declares:
tuturuuu-tasks-vite-staging, compatibility date2026-10-19nodejs_compatandglobal_fetch_strictly_publicworkers_dev: false, no vars and no observability block- static assets with
html_handling: 'none', so the offline worker’s exact/offline.htmlis read directly rather than through the default internal redirect to/offline(a missing value or anauto/trailing-slash policy fails the contract)
public/offline.html is a declared public file, byte-checked like the others;
any other HTML in the output is rejected as prerender output. Run the
regressions with python3 scripts/ci/tasks-native-artifact.test.py (CI runs
them in the build (tasks) job). That job also builds the native candidate,
checks and seals its emitted config, runs bun run --cwd apps/tasks test:runtime and verifies the sealed output is unchanged.
cloudflare/offline-fallback.runtime.mjs checks that /offline.html and
/offline.css answer 200 with their exact bytes and no egress. The app’s
asset fetch follows redirects, which would hide a redirecting policy, so a
probe Worker also reads the ASSETS binding directly (redirect: 'manual')
under the sealed assets block. A negative control with html_handling
dropped must get the 307 to /offline. Only offline.html may be HTML in
the output (PUBLIC_HTML in the wrapper).
After a real bun run build:cloudflare:
apps/tasks/cloudflare/root-browser.profile.mjs: the
fixture user has a personal workspace with its default board, so a signed-in
/ and /vi open that board in English and Vietnamese. The task-board APIs
the root resolves run in the same Worker. See
Signed-in root browser gate.
Shared Calendar task ports
The Calendar task model and view live inpackages/tasks-ui/tu-do/shared.
The model receives headers, navigation and workspace permission services
from its framework adapter. Its server-only entrypoint imports no request
globals or framework navigation APIs. Calendar’s previous paths remain
small adapters and re-exports.
createAuthorizedWorkspaceReader checks the injected actor before reading
workspaces. Each app keeps its own compiled server-function entry and
supplies its registered satellite identity. Do not share a server function
that selects its app audience from client input.
Use the shared scheduling transport and notification view instead of app
copies. The existing shared workspace invitation card remains the client
entry for invitation rendering.