Skip to content
downpipes docs

Data residency and what leaves your account

Residency in downpipes is a decision you make when you choose where backups land, not a concession the vendor grants you. Your backups are written to a destination you select, the engine that writes them runs inside your own Cloudflare account, and the vendor holds no credential for either. This page is the canonical account of where your data and the engine’s metadata sit, written for an auditor or a risk reviewer who needs to state residency precisely rather than gesture at it.

The short version is that the encrypted archives live in your destination, the engine and its operating metadata live in your Cloudflare account, and nothing customer-identifying crosses to the vendor. The engine reports the residency it can see honestly from the destination kind, and it is careful about the one case that genuinely moves data out of your control. The rest of this page is the detail behind each of those claims.

Where the bytes are: ciphertext, metadata, and the vendor

It helps to separate three things, because they sit in different places and an audit needs them named apart.

Thing Where it lives Who can reach it
Backup ciphertext (the sealed archives) The destination you choose: an in-account R2 bucket or an S3-compatible store You, and anyone you grant access to that store
Engine operating metadata (run history, the audit log, presence-only status) A Durable Object in your own Cloudflare account, served on your own custom domain You, through the console served on the same origin
Vendor-held data Nothing The vendor holds no Cloudflare token, no destination credential, and no standing seat

The engine runs as a Worker in your account, and the console is served on the same address: every admin call stays on that one origin, and the console makes no vendor requests (renderConnection, console/src/screens/settings/sections.ts). So the metadata that describes your estate, the run history and the tamper-evident audit log, is account-resident by construction. The vendor is not a place your data passes through; it is a software supplier that holds no key to your account.

The archives are sealed before they leave the engine, so what sits in the destination is ciphertext, wrapped to your recovery recipients. Choosing an S3-compatible store moves where that ciphertext rests, not whether it is encrypted. Residency is about where the bytes sit and who can reach the store, which is a separate question from confidentiality.

You choose the destination; the engine reports it

The engine does not pick your destination and cannot move it. You set it, and the engine reports back the kind it resolved so the console can state residency rather than assume it. The status route’s destination facts are four coarse fields and no value: a destKind enum, a destConfigured boolean, a destResolved string that names the unresolved case rather than leaving it null, and a destAmbiguous boolean that carries a cause. A fifth, statusSource, says whether those facts were read live or fell back to the environment mirror. None of them echoes the endpoint, the bucket name, the region, or any access key (buildStatus, engine/src/admin/status.ts). That is a deliberate redaction, so the residency read itself cannot leak where you write.

There are three states the console renders from that kind, each with its own honest line on the Data and residency panel (residencyBody, console/src/screens/settings/sections.ts).

In-account R2. The console reads this as “In-account R2 (recommended; no destination credentials on the wire).” This is the no-custody destination: an R2 bucket in your own account, reached through a Cloudflare binding, so there is no destination access key to configure, hold, or expose. The bytes never leave your Cloudflare account.

An S3-compatible store. The console reads this as “S3 (verify the region and account; this can move data out of your account).” This is the honest caveat, and it is the one to read carefully in an audit. An S3-compatible destination can put your ciphertext in a store and a region you should verify, and it can move data out of your Cloudflare account. The engine does not decide that is wrong, because a deliberate copy to an Australian S3 region may be exactly what your obligations require; it just states plainly that the bytes are going somewhere you need to confirm.

No destination kind set. The panel reads “No destination kind set.” Read this one carefully, because two different situations reach it. The ordinary one is first run: you have not chosen, and the engine reports not-ready rather than guessing a destination. The other is an engine configured with an R2 binding and S3 credentials at once and no DEST_KIND to choose between them. The destination factory refuses that ambiguity rather than picking one, because a silent choice would split the archive across two stores, and the refusal is coarsened into the same not-ready answer (resolveDestKind, engine/src/admin/status.ts).

An auditor reading the panel alone cannot tell those two apart, and the difference matters: the first means nothing is configured, the second means two things are and the engine will not guess. The engine does distinguish them. destAmbiguous on the status report carries the cause, and the remedy is one line, setting DEST_KIND to the store you meant. The console does not render that field today, so the distinction is visible in the support bundle’s status report and not on the Data and residency panel.

The honest relabel: R2 reached through its S3 endpoint

There is one subtlety the engine handles so it does not mislabel your own bucket as foreign. A destination you set from the console is stored in an S3-shaped record, because the console cannot create a Cloudflare binding at runtime. But an R2 bucket is also reachable through its own S3-compatible endpoint, <account>.r2.cloudflarestorage.com, where <account> is the Cloudflare Account ID of the account the bucket lives in, the 32-character identifier the Cloudflare dashboard shows in its R2 area. That Account ID is what forms the endpoint host, and the console asks you for it in the two cases where the endpoint has to come from an id you type: when discovery could not name the engine’s own account, and when you tick This bucket is in a different Cloudflare account to point the destination at an account other than the one discovery named. In the ordinary case neither applies, the endpoint is derived from the discovered engine account, and there is nothing to type. If the engine blindly read the S3 shape, it would call your own in-account R2 bucket a third-party S3 store, which is wrong and would understate your residency.

So the engine inspects the endpoint host. When it recognises the Cloudflare R2 endpoint, it relabels the destination as in-account R2 rather than foreign S3, because the bytes rest in Cloudflare R2 regardless of which API shape reached them (isR2Endpoint and the console-set branch of buildStatus, engine/src/admin/status.ts). The relabel is a residency correction, not a credential claim: it says where the bytes rest, which is the question residency asks.

The relabel reads the endpoint host, not the account id inside it

isR2Endpoint matches any *.r2.cloudflarestorage.com host and never compares the account in that host against the engine’s own account (engine/src/admin/status.ts). So a destination you deliberately pointed at a second Cloudflare account, using the different-account tick described in putting an R2 bucket in a different Cloudflare account, still reports as in-account R2. The label is telling you the archive rests in Cloudflare R2 rather than in a third-party S3 store; the Account ID on the destination is what tells you which Cloudflare account holds it. An audit that has to name the account should read that field, not the residency label.

The fields, briefly

A destination is a handful of fields, and each carries a rule worth stating for an audit.

Field Applies to The rule
Endpoint S3-compatible destinations Must be an https URL. The engine additionally screens the host against private, link-local and metadata addresses and refuses them, an SSRF guard, so the endpoint cannot be pointed at an internal service. Loopback is the one documented exception: a plain http://localhost, http://127.0.0.1 or http://[::1] endpoint is permitted for local testing against MinIO or LocalStack, and never leaves the host. See the threat model for the precise rule.
Bucket R2 or S3 Proven writable by a live write-probe at save time rather than checked against a name format, so a name that reads as valid but is not reachable and writable is refused.
Region S3-compatible destinations Defaults to auto, which suits R2 and most S3 providers. Amazon S3 itself rejects auto and answers a bucket in another region with an HTTP 301, so set the bucket’s real region there. Under STS AssumeRole it must be the role’s real AWS region, never auto, and the engine refuses auto in that case.

The support bundle does not leak your destination

Residency is something you configure and verify, and it is not something the support bundle quietly carries out of your account. When you raise a ticket, the diagnostics bundle aggregates presence-only status, which includes the destination kind enum but not the destination itself. Because the bundle embeds the same status report, it inherits the same redaction: the endpoint, the bucket, the region, and any access key are absent by construction (buildStatus embedded in the bundle, engine/src/admin/support.ts).

So the bundle can tell support that you write to R2 or to S3, which is a coarse fact useful for diagnosis, without revealing where. Residency is a fact you state and verify yourself; it is not exfiltrated by the diagnostic path. For exactly what the bundle does and does not contain, see the support bundle.

The bundle and the audit feed carry operator identity, so neither is free of personal data even though neither carries your destination. The bundle includes your own downpipe names and operational facts, and the separate audit feed records, per event, the actor email, the stable actor subject, the auth method, and the source IP. A role appears only inside a role-change event’s target, as the affected member’s role, never as the acting operator’s role. Treat them as evidence about your operators, not as anonymous telemetry, when you reason about what leaves your account.

Residency as a configuration decision, not a vendor concession

The principle worth taking to a risk team is that backup residency is a configuration decision, not a vendor concession. The same sentence appears on the SOCI and CIRMP mapping the website publishes, against the material-risk entry about storing sensitive operational information outside Australia: you choose the destination, an R2 bucket in your account or an S3-compatible store in an Australian region, and downpipes is the control you put against that risk rather than the holder of the data (website/src/pages/compliance/soci.astro).

That framing matters because it inverts the usual question. With most backup vendors, residency is something you negotiate and the vendor concedes, because the vendor holds your data. With downpipes the vendor holds nothing, so residency is yours to set and yours to evidence. The engine’s job is to write where you point it and to report that destination honestly; the choice, and the proof of the choice, stay with you.

For the deeper reasoning about what the vendor and the engine can and cannot hold, including the optional operational key posture, see the no-custody trust model. For how that posture interacts with support specifically, see getting support without giving us access.

Where this fits

Residency is where your estate lands; the rest of the picture is how it gets there and how you read it back.

Setting and verifying the destination

The destination is set from the console, not from the engine’s environment by hand, and the write is verified before a run pins it. The mechanics of choosing a destination, the write-probe that confirms it, and what an in-account R2 versus an out-of-account S3 choice means for credentials live with the destination surface. The Data and residency panel under Settings is the at-a-glance read of the kind the engine resolved.

Reading residency on the map

The topology map reflects residency visually: an in-account R2 archive node reads differently from an out-of-account S3 archive, and the destination note states the model in words rather than leaving it implied. See the topology map for how the map labels the destination from the same destKind this page describes.

To see residency rendered as part of the live estate, read the topology map. To understand the support path that deliberately does not carry your destination, read the support bundle. When you are leaving the product, leave downpipes explains why retiring the engine does not move your data: your backups already live in the destination you chose.

Last updated .