| name | cloudflare-ops |
| description | Cloudflare for DNS, R2 object storage, CDN and domains. Load before changing DNS records, creating or configuring R2 buckets, issuing storage credentials, pointing a domain at the VPS, or diagnosing anything served through a 9nau.com / client subdomain. |
Cloudflare
Account: samuelaure@gmail.com · Account ID 6cc895a1e69eb42b6690f9c41ae57506.
Cloudflare fronts DNS for the domains, serves the CDN, and holds R2 object storage for naŭ and for
client projects.
Two sets of credentials — they are not interchangeable
wrangler (v4.84.1, OAuth, already logged in). Scopes: account:read, user:read,
workers:write, workers_kv:write, workers_routes:write, workers_scripts:write,
workers_tail:read, d1:write.
It can list R2 buckets, but it has no DNS scope and no R2 write scope. Reaching for wrangler to
change a DNS record will fail with a permissions error that reads like a bug.
A separate API key with DNS and other permissions, held by Samuel. Ask him for it when a task
needs DNS; do not assume it is in the environment.
R2 S3 credentials are a third thing again — access key / secret pairs issued per use in the R2
dashboard, used by rclone and by the services. These are what applications carry in their .env.
An expired one of these is what silently broke the platform backup for weeks, so when a service
reports 401 Unauthorized against R2, suspect the S3 credential rather than the account.
A custom domain makes the whole bucket public
This is the single most expensive thing to get wrong here. Binding a custom domain to an R2 bucket
publishes every object in it, not just the prefix you had in mind. media.9nau.com is bound to
nau-storage, so nau-storage/backups/ was world-readable for three months — full database dumps,
downloadable by anyone who guessed the path.
Never put anything private in a bucket that has a domain bound, however obscure the prefix. Give it
its own bucket instead. A WAF rule blocking the path is not equivalent: the object stays
public-capable and the protection lasts exactly as long as the rule does.
Deleting an object does not purge the CDN. Cloudflare keeps serving the cached copy, and GET and
HEAD cache separately — a HEAD returning 404 says nothing about GET. After deleting anything
sensitive, purge the cache explicitly and re-verify with GET.
Purging needs a token with Zone → Cache Purge. Neither the wrangler OAuth token nor
CLOUDFLARE_DNS_API_TOKEN has it.
R2 buckets
| Bucket | Created | Used by |
|---|
nau-storage | 2026-04-18 | naŭ Platform. Zazu voicenotes at {env}/zazu/voicenotes/{telegramId}/{uuid}.ogg; mobile captures; served publicly via media.9nau.com |
nau-backups | 2026-08-21 | Private, no domain bound. Encrypted database dumps. Never bind a domain to this |
violeta-storage | 2026-07-09 | Violeta Cuesta client project |
r2-asset-manager | 2026-01-18 | Older asset tooling — the r2-asset-manager repo is in deprecated/, so treat this bucket as legacy and confirm before writing to it |
Endpoint form: https://6cc895a1e69eb42b6690f9c41ae57506.r2.cloudflarestorage.com
Services do not get bucket credentials to hand out. The API issues presigned upload URLs
(POST /media/upload-request) and the client PUTs directly to R2 — keep that shape rather than
shipping keys to clients, and never to a mobile binary.
Different services hold different R2 credentials on purpose. Zazu's key is separate from the backup
job's, which is why Zazu's voicenote uploads kept working while backups failed. Preserve that
separation: one dead credential should not take down everything.
DNS and domains
9nau.com is the platform domain. Subdomains in use: api, app, accounts, media.
zazu.9nau.com was retired along with the Zazu dashboard — do not recreate it.
All application subdomains point at the Hetzner VPS, where nau-gateway terminates TLS and routes.
Adding a service means: DNS record → gateway route → container on the internal network. The
container itself never binds a public port.
Client domains (Karen Explora, Violeta Cuesta, Andi Universo, Polar) are also managed here and also
land on the same VPS. Before changing any DNS record, confirm which project owns it — this account
holds domains for several unrelated businesses.
Before changing anything
DNS changes are fast to make and slow to notice when wrong. State the current record, the intended
record, and what breaks if it is wrong, before applying. TTL and proxy status (orange cloud vs grey)
matter: proxied records hide the origin IP and enable the CDN; unproxied ones expose it. Default to
proxied for anything web-facing.