pithy media

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

FlagDefaultPurpose
--api-token <token>CLOUDFLARE_API_TOKENThe 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_CREDENTIALSR2 S3 access key id the Worker presigns uploads and downloads with
--r2-secret-access-key <key>R2_CREDENTIALSThe secret half of the pair. Passing one without the other is refused
--r2-api-token <token>CLOUDFLARE_API_TOKENThe token carried beside the R2 key pair so the object store can prove bucket access
--jsonfalseMachine-readable output

deprovision

FlagDefaultPurpose
--storagefalseIrreversible. Also delete the R2 bucket with every object in it, and the MEDIA KV namespace
--r2-access-key-id <id>R2_CREDENTIALSRequired with --storage: a bucket must be emptied over the S3 protocol before R2 will delete it
--r2-secret-access-key <key>R2_CREDENTIALSThe secret half of the pair
--jsonfalseMachine-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:

  1. Preflight. Verify the account can host a Workflow at all — most importantly a registered workers.dev subdomain.
  2. Resources. Create or reuse each environment’s R2 bucket, and its MEDIA KV namespace when the capability’s recordStore is kv. In D1 record mode no namespace is created and the binding is dropped rather than pointed at a namespace that never existed.
  3. 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’s ObjectStore, which media presigns through and whose key pair media never sees.
  4. 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

KeyTypeMeaning
command"media provision"The subcommand that produced this line
environmentsarrayOne entry per environment provisioned, in managed-environment order
environments[].envstringThe environment this entry describes
environments[].bucketNamestringThe R2 bucket media objects live in for this environment
environments[].kvNamespaceIdstring or nullThe MEDIA KV namespace id, or null when records live in D1 and the binding is dropped

media deprovision

KeyTypeMeaning
command"media deprovision"The subcommand that produced this line
storageDeletedbooleanWhether --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
ESC