Skip to content
downpipes docs

Run and reset the downpipes demo

This page describes how the team itself runs the downpipes demo, and how to reset it to first-run so an evaluator can walk the whole product end to end. It is for the operator standing up or driving the demo, not for a self-hoster following the production deploy. Customers self-host their own engine and console, so the demo was never a customer-facing service. It was the running reality the team operated from until the owner decommissioned it on 2026-07-07, and it has not run since.

The demo is down, and this runbook is held

Standing owner directive, 2026-07-07: the demo environment was decommissioned by the owner and stays down. Do not deploy, reset, probe or otherwise resurrect downpipe-engine-demo, downpipe-console-demo or the demo hostname until the owner lifts this, and do not do it as an incidental step of some other flow either. The directive is recorded at internal-docs/00-START-HERE.md under the standing owner directives and again in internal-docs/ENGINEERING-WORKFLOW.md, and it overrides every older runbook line that ends in a demo deploy.

Everything below is retained as the record of how the demo was built and reset, so it can be rebuilt exactly if the directive is lifted. Read it as history, not as a task. Nothing on this page is a step to take today.

This is an operator-internal page

The demo is throwaway and operator-internal. Treat the demo host, the demo account and any throwaway credential as private to whoever runs the walkthrough, not as a published address or a customer-facing fact. Examples below use a placeholder console hostname such as console.example.com; your own demo hostname is yours to keep behind Access.

The demo topology

The demo is two throwaway -demo Workers in one Cloudflare account: a demo engine (downpipe-engine-demo) and a demo console (downpipe-console-demo). It mirrors the shipped topology exactly, which is the point: the only public surface is the demo console, fenced by Cloudflare Access, and the demo engine carries no route at all. The console reaches the engine over a worker-to-worker service binding named ENGINE declared in console/wrangler.demo.toml, so gating the one console hostname gates the whole demo.

Surface Reachability Why
Demo console The only public hostname, behind a Cloudflare Access policy scoped to your email Access is the perimeter; everything else hangs off the console origin
Demo engine Routeless. Reached only through the console’s ENGINE service binding A service binding is an in-runtime call that never traverses the public network, so the engine has no public attack surface
Reconciliation cron The */15 trigger on the demo engine, no route Drives the credential-expiry reconciliation and new runs, the same cadence as production

Access is the perimeter only. Inside the demo you bootstrap with native auth exactly as in production: an admin token first, then a passkey. The CF_ACCESS_* variables are deliberately unset on the demo engine, so the engine never trusts an Access assertion and the native sign-in path is the one you actually exercise. That keeps the demo honest about how a self-hosted engine authenticates a real operator.

A WORKER_NAME = "downpipe-engine-demo" variable on the demo engine is the safety pin for the source-attach step. The engine’s in-product source attach rewrites the bindings of the worker named in env.WORKER_NAME (it falls back to downpipe-engine when unset), so pinning the demo worker name guarantees an attach modifies the demo worker and never some other engine.

Stand up the demo

You do this once, with a scoped, short-lived Cloudflare API token that you create for the deploys and revoke the moment they finish. The running demo then holds no Cloudflare API token, the same no-custody posture as production.

  1. Set the demo engine's account id and Access policy

    Fill the demo engine config’s account-id variable with your own account id, and leave WORKER_NAME set to downpipe-engine-demo. Then, before the URL is ever open, add a self-hosted Cloudflare Access application on your demo console hostname with an Allow policy that includes only your email (add teammates as needed). The Access free tier covers this. Because the console config provisions the demo hostname’s DNS on deploy, putting Access on first means the hostname is never reachable unauthenticated.

  2. Deploy the demo engine

    With your scoped deploy token exported as CLOUDFLARE_API_TOKEN, deploy the engine from its demo config and set its admin token secret.

    npx wrangler deploy --config wrangler.demo.toml
    npx wrangler secret put ADMIN_TOKEN --name downpipe-engine-demo

    The first deploy applies the Durable Object migrations to the fresh downpipe-engine-demo script, giving it its own isolated scheduler and per-run seal objects, separate from any other engine’s. There are no source bindings yet, by design: every source is attached later from the console.

  3. Build and deploy the demo console

    Rebuild the console bundle, then deploy the console from its demo config. The console config carries a custom_domain route for your demo hostname and the ENGINE service binding to downpipe-engine-demo, never a workers.dev address.

    npm run build
    npx wrangler deploy --config wrangler.demo.toml
  4. Revoke the deploy token

    Delete the scoped token in the Cloudflare dashboard. The running demo now holds zero Cloudflare API tokens, exactly as a production deployment does.

  5. Bootstrap your identity

    Open the demo console, pass the Access challenge, and sign in with the admin token you set in step 2. That is the last-resort bearer; immediately register a passkey to become the first owner, then retire the admin token when you are ready (the product nudges you to). You are now signed in as a real operator, not as Access.

After this you have a clean first-run demo. Walk it however you like: the natural arc is to add a destination, attach a source, run a backup, prove it recoverable, and then rehearse a full recovery offline with the open-source CLI. The offline rehearsal is the most convincing part of a walkthrough, because it proves the data comes back with neither the vendor nor Cloudflare in the loop.

Reset the demo to first-run

The demo ships a hard-gated, demo-only reset so you can wipe it back to first-run and re-walk it as often as you like, without redeploying. The reset wipes the demo engine’s Durable Object completely: identity and passkeys, roles, downpipes, destinations, sources, the credential registry, the canary, the audit chain, config history, the identity-provider connections and the licence. It is destructive by design, and it is impossible in production.

Why the reset cannot touch a production engine

The reset route POST /admin/demo/reset exists only when DEMO_MODE is set, which is set only on the demo engine. On any engine without DEMO_MODE the route returns a 404 before any authentication or Durable Object round-trip, so a production engine has no reset surface to find. When DEMO_MODE is present, the route is gated directly on the admin-token bearer with a constant-time comparison, decoupled from the Access and passkey precedence, so it works repeatedly and before or after bootstrap. Both properties are covered by the demo-reset validators that run in npm run validate.

There are two ways to invoke it, and the in-console button is the everyday one.

Reset from the console

In Settings, the Demo section appears only on the demo engine (the console reads status.demoMode). Click Reset demo to fresh, paste the demo admin token, and confirm. The page reloads to a fresh first-run. No service token is needed here, because your browser is already past Access. Then re-bootstrap with the admin token and a new passkey to become owner again.

Reset from a script

For automation or continuous integration, engine/scripts/demo-reset.sh calls the same route. It needs a Cloudflare Access service token so the script itself can pass the Access application in front of the demo hostname: create one in Zero Trust under Access service tokens, then add it to the demo application’s policy.

DEMO_URL=https://console.example.com \
ADMIN_TOKEN=<the demo admin token> \
CF_ACCESS_CLIENT_ID=<service-token id> \
CF_ACCESS_CLIENT_SECRET=<service-token secret> \
./scripts/demo-reset.sh
Variable What it is
DEMO_URL The demo console origin the script posts to (its own demo hostname behind Access)
ADMIN_TOKEN The demo engine’s admin token, the break-glass bearer that authorises the reset
CF_ACCESS_CLIENT_ID The Cloudflare Access service-token id, so the script passes the Access app
CF_ACCESS_CLIENT_SECRET The matching Access service-token secret

A reset is deliberately not a full teardown. It does not delete the destination archives in R2, and it does not remove a source binding that an earlier attach left on the worker (the fresh Durable Object simply ignores the inert binding). The script prints the optional commands for a truly pristine slate, so empty the demo bucket or re-deploy the base config separately when you actually want one.

What is and is not live on the demo today

The demo runs the product, so most of what an evaluator sees is genuinely working: native sign-in, passkeys, sources and destinations, backup runs, the keyless attestation, and offline recovery with the CLI. A few surfaces are built and wired in the codebase but are deliberately not exercised on the demo as a hands-off feature, so it is worth setting expectations before a walkthrough.

Surface State on the demo
Cloudflare config restore The backup-and-preview side is real, but in-band config write-back is supervised-first and not exercised on the live demo today. The cross-account guard is enforced: an apply into a different account than the archive’s recorded origin is refused unless you explicitly confirm the target. See Cloudflare config backup and restore
Point-in-time restore The restore screen’s calendar (Browse by date) marks the days that hold a retained run, wired the same way against the demo’s fake data as against a live engine. Recovery is still by run id, and the recovery objective is shown as the newest good run. See recovery objectives
Coverage view Coverage is shown in the honest-unknown state, never a green claim for something the engine has not measured. See coverage
Config version id and the update channel The config-version pin stays built-not-deployed on the demo. The engine safe-apply has run live on the demo exactly once (2026-07-03: one promote and one automatic rollback, both of which executed correctly), so do not present the demo as running channel-driven updates day to day, and the console component apply has not been exercised live at all. See the update channel

Holding to that list keeps the demo honest. The reset lets you re-walk the working surfaces as many times as you need, and the offline recovery rehearsal is the one to dwell on, because it is the promise the whole product is built around.

Where this fits

This page is the operator’s guide to the demo specifically. For the production deploy a self-hoster follows in their own account, see deploy and the topology it mirrors in topology: one public hostname and a routeless engine. For the offline recovery rehearsal that closes a good walkthrough, see break-glass recovery and the full CLI command reference. For the no-custody posture that explains why the running demo holds no Cloudflare token, see the no-custody trust model.

Last updated .