What pithy add media did, and what pithy media provision does
pithy add media writes bindings and touches no Cloudflare account. pithy media provision stands up what those bindings point at.
| Resource | When |
|---|---|
| An R2 bucket | Always |
A MEDIA KV namespace | Only when records live in KV |
media-storage-credentials | Always |
media-r2-credentials | Always |
| The media Worker | Always — it hosts the four enrichment Workflows |
All of it per environment, alongside pithy provision for the resources every capability needs. Which backends are stood up depends on the type map.
Two secrets because media reads one of them
media-storage-credentials is the Images and Stream token media mints direct-upload URLs with. Media’s own.
media-r2-credentials is the R2 key pair and bucket, and it is read by storage’s object store. Media declares the name and never opens the values.
The one thing you supply
Every step is idempotent
Find-then-create for resources, create-then-update for the secrets, and a deploy that overwrites. Re-running provisioning against an already-provisioned account is a no-op.
The live Cloudflare and wrangler steps sit behind a seam, so the orchestration — order, idempotency, the per-environment fan-out — is unit-tested against a fake that records its call order, with no account involved.
Three namespaces, three limits, one facade
Media names the same capability three times: an R2 bucket, a KV namespace, and a host Worker. Their caps are 63, 512 and 63.
Every name goes through the naming facade, and each resource asks for its own kind:
That is what stops one number standing in for all three. A KV title truncated to an R2 bucket’s limit is a name that works and is quietly wrong; a bucket name allowed a KV title’s length is a create that fails at the API. Asking for the right kind gets the right rule.
It is also what refuses production here rather than quietly provisioning a fourth environment under a name nobody declared.
The project is validated once and the environment once, in a single call, before any of the three ask for anything.
The per-environment posture
acme-staging-media and acme-prod-media are different buckets, and the media Worker is deployed per environment too.
A shared bucket would put staging’s writes in prod’s reads and let a staging teardown delete prod’s media. The project segment carries ownership, because R2’s namespace is flat and account-wide and provisioning reuses what it finds by name — so without it a second Pithy project in the account adopts this one’s bucket.