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 intuturuuu.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. ThestagingAdmission 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
- Retain the production hold. Create protected GitHub environments
wave1-<app>-stagingandwave1-<app>-productionfor Calendar, Mail and Apps. Production needs explicit release-owner approval. Use a least-privilegeCLOUDFLARE_STAGING_API_TOKENfor staging only; this is the only deploy-token secret referenced by staging publication. Never store a production token under that name. The workflow never readssecrets.CLOUDFLARE_API_TOKENor production token secrets. Only the staging token is mapped to Wrangler’s standardCLOUDFLARE_API_TOKENprocess variable, after admission. Never copy credential values into source or evidence. SetCLOUDFLARE_STAGING_ACCOUNT_IDto the reviewed shared account identity. Missing or mismatched identity denies publication. ProvisionWAVE1_CONTROL_READ_TOKENwith repository Variables read permission for the fresh stop-fence observation. Do not assumeactions: readsupplies 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. - Set the actual
CLOUDFLARE_WORKERS_SUBDOMAINrepository variable. Provision the three staging Workers withworkers_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 inwave1-native-profiles.json; do not use canonical configs. - 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.
- Provision Worker secret names from each app’s
secretslist 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. - Supply GitHub build/publication secret names
STAGING_SUPABASE_URLandSTAGING_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. - After owner review, set the exact
WAVE1_STAGING_PUBLICATION_ADMISSIONrecord described below and repository variableWAVE1_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:
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:
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:
Serving cost and emergency stop
All staging profiles, including the prepared Meet frontend/realtime profiles, setworkers_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 enableWAVE1_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
Runttr 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-closedproduction-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 date2026-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.