Cloudflare Secrets Store provider
The Cloudflare provider publishes declared values to an account-level Cloudflare Secrets Store through the Cloudflare REST API.
At a glance
Section titled “At a glance”| Provider | cloudflare (0.20+) |
| URI | cloudflare://STORE_ID[?account_id=ACCOUNT_ID][&OPTIONS] |
| Access | Write, delete, and discover names; plaintext values cannot be read back |
| Best for | Publishing secrets to Workers and other Cloudflare services from a separate source of truth |
| Authentication | API token or credentials from wrangler auth token --json |
| Availability | SecretSpec 0.20+; included in official and default builds (cloudflare feature for custom minimal builds) |
| Default storage | Account secret named {key} in the selected store |
Quick start
Section titled “Quick start”Find the account ID and Secrets Store ID in the Cloudflare dashboard or with Wrangler, then authenticate and configure an alias:
$ wrangler login$ wrangler secrets-store store list --remote[providers]cloudflare_prod = "cloudflare://0123456789abcdef0123456789abcdef?account_id=abcdef0123456789abcdef0123456789&auth=wrangler"
[profiles.production]DATABASE_URL = { description = "Production database URL" }# Publish or replace the account secret$ secretspec set DATABASE_URL --profile production --provider cloudflare_prod
# Remove it$ secretspec delete DATABASE_URL --profile production --provider cloudflare_prodCloudflare never returns plaintext through its management API, so
secretspec get, check, and run cannot resolve a value from this provider.
Keep the authoritative value in a readable provider and select
cloudflare_prod explicitly when publishing it.
Prerequisites (0.20+)
Section titled “Prerequisites (0.20+)”- SecretSpec 0.20 or newer
- A Cloudflare account with a Secrets Store
- Account Secrets Store Write permission for publishing and deletion
- The account ID and Secrets Store ID
The official SecretSpec CLI includes this provider. Custom minimal Rust builds
enable it with --features cloudflare.
Wrangler authentication (0.20+)
Section titled “Wrangler authentication (0.20+)”With auth=wrangler, SecretSpec runs:
$ wrangler auth token --jsonWrangler can return an API token, a refreshed OAuth token from wrangler login, or legacy API-key/email credentials. SecretSpec uses the returned
credential only in HTTPS request headers. It never passes the account secret
value to Wrangler.
Wrangler supports named authentication profiles:
cloudflare://STORE_ID?account_id=ACCOUNT_ID&auth=wrangler&wrangler_profile=productionIf the executable has another name or location, set
SECRETSPEC_WRANGLER_PATH. SecretSpec never invokes npx automatically.
Authentication with provider credentials (0.20+)
Section titled “Authentication with provider credentials (0.20+)”For CI or a machine identity, declare the api_token
provider credential:
[providers]bootstrap = "keyring://"
[providers.cloudflare_prod]uri = "cloudflare://0123456789abcdef0123456789abcdef?account_id=abcdef0123456789abcdef0123456789&auth=token"credentials = { api_token = "bootstrap" }Store it once:
$ secretspec config provider login cloudflare_prodEnter api_token for provider 'cloudflare_prod' (source: bootstrap): ****Use a scoped user or account API token with Secrets Store Write on only the required account. Do not use the full-access Global API Key for new setups.
Environment fallback (0.20+)
Section titled “Environment fallback (0.20+)”CLOUDFLARE_API_TOKEN supplies the api_token credential when no explicit
provider credential exists. CLOUDFLARE_ACCOUNT_ID supplies the account ID
when the URI omits account_id.
The default auth=auto uses the provider credential or
CLOUDFLARE_API_TOKEN first, then falls back to Wrangler. Use auth=token to
require a token or auth=wrangler to require Wrangler credentials.
Provider credentials
Section titled “Provider credentials”| Credential | Environment fallback | Available since |
|---|---|---|
api_token | CLOUDFLARE_API_TOKEN | 0.20+ |
See the complete provider credential reference for all supported providers and environment fallbacks.
Configuration
Section titled “Configuration”URI format (0.20+)
Section titled “URI format (0.20+)”cloudflare://STORE_ID[?account_id=ACCOUNT_ID][&scopes=LIST][&auth=MODE][&wrangler_profile=NAME]STORE_IDis required and selects the account-level Secrets Store.account_idselects the Cloudflare account and falls back toCLOUDFLARE_ACCOUNT_ID.scopesis a comma-separated list applied when a secret is created or replaced. It defaults toworkers. Supported values areworkers,ai_gateway,dex,access,containers, andwebsearch.authisauto(default),token, orwrangler.wrangler_profileselects a named Wrangler auth profile and requiresauth=wrangler.
URI examples (0.20+)
Section titled “URI examples (0.20+)”cloudflare://STORE_ID?account_id=ACCOUNT_IDcloudflare://STORE_ID?account_id=ACCOUNT_ID&auth=tokencloudflare://STORE_ID?account_id=ACCOUNT_ID&auth=wranglercloudflare://STORE_ID?account_id=ACCOUNT_ID&scopes=workers,containerscloudflare://STORE_ID?account_id=ACCOUNT_ID&auth=wrangler&wrangler_profile=productionStorage model (0.20+)
Section titled “Storage model (0.20+)”The provider maps a declaration key directly to an account-secret name. For
example, DATABASE_URL maps to:
account: ACCOUNT_IDstore: STORE_IDsecret: DATABASE_URLProject and profile names are not added to the secret name. The store selected by the provider alias supplies isolation. Use a different alias and store when two profiles must hold different values for the same key.
Cloudflare Workers can bind that account secret to any binding name; the SecretSpec key does not need to match the Worker’s binding variable.
Use existing secrets (0.20+)
Section titled “Use existing secrets (0.20+)”A ref changes the Cloudflare
secret name updated or deleted by SecretSpec:
[profiles.production]DATABASE_URL = { description = "Production database URL", ref = { item = "PRIMARY_DATABASE_URL" }}The reference remains write-only: it can select an existing name but cannot retrieve its plaintext value.
Discover secret names (0.20+)
Section titled “Discover secret names (0.20+)”Cloudflare’s list API exposes names, IDs, scopes, comments, and status without returning values. SecretSpec uses that metadata for declaration discovery:
$ secretspec init \ --from 'cloudflare://STORE_ID?account_id=ACCOUNT_ID&auth=wrangler' \ --project my-app --profile productionThe generated manifest contains required declarations for active or pending secret names, not defaults or values.
CI/CD (0.20+)
Section titled “CI/CD (0.20+)”Use a short-lived or account-owned token scoped to Secrets Store Write:
- run: secretspec set DATABASE_URL --profile production --provider cloudflare_prod env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}The account ID and store ID are attribution, not credentials, and can stay in the checked-in provider URI.
Security considerations and limitations (0.20+)
Section titled “Security considerations and limitations (0.20+)”- Cloudflare’s management API accepts values for creation and replacement but
never returns them. Plaintext access exists only inside a Cloudflare service
with a Secrets Store binding. This provider therefore cannot support
get,check,run, fallback reads, generation-on-miss, prompting-on-miss, or value comparisons. - Secret values are serialized directly into an HTTPS request body. They do not appear in the provider URI, command arguments, Wrangler input, or SecretSpec diagnostics.
- HTTP redirects are rejected so credentials and secret-bearing request bodies remain confined to Cloudflare’s API origin.
secretspec setlists metadata to resolve an existing name to the secret ID, then creates or patches it.secretspec deleteuses the same metadata lookup and remains idempotent when the name is absent.- A replacement applies the
scopesconfigured in the provider URI. Review those scopes because changing them affects which Cloudflare services may bind the secret. - Secret values cannot exceed Cloudflare’s 65,536-byte limit.
- Cloudflare Secrets Store is currently a beta service; API behavior and scope availability may change upstream.