Skip to content

A temporary Cloudflare account cannot host a Mailda Node

id: temporary-account-provisioning
kind: platform-limit
measured_on: 2026-09-03
stale_when: >
Cloudflare adds R2, Workflows or Email Sending to the products a temporary preview account supports;
temporary accounts stop being created on the Workers Free plan, which ADR 25 requires to be Paid; the
60-minute claim window changes; `wrangler deploy --temporary` begins validating bindings before creating
an account rather than failing part way through provisioning; or this Node stops declaring an `r2_buckets`
binding, which is the binding the refusal below lands on
values:
temporary.supports_mailda_node: 0
temporary.claim_window_minutes: 60
temporary.bindings_provisioned_before_refusal: 1

Issue #108 charts an onboarding path in which Mailda provisions an account for a customer who has none, deploys into it, and hands over a claim URL. This is the measurement of whether that path exists for this product. It does not.

Run against this repository’s own wrangler.jsonc, unauthenticated, with wrangler’s configuration directory redirected to a temporary path so the operator’s own session was untouched.

Continuing means you accept Cloudflare's Terms of Service and Privacy Policy.
Solving proof-of-work challenge…
Temporary account ready:
Account: Rectangular Clipper (created)
Claim within: 60 minutes
Claim URL: https://dash.cloudflare.com/claim-preview?claimToken=…
The following bindings need to be provisioned:
env.SENDING_EVENTS Queue
env.CATALOG D1 Database
env.EVIDENCE R2 Bucket
Provisioning CATALOG (D1 Database)...
✨ CATALOG provisioned 🎉
✘ [ERROR] A request to the Cloudflare API (/accounts/…/r2/buckets) failed.
Authentication error [code: 10000]

Exit 1. The account was created and one binding was provisioned before the refusal.

Three things this measurement establishes that the product list does not

Section titled “Three things this measurement establishes that the product list does not”

Cloudflare documents the supported products as “Workers, Workers Static Assets, Workers KV, D1, Durable Objects, Hyperdrive, Queues, and SSL/TLS certificates”. Reading that list tells you R2 is absent. Running it tells you three further things, and each changes how an implementation would go wrong.

1. The refusal is an authentication error, not a capability error. Authentication error [code: 10000] is what a wrong token looks like. Nothing in the message says “temporary accounts do not support R2”, so an implementer reads it as a credential problem and goes looking for a scope to add. There is no scope to add.

2. It fails part way through, and the failure is not idempotent. D1 was created first. Wrangler caches and reuses the temporary account while its credentials hold, and auto-provisioning never adopts an existing resource, so the second attempt fails differently:

✘ [ERROR] You have reached the maximum number of D1 databases for your account.
On the Workers Free plan? Upgrade to create more:

Attempt one blames authentication on R2. Attempt two blames a plan limit on D1 and offers an upgrade link. Neither names the actual cause, and the second is actively misleading. It points at billing for a problem that is a product-support gap.

3. The account is on the Workers Free plan, which attempt two’s message states outright. ADR 25 requires Workers Paid. So even if R2, Workflows and Email Sending were added to the supported list tomorrow, a temporary account would still not satisfy this Node’s own plan requirement.

Which of this Node’s bindings the path can serve

Section titled “Which of this Node’s bindings the path can serve”

wrangler.jsonc declares six binding kinds. Three are unsupported, and the deploy only got as far as naming the first three because it refused before reaching the rest.

bindingtemporary account
d1_databasessupported; provisioned in this run
durable_objectssupported per the product list; not reached
queuessupported per the product list; not reached
r2_bucketsrefused, measured
workflowsabsent from the product list; not reached
send_emailabsent from the product list; not reached

EVIDENCE is not optional: it is where every message body lives. There is no reduced Node that fits, and no useful preview of one either. Without send_email and Email Routing a preview could neither receive nor send mail, which is the whole product.

The claim window collides with ADR 28, and that survives any product-list change

Section titled “The claim window collides with ADR 28, and that survives any product-list change”

“If the user does not complete the claim, Cloudflare deletes the account and its resources.” Sixty minutes.

ADR 28 puts both root keys in Durable Object storage, generated on first use, and #92’s drill measured that Durable Object storage is not carried by a D1 export, which is why ADR 29’s escrow exists at all. ADR 29’s ten recovery codes are shown when the Node is claimed.

So a Node claimed inside a temporary account that then expires unclaimed leaves the customer holding ten recovery codes for a vault that no longer exists: the escrow’s premise inverted, silently, and discovered only by somebody attempting to recover. Two claims in two systems need an enforced order, and nothing enforces one.

What was handled for us, and what a REST orchestrator would owe

Section titled “What was handled for us, and what a REST orchestrator would owe”

Two costs are invisible when wrangler does it and land on any backend that does not:

  • Terms acceptance is implicit in continuing. “Continuing means you accept Cloudflare’s Terms of Service and Privacy Policy.” A platform accepting Cloudflare’s terms on a customer’s behalf is a legal question, not an integration detail, and #108 already requires that acceptance be recorded “only where Cloudflare permits an orchestrator to do so”.
  • A proof-of-work challenge is solved before an account is created. Wrangler does it; the documentation states a REST integration must submit a solution itself.

And the claim URL is a bearer credential. Anyone holding it can claim the account. This run’s URL and the temporary API token were written to disk by wrangler and deleted immediately afterwards; neither belongs in a log, an analytics event or a support transcript.

Nothing about the existing_oauth path, which is the one #108 calls first to ship and which this measurement leaves untouched.

Nothing about a Worker that does not need R2. This is a measurement about this Node’s bindings, not a general claim about the temporary-account path, which works as documented for the products it lists.

The residue: one temporary account named Rectangular Clipper holding an empty D1, unclaimed, which Cloudflare deletes within the hour.