Stand up what pithy add media only wired: the per-environment R2 bucket, the MEDIA KV namespace when records live in KV, the two credential secrets, and the deployed media worker that hosts the enrichment Workflows.
Synopsis
pithy media provision [--api-token <token>] [--r2-access-key-id <id>] [--r2-secret-access-key <key>] [--r2-api-token <token>] [--json]
pithy media deprovision [--storage] [--r2-access-key-id <id>] [--r2-secret-access-key <key>] [--json]This is the provisioning half of the media capability, and provisioning Media walks the same run with the account steps around it.
Both subcommands reach a Cloudflare account. There is no local mode and no --env flag: provisioning spans every managed environment in one run.
Flags
provision
| Flag | Default | Purpose |
|---|---|---|
--api-token <token> | CLOUDFLARE_API_TOKEN | The token the media Worker mints Images and Stream direct-upload URLs with. The default is a broad token; supply a scoped one for production |
--r2-access-key-id <id> | R2_CREDENTIALS | R2 S3 access key id the Worker presigns uploads and downloads with |
--r2-secret-access-key <key> | R2_CREDENTIALS | The secret half of the pair. Passing one without the other is refused |
--r2-api-token <token> | CLOUDFLARE_API_TOKEN | The token carried beside the R2 key pair so the object store can prove bucket access |
--json | false | Machine-readable output |
deprovision
| Flag | Default | Purpose |
|---|---|---|
--storage | false | Irreversible. Also delete the R2 bucket with every object in it, and the MEDIA KV namespace |
--r2-access-key-id <id> | R2_CREDENTIALS | Required with --storage: a bucket must be emptied over the S3 protocol before R2 will delete it |
--r2-secret-access-key <key> | R2_CREDENTIALS | The secret half of the pair |
--json | false | Machine-readable output |
What it does
The credentials are supplied, not minted. Cloudflare exposes no API for creating an R2 S3 access-key pair, and the permission catalog carries no Images or Stream keys. The pair and the scoped token come from the flags or from R2_CREDENTIALS in the account config, and are written into the secret as given.
provision runs in phases across every managed environment rather than one environment end to end, so a failure in production stops the run before staging is deployed against a half-provisioned account:
- Preflight. Verify the account can host a Workflow at all — most importantly a registered
workers.devsubdomain. - Resources. Create or reuse each environment’s R2 bucket, and its
MEDIAKV namespace when the capability’srecordStoreiskv. In D1 record mode no namespace is created and the binding is dropped rather than pointed at a namespace that never existed. - Credentials. Write each environment’s two secrets. Two, because there are two owners: one is media’s Images and Stream token, and the other belongs to
pithy storage’sObjectStore, which media presigns through and whose key pair media never sees. - Workers. Deploy the prebuilt media worker per environment, wired to the resources the secrets already name.
Each environment’s deploy needs two things resolved first, and each missing one is refused rather than deployed around: the app DB id from that environment’s stanza, and the environment’s secrets database, which pithy secrets provision creates.
deprovision removes the media workers. The bucket, its objects and the namespace stay unless --storage is passed. With --storage, the key pair is resolved before the first worker comes down: discovering it missing at the bucket step would leave the workers gone and the bucket standing.
--json
media provision
| Key | Type | Meaning |
|---|---|---|
command | "media provision" | The subcommand that produced this line |
environments | array | One entry per environment provisioned, in managed-environment order |
environments[].env | string | The environment this entry describes |
environments[].bucketName | string | The R2 bucket media objects live in for this environment |
environments[].kvNamespaceId | string or null | The MEDIA KV namespace id, or null when records live in D1 and the binding is dropped |
media deprovision
| Key | Type | Meaning |
|---|---|---|
command | "media deprovision" | The subcommand that produced this line |
storageDeleted | boolean | Whether --storage was passed, and therefore whether the bucket, its objects and the namespace were deleted |
Errors
The capability is not configured. No Worker’s pithy.config.ts composes media.
The capability will not load. Distinct from the above, and classified rather than assumed. The package missing answers not installed; the package present with one of its own imports unresolved answers could not be loaded and tells you to install the project’s dependencies. A package that resolves and throws answers installed and will not load.
Credentials are missing.
No Secrets Store id. The media worker decrypts its credentials from it.
Half an R2 key pair, or none at all.
The project has no name. Every resource name this run creates, and deprovision’s ability to find them again, derives from it.
The environment is not in wrangler.jsonc, or its DB binding has no id.
The secrets database does not exist. Run pithy secrets provision first.
Examples
$ pithy media provision --api-token "$MEDIA_TOKEN"
staging: bucket acme-staging-media and its MEDIA namespace ready, worker deployed.
prod: bucket acme-prod-media and its MEDIA namespace ready, worker deployed.
Done.$ pithy media deprovision
Media workers removed.
Done.pithy media deprovision --storage --json