Secrets reference

Config options

OptionDefaultMeaning
registry{}Your secrets, keyed by name. Edited by hand — not settable from the command line
rotationIntervalDays30How often the at-rest key rotates. The manager re-encrypts every stored secret on this cadence

An empty registry here does not mean an empty registry in the Worker: every capability contributes its own slice at runtime.

A registry entry

Declaring one is the walkthrough; writing its value is pithy secrets; a dev value lives in the config directory.

FieldValuesDecides
backendd1, cf-secrets-storeWhere the value is stored. A storage decision, not a read-time one
scopeenvironment, globalPer environment, or one value everywhere
valueTypetext, json with a schemaWhat shape it is
devValuerandom, or absentWhether the kit can mint a dev value
rotationlocal, provider, manualWho replaces it
rotateEveryDaysa numberWhat makes overdue a fact the registry states rather than a number a dashboard invented
keyeda flagA keyspace rather than a single name

rotateEveryDays is independent of whether anything can rotate it automatically. A third-party key no automation will ever roll is exactly the one whose drift nothing else would surface.

Bindings

BindingTypeWritten by
SECRETSd1pithy add
SECRETS_ENCRYPTION_KEYSsecretpithy secrets provision

A secret binding has no array in the wrangler config until an account has been reached — the entry needs a store id and an entry name that do not exist yet. So add reports it rather than writing an entry wrangler would refuse.

Its own database, per environment

Separate from the app database, and a singleton per environment rather than per feature.

The reason: a deployed feature Worker still needs the signing keys, and a global secret must be present in every environment at once. The app database is provisioned and torn down per branch; this is not.

A feature gets no secrets manager, deliberately — a manager is a Worker with its own database and its own rotation cron, and one per open pull request is not a cost a branch should carry.

Storage format

Every stored value is one envelope carrying its current version and every still-valid one, sealed once.

The secret’s name is bound in as authenticated data — not encrypted, bound. A ciphertext moved between rows does not open.

The master key

Lives inside that environment’s manager Worker and never leaves it. Nothing reads it back out.

A stored value therefore cannot be copied between environments. A global secret split across environments cannot be completed by reading one of them.

It rotates on its own axis, inside the manager, on its own cron. It is the one secret nothing replaces in place — replacing it would leave everything sealed under a key nobody holds.

Command requirements

CommandNeeds
lsNothing. Offline, no credentials
editNothing beyond a project name
create, update, rmCloudflare credentials, and a deployed manager
rotateThe same. --dry-run needs neither
provision, deprovisionCredentials and a recorded store id

--env here is the managed set — every environment the project declares — rather than the three --env takes elsewhere. dev is refused, pointing at the dev server.

--env on a global secret is refused, not ignored, and the re-run is the confirmation.

Exit codes

0, and 1 for an ordinary failure. 3 is its own status, for the one failure that cannot be undone: an issuer rolled a credential and the store write failed.

1 means the previous credential is still live and the command can be re-run. 3 means it is not. A script cannot tell those apart from a message, and the two need opposite reactions.

2 is deliberately unused — shells use it for usage errors, and a status that might mean you typed it wrong or a production credential is dead is no signal at all.

Peer capabilities

Required: none. It is the bottom of the graph.

Optional: controlplane — exposes reading the registry and triggering a rotation. audit — records every set, rotation and removal, naming the secret and the environments reached, never the value.

What no surface here can carry

A value. Not on stdout, not in a payload, not in an audit event, not in an error. The rotation result has no field that could hold one, so it is structural rather than a habit.

ESC