Skip to main content
App coordination tokens (ttr_app_...), session-assurance transfers, signed access tokens (sat1.), email unsubscribe tokens and inventory simulated-order tokens are HMAC-signed with a server secret. Every runtime that verifies or mints them must resolve the same secret chain, so a new secret is rolled out in stages. The first value of the chain signs; every value is accepted.

Env names

With none of the three new names set, behavior is identical to before. The purpose-bound tokens (getServerTokenSecretCandidates, email unsubscribe, inventory simulated order) resolve their own dedicated names first, then follow the same three rules, through resolveServerTokenSecrets in packages/utils/src/app-coordination-token.ts. Mint the next value as 32 or more random bytes (for example 64 hex characters) in the secret manager. Never paste it into a commit, PR or chat. SUPABASE_SECRET_KEY itself stays on every Worker: the database client still needs it. Only its use as a token-signing fallback is retired.

Order

Each step is complete only when every runtime in the table below has the new deployment and a smoke passes. Run steps in order; wait the stated time before the next.
  1. Deploy the code everywhere with no new env set. Verify that sign and verify behave as before (cross-app sign-in, app-token exchange).
  2. Add APP_COORDINATION_TOKEN_NEXT_SECRET (same value) to Web and every Worker below, as a secret, then redeploy or restart each. Tokens still sign with the current signer. Smoke: sign in from Web into two satellites. An invalid or short value surfaces immediately as a thrown error, not as a silent downgrade.
  3. Promote: set APP_COORDINATION_TOKEN_SIGNER=next on every runtime. Roll Web last. Until Web flips, satellites already sign with next, and Web still verifies them because it holds the slot. Tokens issued before the flip keep verifying through the chain.
  4. Wait for the longest token lifetime to elapse. App coordination tokens default to 8 hours (DEFAULT_EXPIRES_IN_SECONDS in packages/utils/src/app-coordination-token.ts; callers may set less), signed access tokens take a TTL per call, and simulated-order tokens last 24 hours. Email unsubscribe tokens carry no expiry: links already sent stay valid only while the secret that signed them remains in the chain.
  5. Retire the fallbacks: set APP_COORDINATION_TOKEN_DISABLE_SUPABASE_FALLBACK=1 everywhere. Make sure TUTURUUU_APP_COORDINATION_SECRET (or the slot) is present first, because an empty list fails closed. If that secret equals the old Supabase signer value, existing unsubscribe links keep verifying.
  6. Hold: leave APP_COORDINATION_TOKEN_NEXT_SECRET and APP_COORDINATION_TOKEN_SIGNER=next set. Moving the promoted value into TUTURUUU_APP_COORDINATION_SECRET and dropping the old one is a separate, later change that retires every token the old value signed, including unsent-yet-valid unsubscribe links.

Rollback

If a deployment is partial after step 3 (some runtimes without the slot), flip the signer back to current first and redeploy the stragglers before trying again.

Runtimes that read the secret

Web (apps/web, Next.js) and every satellite that verifies tokens through @tuturuuu/auth, @tuturuuu/utils or @tuturuuu/inventory-core. Native Start Workers whose packet or startup check names the secret (see each packet for its Worker name, vars and secrets): ai, chat, cms, drive, external, finance, forms, git, hive, infrastructure, inventory, learn, mind, nova, pay, rewise, shortener, storefront, tasks, teach, track, workspace. Other packets that name it: finance-pay, mastermind, playground, tools, wave 1, web Docker and TanStack/Rust local deploy (inactive runtimes). Apps with env.example entries for the secret: calendar, cms, drive, finance, forms, hive, inventory, learn, mind, nova, pay, rewise, shortener, tasks, teach, track, workspace, plus web. Worker startup checks require TUTURUUU_APP_COORDINATION_SECRET (tasks, tools, forms, playground, storefront and others, see their cloudflare/ folder). Those checks keep requiring it; the new names are optional and are read directly from the runtime environment. The paused Rust backend (apps/backend) verifies app tokens with its own implementation and does not read the new names. It must adopt the same rules before it is resumed; do not roll out step 3 while it serves live traffic.

Verification

  • Unit: packages/utils/src/app-coordination-token.test.ts covers default behavior, accept-only, a verifier holding only the slot, promotion with old tokens, fallback removal, the empty-list failure and the short secret.
  • Per runtime, before moving on: one cross-app sign-in, one app-token exchange, one signed-access-token flow, and confirm there are no must be at least 32 characters or requires errors in Worker logs.