Skip to main content

Scope and admission

Land the production hold before these publisher changes. Meet, Calendar, Mail and Apps production publication remains held for wave-1 cutover. The production planner holds in tuturuuu.ci.ts remain authoritative. Native Calendar/Mail/Apps jobs additionally report the source hold in scripts/ci/wave1-native-profiles.json; removing either hold alone does not authorize publication. A later explicit cutover PR must reconcile both policies and its exact production promotion range. These workflows add no automatic schedule and perform no provider or DNS setup. PR profile qualification has read-only deployment permission. Each caller grants deployments: write only to its separate staging publication job, after successful profile qualification and staging policy admission on a main push or dispatch. The reusable qualification workflow never calls a publisher, so read-only callers do not need to grant nested write permissions. wave1-native-permissions.test.js covers this boundary, including same-repository PR events. Main pushes and trusted main dispatch can publish staging only, after provisioning the repository enable flag and an exact source/artifact admission. PRs and other branches qualify artifacts without publication. Existing Vercel deployments, project framework settings and disabled Git deployment remain unchanged. Existing central APIs and scheduler/provider owners stay authoritative; a native frontend is not a completed Vercel exit.

Identities and resource continuity

Owner decision: staging stays in the production Cloudflare account. The stagingAdmission record in scripts/ci/wave1-native-profiles.json records this exception as same-account-staging-only, with shared-account residual risk and a required scoped token. The account identity is supplied by the repository variable CLOUDFLARE_STAGING_ACCOUNT_ID; publication compares its value with the reviewed profile account and fails closed if absent or different. No new account is created. This is resource and credential isolation within one account, with shared account administration, account-wide limits and billing risk. A secret name cannot prove its actual permission scope: the owner must provision and review a token limited to the staging operations/resources supported by Cloudflare, and acknowledge any remaining account-level Worker permission exposure before admission. CLOUDFLARE_WORKERS_SUBDOMAIN is the owner’s actual account subdomain, not a guessed hostname. Configure it as a repository variable. Staging hosts are the corresponding Worker name followed by .<CLOUDFLARE_WORKERS_SUBDOMAIN>.workers.dev as an identity only; those URLs are disabled for serving. Publication profiles are derived explicitly from the original Void-emitted config; the source configs are retained. The derived config, original modules/assets and manifest are sealed together, then restored without rebuilding. Nonempty source resource bindings, routes and unreviewed vars fail qualification rather than being dropped. Frontends have only ASSETS; Calendar and Mail preserve central API ownership. There is no D1, KV, DO, queue or scheduler binding for these three frontends. The publisher uses a separate owner-provisioned D1 control database; it is never bound to application Workers. Staging uses https://staging.tuturuuu.com and staging Supabase/public configuration. It does not activate production callbacks or processors. Mail’s production R2 S3 access continues to the existing tuturuuu-mail bucket; its staging descriptor names tuturuuu-mail-wave1-staging with separate credentials. Never repoint the existing tuturuuu-mail-email-routing ingestion Worker, signing identity, bucket, MX records or canonical webhook as part of frontend staging. Preserve optional SES credentials for legacy attachment reads before production admission. Apps staging has no canonical routes, APPS_CANONICAL_HOST_ENABLED=0 and noindex. Its separately built production profile sets the fence to 1 and declares its canonical Custom Domain. That configuration is qualified by original/restored workerd checks while publication remains held. Merely attaching the staging profile to the canonical host would return 503 and is forbidden.

One-time owner provisioning

  1. Retain the production hold. Create protected GitHub environments wave1-<app>-staging and wave1-<app>-production for Calendar, Mail and Apps. Production needs explicit release-owner approval. Use a least-privilege CLOUDFLARE_STAGING_API_TOKEN for staging only; this is the only deploy-token secret referenced by staging publication. Never store a production token under that name. The workflow never reads secrets.CLOUDFLARE_API_TOKEN or production token secrets. Only the staging token is mapped to Wrangler’s standard CLOUDFLARE_API_TOKEN process variable, after admission. Never copy credential values into source or evidence. Set CLOUDFLARE_STAGING_ACCOUNT_ID to the reviewed shared account identity. Missing or mismatched identity denies publication. Provision WAVE1_CONTROL_READ_TOKEN with repository Variables read permission for the fresh stop-fence observation. Do not assume actions: read supplies that permission; see GitHub’s variable API permissions. Both reusable workflows declare and explicitly forward only these two staging credentials plus the two public Supabase inputs; neither uses secret inheritance. Only the protected publisher step receives deploy/control process variables. Keep deploy/control values exclusively in the protected staging environment, restricted to main and owner approval; never give PR jobs a repository-level copy. The staging deploy token also needs D1 write access to the staging control database. GitHub job tokens and the Variables-read token must have no D1 authority.
  2. Set the actual CLOUDFLARE_WORKERS_SUBDOMAIN repository variable. Provision the three staging Workers with workers_dev=false, preview_urls=false, no routes, and an accepted initial version. The publisher intentionally refuses a missing Worker or missing rollback deployment instead of bootstrapping resources. Bootstrap through the owner’s reviewed infrastructure procedure with the identities and resource descriptors in wave1-native-profiles.json; do not use canonical configs.
  3. Provision Mail’s isolated staging R2 bucket and scoped S3 credentials according to that descriptor. Never reuse production storage/resource IDs or encryption/signing keys in staging. Grant only staging access to staging secrets. Confirm the existing staging central issuer/API and Supabase instance are admitted for disposable acceptance.
  4. Provision Worker secret names from each app’s secrets list in the profile file. Values stay in the provider. The workflow checks names through the API and never uploads secrets. Calendar additionally requires the registered Google and Microsoft callback URLs declared in the profile vars. Mail’s ingestion secret, connected-account encryption key and storage credentials preserve the owning environment’s identity. Optional names are listed separately; provider acceptance must identify which are required for the configured transports and old attachments.
  5. Supply GitHub build/publication secret names STAGING_SUPABASE_URL and STAGING_SUPABASE_PUBLISHABLE_KEY. Held production builds retain fixtures and read no production inputs. Only public Supabase inputs reach staging builds. Missing values allow source-only fixture artifacts, which cannot publish: the publisher rejects fixture URLs, different public-input fingerprints or a different account subdomain. Environment overrides must agree with the qualified artifact.
  6. After owner review, set the exact WAVE1_STAGING_PUBLICATION_ADMISSION record described below and repository variable WAVE1_STAGING_PUBLISH_ENABLED=true. Only that admitted main source and artifact can publish once; later sources default to denied. Retain the same qualified archive when resuming a failed publication job after updating admission. Turning it off is the durable stop fence for subsequent writes; the publisher reads it freshly immediately before its sole deployment. This cannot cancel a write already accepted by Cloudflare. No resources, settings, secrets or DNS were created by authoring these workflows.

Exact artifact and publication gates

wave1-native-app.yaml builds staging and held production profiles in exact-source CI. Each runs original workerd acceptance, Wrangler dry-run, seal, bounded archive, separate-job restore, repeated workerd acceptance/dry-run and final seal verification. Mail has its own original/restored native Worker checks. Calendar and Apps keep their existing deeper SSR/offline/RPC/browser gates; the new checks do not replace them. The native public /api/build-info endpoint reports bundled full SHA, app and environment before session middleware, accepts only GET/HEAD/OPTIONS, and is no-store. It exposes release metadata only; the shared product contract records scope and exceptions. Original/restored checks compare this identity, actual static asset bytes, anonymous private-page login handoff for Mail/Calendar, and Apps’ staging canonical refusal versus admitted production fence behavior. Runtime credentials are disposable and network egress is denied. This finite fixture is not hosted provider, signed-in browser, or complete SDK descendant-cleanup proof. CI step/job timeouts fail a stuck runtime rather than relaxing assertions. The publisher restores the same-run archive and rechecks runtime before credentials are introduced. It observes Cloudflare’s deployment/version APIs and captures the existing deployment’s weighted rollback versions, checks expected secret names, rechecks the fresh stop fence and seal, then invokes Wrangler once against the sealed config. The version tag is the complete artifact receipt digest and its message is the source SHA. The hosted active version/tag and disabled workers.dev/preview state must match before recording a GitHub deployment marker. Original/restored CI proves the HTTP build identity locally. Staging and production marker environments remain distinct.

Finite publication admission and uncertain outcomes

WAVE1_STAGING_PUBLISH_ENABLED=true alone authorizes zero uploads. The owner must also set the repository variable WAVE1_STAGING_PUBLICATION_ADMISSION to one exact app/source/artifact record after reviewing the qualified artifact receipt. All fields are required, unknown fields are refused, and the account is named by variable reference, never copied into the admission JSON:
This admits at most one upload for that artifact, with zero application retries. Later main pushes and other apps are denied until the owner explicitly replaces this record. Deleting the record or disabling the enable variable stops future uploads. Both are freshly read from GitHub before preflight and again immediately before upload; deletion, malformed data, denial or timeout fails closed. The earlier policy job checks source admission; the publisher additionally checks the exact restored digest. These controls stop publication, not invocations of an already serving Worker; the owner must separately disable serving to stop its runtime spend. Before enabling publication, the owner must provision a dedicated staging D1 control database and set WAVE1_STAGING_ATTEMPT_D1_ID. This is option (a): D1 SQL supports atomic comparison against the previous record, whereas KV visibility can lag, and repository Deployments remain writable/deletable by PR job tokens. No new GitHub App/bot credential or public control Worker is needed. The table is:
Seed one row per app (wave1-mail-staging, wave1-calendar-staging, wave1-apps-staging) through the owner’s separately reviewed infrastructure procedure. The record value is the output of exported signAttemptState(data, stagingToken) from scripts/ci/wave1-native-attempt.mjs, with data shaped as:
The token must come from private environment injection, never command arguments, source or logs. The signed envelope contains serialized data plus a domain-separated HMAC-SHA256, using only the staging deploy token as signing authority. Each entry binds the original GitHub run ID/attempt, full admission, SHA, digest, account, Worker and rollback versions to progress. Only a genuinely unused app may be seeded with empty history. Never reseed an existing app to retry: migrate/reconcile prior GitHub attempt records and provider versions first, preserving all consumed and uncertain identities. Token rotation requires owner verification and re-signing of the existing complete ledger, never resetting its history. The publisher only SELECTs and conditionally UPDATEs the existing app row; it never creates a missing row/table or inserts a replacement. Before any upload it persists signed pending state with a compare-and-swap and authenticates the readback. A lost write response remains consumed/uncertain on restart. Missing/deleted records, invalid signatures, wrong scopes, failed queries and concurrent changes block upload. An ordinary GitHub deployment/status, including a forged terminal or newer record, has no authority over this ledger. The ordinary success marker remains diagnostic. The exact owner admission remains a separate fresh GitHub Variables observation; a valid D1 ledger alone grants no upload. A definitive final fence/seal refusal records failed and consumes admission. Upload exceptions remain pending, and failures after a returned upload stay uploaded. Any preceding unresolved entry blocks a different artifact; terminal entries remain in the signed history, so consumption cannot be erased by renewing the admission. Reruns load this ledger and inspect the Worker versions API, including inactive versions. Each listed version is read by ID because the list response does not include artifact annotations. Exactly matching SHA plus digest is reconciled against active 100% deployment, version annotations and disabled workers.dev/preview URLs without uploading or activating anything. Public HTTP identity probing is deliberately absent while ingress is disabled; real workerd identity and behavior acceptance remains required on original/restored bytes in CI. An inactive match remains uploaded until the owner resolves activation under separate authority. A previous attempt with no matching version is blocked, even after failed status; absence is not proof that the original write did not happen. Preserve the original operation identity, inspect provider/audit evidence, and obtain a new explicitly reviewed artifact admission only after resolving an uncertain outcome. A lost Wrangler receipt does not permit a second upload. Reads have 15-second deadlines, 1 MiB response bounds and no application retry. The signed ledger is at most 512 KiB and fewer than 100 retained entries. A full ledger blocks publication and requires reviewed archival/reconciliation; it is never automatically truncated. Each SQL query uses the app primary key and at most one row; a normal success uses seven D1 queries (one read, three conditional updates and three readbacks), six other provider reads with empty version history, and four GitHub variable reads. Version history has one page of fewer than 100 entries; a full/additional page blocks, and each version needs one detail read. There is no application retry. The publication step has a five-minute watchdog; each single Wrangler invocation has a 180-second limit. Wrangler’s internal asset requests/retries are additional, bounded by that watchdog and the 50,000-member / 1 GiB artifact ceiling. Non-cancelling app/environment concurrency and SQL conditional updates protect consumption across runners. A new owner admission is required for another artifact. No recurring producer, automatic renewal or public control endpoint exists.

Serving cost and emergency stop

All staging profiles, including the prepared Meet frontend/realtime profiles, set workers_dev=false, preview_urls=false and routes=[]. These Wrangler controls remove public ingress rather than charging for a secret-header check on each request. No public identity exception, shared-secret gate, paid Access product or rate-limit product is required. The publisher also observes disabled subdomain and preview serving before marking an upload accepted. Do not attach a Custom Domain, zone Route, external service caller, cron or queue to these identities. The owner must verify that existing hosted identities have no such attachment before enabling publication; source configuration alone is not hosted-topology evidence. The disabled workers.dev URLs retained in app vars are identity placeholders, not usable callback/media URLs. With no public ingress or scheduled/queue producers, arbitrary Internet traffic cannot invoke these Workers; finite publishing and owner-controlled private probes are the remaining work. This does not cap shared-account spend or an independently added service binding/producer. Browser/provider callback/media acceptance needs a separately approved private access plan; do not re-enable workers.dev to make it pass. For emergency stop, first set WAVE1_STAGING_PUBLISH_ENABLED=false and remove the exact admission to fence future uploads. In Cloudflare, disable workers.dev/preview serving and remove any unexpected Routes/Custom Domains. If execution continues or its ingress cannot be established, delete only the named staging Worker in the Workers dashboard (Settings / Delete) under release-owner custody; include both Meet staging Workers if used, never a production identity. Verify its disabled/deleted state and absence of further executions. Preserve the D1 ledger and provider/audit receipts: deleting a Worker does not restore consumed admission or permit replay. No deletion or other provider mutation is performed by these source tests.

Meet off-canonical profile

apps/meet/void.staging.config.ts is selected only by MEET_DEPLOYMENT_PROFILE=staging; ordinary source CI retains its existing default. It uses tuturuuu-meet-wave1-staging, no routes, isolated MEET_LIVE namespaces, self-reference to that same staging Worker and bucket tuturuuu-meet-wave1-staging-cache. Realtime’s separate apps/meet-realtime/wrangler.staging.jsonc uses tuturuuu-meet-realtime-wave1-staging, no canonical route and its own local DO namespaces/migration history. Its secret names retain the protocol contract but must refer to disposable staging SFU/TURN credentials, never production resources. The staging frontend omits the account-level Images binding; shared production Images access is not admitted. The owner must provision those staging identities, the isolated cache bucket, self-service binding, stage Supabase/central issuer and disposable SFU/TURN app/key before CI building/qualifying/publishing this profile. Meet staging publication is not automated by these three frontend publishers. This is a defined profile, not a deployed or workerd-qualified Meet staging claim and not a no-staging exception. Before any canonical Meet release, retain original/restored gates, qualify this profile off canonical traffic and exercise hosted two-participant media/reconnect. Meet’s production workflow captures rollback versions for both frontend and realtime.

Remaining cutover and rollback

Require all applicable exact-main tests/type/lint/native builds, original/restored artifacts and shared-supplier checks. After a separately approved private ingress plan, collect real signed-in EN/VI session, refresh/MFA/logout/privacy and app-specific hosted evidence: Calendar provider/ recurrence/offline/Tasks; Mail provider sends with original-operation readback, webhook/attachments; Apps catalogue anchors/redirects/query/RPC; Meet real media/DO/ SFU/TURN. workers.dev does not itself prove shared canonical-domain cookie continuity. Before a later cutover PR, capture authoritative DNS/proxy/Route/Custom Domain IDs, immutable Vercel fallbacks, current Worker versions, rollback operator, explicit promotion range and compatible secrets/state. Production configs currently declare Custom Domains: their eventual publication is a traffic switch, not just code upload. Do not enable WAVE1_PRODUCTION_PUBLISH_ENABLED, remove holds or attach routes until that later release is expressly approved. Retain Vercel framework/domain settings. Rollback uses the retained publication receipt’s exact prior version(s), not a new build. Under release custody, restore frontend code and Meet realtime when changed. For Vercel exits, restoring code alone does not restore traffic: reverse the exact captured Route/Custom Domain and DNS/proxy topology to the saved deployment. Observe Apps for 14 days and Meet/Mail/provider-critical Calendar for 30 days and relevant business cycles. Provider side effects and persistent data do not roll back with code.

Source-only validation

Run ttr resources run -- node --test scripts/ci/wave1-native-publishers.test.js and scripts/ci/wave1-native-publication.test.js for resource/profile isolation, exact bytes and relocation, unsafe archive rejection, production holds, source/ref admission, fresh-stop failure, one-write bounds, unknown outcome retention, rollback shape, identical-version identity and workflow gate order. The real main publish entrypoint runs with sealed fixtures and bounded counting GitHub/Cloudflare/CLI transports. Tests cover post-preflight disable/delete/deny/ timeout/admission changes, fresh-runner restart, inactive versions and lost receipts. wave1-native-attempt.test.js executes the conditional SQL against SQLite and covers concurrent consumers, deletion, authentic recovery and scope binding. The publisher fixtures reject forged terminal/newest records, signatures made with a PR token, missing/deleted state and deletion during consumption, always with zero uploads. Workflow contracts assert exact staging credential forwarding at every call boundary. A copied entrypoint with its final fresh check removed must fail the same zero-upload assertion; it reaches exactly one forbidden fake upload. These are source regression proofs, not hosted token-scope, emitted app or deployment evidence. Real app builds and workerd gates run only in CI. No local build or production workflow dispatch is part of this validation.

Held validation and staging admission

The shared fail-closed production-publication-policy.ts decision also applies to native publisher entrypoints, alongside the profile hold. Production profile builds use disposable public inputs and no production credentials. Staging build inputs are read only for a trusted main push or dispatch; PRs use fixture inputs. The reusable publisher itself admits only staging on those trusted events, even if a caller tries to pass a production profile. Publication remains non-cancelling. Regression evidence lives in production-publication-policy.test.js, production-publisher-secrets.test.js and wave1-native-publishers.test.js. Meet’s app TypeScript project includes both Void configs because the default config imports the staging config; keep that imported tooling file in the same project.

Mail Node builtin startup compatibility

Both Mail profiles preserve the source compatibility date 2026-10-19 and flags nodejs_compat and global_fetch_strictly_public. They share one Vite configuration; profile derivation changes routing and resource identity after bundling. A staging startup failure in a failed-recipients chunk exposed a CommonJS require("path") through sanitizeMailHtml -> sanitize-html -> PostCSS, not a difference in those compatibility flags. Void 1.1.12 skips its builtin require adapter for its default SQLite dialect, including applications with database inference disabled. Node compatibility does not define a global CommonJS require inside an emitted ESM chunk. The Worker entry also includes iconv-lite through inbound group authentication -> mailauth/lib/dkim/verify -> mailauth/lib/tools -> libmime/lib/charset. Its streaming API initialization uses a bare CommonJS require("stream"). The conservative emitted-byte guard also matches that spelling in its fallback diagnostic. In a Linux preimage reproduction, the remaining emitted match was the diagnostic example rather than an executable call (the browser mapping disables its optional stream import). The workerd-only plugin adapts the stream import to native node:stream and rewrites only that diagnostic to describe passing the module without a CommonJS call example. The emitted-byte guard remains unchanged; tests execute the transformed installed iconv-lite entry and check charset decoding and stream construction. The same graph also uses node:buffer throughout DKIM and libmime, plus node:dns, node:crypto and node:https in mailauth tools. Charset decoding reaches string_decoder; bundled stream/deprecation helpers and document preview reach util and events. Adapt these imports through the same ESM protocol. Mail’s build plugin resolves CommonJS buffer, crypto, dns, events, fs, https, path, stream, string_decoder, url and util imports in the workerd server build to native ESM adapters, matching Void’s adapter protocol. Client, development, ESM and unrelated builtin imports keep their existing resolution. Both profiles use this same plugin. Before original or restored runtime acceptance, the artifact gate rejects emitted server assets containing static CommonJS Node builtin calls, including Rolldown’s __require helper. The conservative byte check covers bare and node:-prefixed names with quoted or backtick strings. An additional AST check follows the exported Rolldown require helper into renamed imports, so minified calls such as t(`node:buffer`) also fail before startup. ESM imports remain allowed. Scan every server JavaScript module, including .mjs and .cjs. Unit and profile tests cover the adapter, the unchanged compatibility settings and both profile rejection paths. Exact-head CI must still prove original/restored emitted Worker startup; config tests are not emitted-byte proof. Email-service memory rate-limit and credential-cache expiry cleanup runs lazily during use, at most once per minute. Importing those modules must not start timers: workerd forbids global-scope setInterval. TTL checks remain authoritative on every read; the worker-import regression covers timer-free import and expiry. The unadapted dependency graph is not specific to the staging profile; production builds share it.