Activating the controlled update channel (owner ceremony)
The vendor serves a signed channel at the singular update.downpipes.io, and the engine’s pinned release-signer public key verifies it. On the shipped base config the signer is pinned and the channel URL is set. The live confirmation is GET /admin/updates reporting verified: true: it has somewhere to look and the host serves a channel that verifies under the pinned key. The activation ceremony is a one-time piece of owner and infrastructure work that lives outside the product: you stand up the host, generate and pin a release-signer key (or confirm the pinned one), point the engine at the signed document, and redeploy.
This page is for the owner who runs that ceremony, and for re-keying or rotating it later. It is the prerequisite for applying updates, which is where the day-to-day flow lives. None of this gates a backup or a recovery: an update is never required for either.
Activation switches on the verify-and-review side and deploys nothing. Applying a live update and rolling one back are separate, explicit actions, each needing a one-shot Cloudflare deploy token you supply at the moment you act, so neither can happen as a consequence of turning the channel on.
What you are turning on
The engine updates itself by pulling a vendor-signed channel document from inside your own account and verifying it against a release-signer public key you have pinned. A release can also carry the console as a component, applied by the same engine under the same one signature. The vendor never pushes code, and never holds a token that can rewrite your Workers. This is the deliberate alternative to a vendor-held deploy credential, which would be a worse custody risk than the data itself.
The engine ships the verify side. It is presence-gated on two settings and is otherwise a pure pull-and-verify, so activation is a handful of operator actions plus one offline vendor action.
The activation steps
Sign the channel offline (vendor action)
The vendor signs the channel document with the release-signer private key, offline, on a machine with no network. That signing tool lives outside the engine and console packages. It emits the channel JSON, its detached signature, and the public key for you to pin. The signature covers exactly the emitted bytes (the engine verifies over the bytes it fetches and then parses those same bytes, with no canonicalisation), so the document must be published verbatim. Re-serialising or reformatting it after signing breaks the signature. The release-signer private key never leaves that offline step. The vendor process is documented on publishing a signed release.
Stand up the channel host on a custom domain
Publish two objects at a stable HTTPS URL: the channel body, and the same URL with a
.sigsuffix holding the base64url signature on a single line. Because the engine trusts the pinned signature and not the host, this is static signed content with no server logic, so it is served as two static files from an R2 public bucket attached to a custom domain. The host is the singularupdate.downpipes.io, servinghttps://update.downpipes.io/stable.jsonandhttps://update.downpipes.io/stable.json.sig. The host also carries the artefact bytes each release points at: the engine bundle and, for a release that carries a console component, the self-describing console artefact (console-<version>.json), both hash-pinned by the one signed document. The host serves nothing until the bucket exists, public access is on and the custom domain is attached.Pin the signer and point the engine at the channel
Set two values on the engine Worker, as a wrangler secret or var. The engine reads each one as a plain string from its environment.
UPDATE_SIGNER_PUBLICis the printed release-signer public key (an Ed25519 32-byte half concatenated with the ML-DSA-87 public 2592-byte half, 2624 bytes, base64url). This pin is the whole security model.UPDATE_CHANNEL_URLis the channel body URL from the previous step.A third setting exists for component-aware releases and is optional:
CONSOLE_WORKER_NAMEnames the console script a release’s console component may deploy to, and it defaults todownpipe-console, so set it only if your console Worker uses a different script name. The name alone is never trusted: before the engine touches that script it requires the script’s live bindings to include theENGINEservice binding pointing back at this engine, so a wrong name can never redeploy an unrelated Worker.Redeploy the engine
Redeploy so the engine picks up the two settings. Presence of both makes the status report
configured: true, but the channel does not verify, and nothing applies, until the host is stood up and serving the signed channel.Confirm configured and verified
Read the engine’s update status. The status flag reports the channel as configured (presence only). The dedicated updates route does the real work: it fetches the signed document and its signature, verifies them under your pinned key, and returns whether the channel verified. Confirm that it reports verified true, and that the recommended version it returns is the one you signed.
Configured is not verified
The distinction is deliberate and it is the thing operators most often conflate. They are two different checks, answered by two different routes.
| Reading | Where it comes from | What it actually means |
|---|---|---|
configured | The status report flag updateChannelConfigured | Presence only: both UPDATE_CHANNEL_URL and UPDATE_SIGNER_PUBLIC are set. It says nothing about validity |
verified | The updates route, which fetches and checks the signature | The real result: the channel was fetched over HTTPS and its detached signature verified under the pinned signer |
The status flag is a pure truthiness check on purpose: it cannot leak a parse-derived detail of a secret, and it is cheap enough for the console to poll during setup. So a malformed pinned key still reads as configured at the status level. The verified result comes from the updates route, which never throws and never falls back to an unverified document. A wrong signer, a tampered body or an unfetchable URL all surface there as not verified, with a reason. If both settings are absent, the channel simply reports not configured, which is not an error.
The console reflects these states directly. The update tile reads “Channel not set” when the channel is unconfigured, and the signing tile reads “Configured, not verified” when the settings are present but the signature has not verified. Only when the channel is both configured and verified does the signing tile read “Signer verified”.
The trust property
The engine pulls and verifies; it never accepts a push, and nothing inbound is ever accepted. The document is fetched outbound over TLS with redirects disabled, so a redirected signed fetch cannot be steered to another host. The signature is the hybrid scheme used everywhere else in the product, an Ed25519 signature alongside an ML-DSA-87 signature, both halves required, so the channel is post-quantum hybrid signed. The engine verifies against the key you pinned, never a key the channel asserts about itself.
The custody story is the point of the whole design. The vendor never holds a credential that can rewrite your Workers, and the release-signer private key lives offline and is never deployed. A compromised channel host therefore cannot substitute an unsigned or wrong-signer document, because the engine checks the pinned signature rather than trusting the host. For the deploy step of an actual apply, the only credential involved is a one-shot token scoped to the “Edit Cloudflare Workers” template (Workers Scripts: Edit), collected in the console at apply time and used once; it is distinct from your full account deploy credential and is never stored.
A release-signer fingerprint is pinned via the UPDATE_SIGNER_PUBLIC setting and that pin alone. There is no compile-time bake of the fingerprint into the engine binary, so do not rely on the engine printing a baked-in fingerprint or on a recovery sheet carrying one; the pin is the environment value you set in step three.
The generated deploy config cannot drift the channel settings
The shipped base config points at the singular update.downpipes.io host and pins the key. A bare wrangler deploy is destructive to bindings: it replaces the worker’s whole binding set with exactly what wrangler.toml lists, so it would drop any source attached from the console. Deploys instead run through scripts/sync-bindings.mjs, which reads the live worker’s current bindings and writes wrangler.deploy.toml: a generated, gitignored file that is the committed wrangler.toml plus those live source-binding stanzas appended. Deploying that generated file (npm run deploy, or wrangler deploy -c wrangler.deploy.toml) carries the console-attached sources forward instead of dropping them. Nothing in that generation step touches the update settings, so wrangler.deploy.toml always carries the same UPDATE_CHANNEL_URL and UPDATE_SIGNER_PUBLIC as wrangler.toml. There is no separate deploy-only copy of these settings to reconcile; editing wrangler.toml is enough.
What activation does not do
Activation makes the channel verifiable and reviewable. It deploys nothing, and it changes no running Worker. Two further actions are yours to take, and each needs a one-shot deploy token.
Applying an update is the first. The apply reads the target script’s live settings, preserves every binding and secret, and refuses the upload rather than promote a version it cannot prove will keep them; an uploaded version is inert until it is promoted, and a promote that fails leaves the previously-live version serving, because Cloudflare deploys are atomic. A rollback is the second, and running one deliberately proves the revert direction end to end on your own deployment. The engine records a rollback target the first time you apply an update from the console, and the standalone Roll back control deploys that recorded version and canary-verifies that it landed healthy; with no recorded target it refuses rather than deploy anything blind. Plan both rather than meeting them for the first time during an incident; the mechanics are on applying updates and the recovery controls on update trust and rollback.
Where this fits
Once the channel reports verified, move to applying updates for the review, preview and one-click apply flow. For the brick-safety and trust model in depth, including the standalone rollback and the opt-in gradual ramp, read update trust and rollback. For the vendor side of signing and publishing a channel, see publishing a signed release. For the scopes a deploy token needs, see Cloudflare token scopes.
Last updated .