> ## 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.

# Cloudflare backend ownership

> New schedules, queues and heavy background work run on Cloudflare; a shrink-only guard holds the legacy owners.

## Decision

New crons, queues, schedulers and heavy or long-running backend work belong on
**Cloudflare**: Workers Cron Triggers, Queues, Workflows and Durable Objects. Do
not add them as Vercel Cron entries, Next.js `after()` continuations, new
`/api/cron` processor routes, pg\_cron jobs, Supabase Edge Functions or Trigger.dev
tasks. This applies to all new services and to every legacy owner as it migrates.

Supabase remains the system of record. The rule moves **compute and scheduling**,
not data: Workers keep using the existing Supabase tables, RPCs, claims and RLS
through injected credentials. It does not authorize a database migration, a new
datastore or a production cutover.

Every Cloudflare job must meet the
[runaway-work and cost acceptance](https://docs.tuturuuu.com/build/development-tools/oxc-cloudflare-adoption.md#runaway-work-and-cost-acceptance)
rules: bounded work and retries, idempotent progress, no rescheduling after a
terminal state, and a tested durable stop fence. Usage alerts and CPU limits are
not spending caps.

## Choose the Cloudflare primitive

| Work shape | Primitive | Notes |
| - | - | - |
| Recurring trigger for an existing authenticated processor | Cron Trigger in [Cron Control](https://docs.tuturuuu.com/build/devops/cloudflare-cron-control.md) | One controller; per-job default-off flag, slot claim and stop fence |
| Recurring work that can run in a Worker | Cron Control job with a Worker processor | Call existing RPCs/claims directly; bound batches per tick |
| Fan-out, retry isolation or bursts | Queue consumer | Align `max_retries`/DLQ with the durable attempt budget |
| Multi-step or long-running work with checkpoints | Workflow | Persist step results; finite attempts and deadlines |
| Per-entity coordination, leases or ordered state | Durable Object | No self-rearming alarm chains; check the stop fence before rescheduling |
| Best-effort follow-up of a request | `ctx.waitUntil` on the Worker host | Only for bounded, losable work; durable business work needs a queue |

The [background work inventory](https://docs.tuturuuu.com/build/devops/background-work-inventory.md) maps
every current owner to its target and cutover class. Reuse Cron Control for
recurring jobs instead of adding another controller for the same work. Preserve current cadence when moving a schedule; changing it needs
job-specific evidence and a product decision.

## Shrink-only guard

`scripts/ci/cloudflare-backend-ownership.test.js` runs in the root script suite
and fails when a new owner appears outside Cloudflare:

| Kind | Detected as |
| - | - |
| `vercel-cron` | A `crons` entry in any `vercel.json`, keyed by project, path and schedule |
| `cron-catalog` | An enabled job in a `cron.config.json` catalog |
| `pg-cron` | A live pg\_cron job after replaying every migration's `cron.schedule`/`cron.unschedule` in order |
| `edge-function` | Any file under `supabase/functions/` or a `[functions.*]` section in `supabase/config.toml` |
| `trigger-dev` | `trigger.config.*`, an `@trigger.dev/*` dependency or a source import |
| `next-after` | Active app or package source importing `after` from `next/server` |
| `next-cron-route` | A Next.js `src/app/api/cron/**/route.*` processor |

A migration after the rule's cutoff (`20261023000000`) may not call
`cron.schedule`, even for an allowlisted name, except to re-tune a justified
retained Postgres job. A changed cadence is a new owner. Tests, paused runtimes
and the guard's own files are excluded.

`scripts/ci/cloudflare-backend-ownership-allowlist.js` records each legacy owner
with its owner, Cloudflare target and plan. The test pins the adopted baseline;
entries may be removed but never added. Removing a legacy definition without
removing its entry fails as stale, so the list shrinks with each migration.

```bash theme={null}
node --test scripts/ci/cloudflare-backend-ownership.test.js
node scripts/ci/cloudflare-backend-ownership.js   # prints violations, exits 1
```

To retire an entry: move the schedule or processor to Cloudflare behind its
default-off flag, complete the cutover, delete the legacy definition in its own
reviewed change, then delete the allowlist entry. Never run both owners unfenced.

## Retained Postgres exceptions

pg\_cron may keep a job only when it is **pure in-database maintenance**: a single
SQL statement against Postgres tables with no HTTP, storage or provider side
effects, where a Cloudflare trigger would add a Worker invocation and a service-key
round trip to run the same SQL in the same database. Each retained job carries its
justification in the allowlist; jobs using `pg_net` are not exceptions. New
exceptions are not admitted through the guard: schedule new work from Cloudflare.

## Regression evidence

The guard test covers the repository scan, allowlist metadata, the frozen baseline,
new and re-cadenced Vercel crons, catalog enablement, stale entries, post-cutoff
migrations, unschedule replay, SQL comments/quoting/PL/pgSQL parsing, Edge Function
source and configuration, Trigger.dev configuration/dependencies/imports, Next
`after()` imports and new cron routes. It does not certify hosted schedules,
processor behavior or spend; those remain per-job cutover evidence.


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