Maxisnap Studio 2.3.0 · for web developers and IT
Cloudflare setup: technical brief
Exactly what the Cloudflare edition’s deploy tool in the Studio download (cloudflare/deploy/cli.mjs) creates,
the permissions it uses and why, what it refuses to do, where the credential lives, and
how to revoke or remove everything.
A developer runs it from a terminal, or the customer’s AI assistant runs it for them following AGENTS.md in the package: the same tool with the same plan-first checks either way.
Someone asked you to approve this? The plain-language version is the Studio setup guide.
Summary
- Studio’s Cloudflare edition is one Worker (with static assets), one R2 bucket and one D1 database in the customer’s own account. Address: the account’s
workers.devsubdomain, optionally one new Workers Custom Domain as well. - Setup is additive only. Existing names are refused, not reused; existing DNS records block a custom domain; removal deletes only what the deployment record says the tool created.
- The tool runs on the customer’s or developer’s computer:
node deploy/cli.mjsin the package’scloudflarefolder, with Node.js 20 or later and an API token inCLOUDFLARE_API_TOKEN. It reads the account, prints a plan, and changes nothing until it is run again with--yes. The deploy logic iscloudflare/deploy/engine.mjs. - No captures, library data, people or settings go to Maxisnap. Captures live in the customer’s R2 bucket; the library, people and settings in their D1 database.
What is created
| Resource | Name | Notes |
|---|---|---|
| Worker | <name> from --name, default snaps | ES module Worker, compatibility date 2026-10-01, Workers Logs on, tag maxisnap-studio. Bindings: MEDIA (R2), DB (D1), ADMIN_KEY (secret). Static assets: the dashboard’s CSS, JS and fonts. |
| R2 bucket | <name>-media (next free name if taken) | Captures (media/) and browser-made previews (thumbs/). Default location and storage class. |
| D1 database | <name> (next free name if taken) | Library, people, sessions, settings. The Worker creates its schema on first request. |
| workers.dev address | <name>.<subdomain>.workers.dev | Enabled for this Worker, also when it gets a custom domain; preview URLs off. If the account has no workers.dev subdomain yet, the customer chooses one and it is registered (account-wide; Cloudflare makes it hard to change later). |
| Custom domain (optional) | e.g. snaps.example.com | Workers Custom Domain: Cloudflare creates one proxied DNS record and a certificate for that hostname only. The zone must be in the same account; while it is not active yet, the plan warns that the hostname will not work until it is. |
The Admin key is 40 random characters, stored as the Worker secret ADMIN_KEY
and shown to the customer once.
The CLI generates it and prints it; the customer signs in to the dashboard with it and pastes it into the Maxisnap app to connect.
Permissions and why
| API token permission | Used for |
|---|---|
| Account Settings: Read | List the accounts the credential can use. |
| Workers Scripts: Edit | Read existing Worker names; upload Studio; enable workers.dev; register a workers.dev subdomain; attach the custom domain. On removal: detach the custom domain and delete the Worker. |
| Workers R2 Storage: Edit | Check R2 is on; list bucket names; create the bucket. On removal with “delete data”: list and delete the bucket’s objects, then the bucket. |
| D1: Edit | List database names; create the database. On removal with “delete data”: delete the database. |
| Zone: Read | List the account’s domains: they appear in the plan’s “Will not touch” list, and the zone given with --zone is checked against them. |
| DNS: Read | Refuse a hostname that already has DNS records. |
| Workers Routes: Read | Refuse a hostname an existing Worker route (e.g. *.example.com/*) already covers. Without it the plan shows a warning instead. |
No DNS, zone-settings, firewall, billing or user write permission is requested. Zone Read, DNS Read and Workers Routes Read are needed only for a custom domain. Create the token at My Profile → API Tokens as a custom token; scope it to one account, and to one zone for a custom domain, if you prefer.
Every API call
Read (planning)
GET accounts
GET user (email for display; optional)
GET accounts/{a}/r2/buckets (error 10042 = R2 not enabled yet)
GET accounts/{a}/workers/subdomain
GET accounts/{a}/workers/scripts
GET accounts/{a}/d1/database
GET zones?account.id={a}
GET zones/{z}/dns_records?name={hostname} (custom domain only)
GET accounts/{a}/workers/domains?hostname={hostname} (custom domain only)
GET zones/{z}/workers/routes (custom domain only) Write (after the customer confirms the plan)
PUT accounts/{a}/workers/subdomain (only when the account has none)
POST accounts/{a}/r2/buckets
POST accounts/{a}/d1/database
POST accounts/{a}/workers/scripts/{w}/assets-upload-session
POST accounts/{a}/workers/assets/upload?base64=true (upload JWT, not the credential)
PUT accounts/{a}/workers/scripts/{w} (module upload with bindings)
POST accounts/{a}/workers/scripts/{w}/subdomain (enabled: true, previews_enabled: false)
PUT accounts/{a}/workers/domains (custom domain only) Delete (removal only, and only for resources in the deployment record)
DELETE accounts/{a}/workers/domains/{id}
DELETE accounts/{a}/workers/scripts/{w}?force=true
DELETE accounts/{a}/d1/database/{id} (only with "delete data")
GET accounts/{a}/r2/buckets/{b}/objects (only with "delete data": lists what to empty)
DELETE accounts/{a}/r2/buckets/{b}[/objects/…] (only with "delete data") Deletes run only from remove --record deployment.json --yes or by hand in the Cloudflare dashboard
(see Revoke and remove).
An update (new Studio version or new branding) repeats the asset upload and the script
PUT, plus the custom-domain call when a subdomain was newly chosen, and keeps
the existing ADMIN_KEY secret.
What it refuses to do
- Replace a Worker. A Worker name that already exists in the account is a blocker in the plan, unless the deployment record in use says this tool created it (an update, or finishing an install that stopped).
- Rename your workers.dev subdomain. A subdomain is registered only when the account has none yet; otherwise
--subdomainis ignored and the existing one is used. - Read your content. It lists bucket objects only to empty a bucket from its own record during
remove --delete-data, and reads DNS records only for the one hostname being checked. - Take an existing hostname. Any DNS record at the name, a custom domain attached to another Worker, or an overlapping Worker route blocks the plan. The zone apex is refused.
- Delete anything it did not create.
removetouches only the resources the deployment record lists as created by the tool, and keeps the bucket and database unless--delete-datais given. - Act without a yes.
planonly reads.deploywithout--yesprints the plan and stops;updateandremovealso need--yes. - Take the token as an argument. The CLI reads it from the
CLOUDFLARE_API_TOKENenvironment variable; it has no command-line option for it.
An install that stops half-way (network drop, closed terminal) is recorded step by step in the deployment record; running the same deploy command again plans from that record
and continues without creating anything twice.
Credential and local files
| What | Where |
|---|---|
| Cloudflare API token | Read from the CLOUDFLARE_API_TOKEN environment variable of the terminal session that runs the CLI. |
| Deployment record (names, ids, addresses, what it created; no secrets) | deployment.json in the folder the CLI runs from (normally the package’s cloudflare folder), or the file given with --record. Updates and removal read it. |
| Admin key | deploy prints it once and stores it as the Worker secret ADMIN_KEY; it is not in the deployment record. The customer keeps it and pastes it into the Maxisnap app to connect. |
Once connected, the Maxisnap app keeps the Studio address and key in its own settings on that computer (on a Mac, the key goes in the Keychain). It does not need the Cloudflare token.
Custom domain checklist
The Maxisnap desktop app uploads with an X-Dashboard-Key header, not a
browser session. On the Studio hostname make sure that:
- No Cloudflare Access application covers it (for example a wildcard
*.example.compolicy). Share links must also be reachable by the people you send them to. - Bot Fight Mode, Super Bot Fight Mode, Under Attack mode and WAF custom or managed rules do not challenge or block
/api/*. A WAF skip rule for the Studio hostname is the simplest fix. - No Cache Rule or Page Rule forces caching of HTML or
/api/*. Studio sendsprivate, no-storeandno-transformon media and pages. - Uploads arrive at
/api/upload.php. Maxisnap for Windows and Maxisnap for Mac send each upload to a Cloudflare Studio in one request, which it accepts up to 25 MB per file.
A workers.dev address is not affected by zone settings, which is why it is the default.
Usage and cost
Studio is built for Cloudflare’s free plan. Cloudflare publishes the current free allowances, limits and prices; check them against your expected use.
| Service | Studio’s use | Cloudflare’s current terms |
|---|---|---|
| Workers | Opening a share link is about two Worker requests. Route modules load lazily to stay inside the Free plan’s CPU time per request; measured CPU is 1–8 ms per request, warm. Static asset requests are served before the Worker runs. | Workers pricing and limits |
| R2 | Captures and their previews. Maxisnap for Windows and Maxisnap for Mac send each upload to a Cloudflare Studio in one request, which it accepts up to 25 MB per file. | R2 pricing |
| D1 | One row per capture, plus small session and settings rows. | D1 pricing |
Cloudflare may ask for a payment method before R2 can be turned on, even within the free allowance. Cloudflare’s Workers error list, read in October 2026, gives error 1027 as a Worker exceeding the free tier’s daily request limit. Its Workers limits page describes the current limits and the Workers Paid plan.
Revoke and remove
Revoke the deploy token
- In Cloudflare: My Profile → API Tokens → delete the token you created for the deploy. An update or removal later needs a token with the same permissions.
Revoking does not affect the running Studio or uploads; those use the Studio’s own Admin key.
Remove the Studio
# PowerShell, in the cloudflare folder you deployed from (it holds deployment.json)
$env:CLOUDFLARE_API_TOKEN = "<token>"
node deploy/cli.mjs remove --record deployment.json --yes # keeps captures (bucket + database)
node deploy/cli.mjs remove --record deployment.json --yes --delete-data # deletes everything it created By hand in the dashboard: delete the custom domain (Worker → Settings → Domains & Routes), the Worker, then the R2 bucket (after emptying it) and the D1 database. The DNS record Cloudflare made for the custom domain is removed with the custom domain.
Doing it yourself
The cloudflare folder in the Studio package is a standard Wrangler project.
cloudflare/README.md covers wrangler deploy;
docs/Developer guide.html covers the architecture and upload API.
With plain Wrangler you create the bucket and database yourself and check names by hand; the bundled CLI (node deploy/cli.mjs plan|deploy|update|remove|whoami) does the planning and guards described above.
The developer route of the setup guide has the
commands.
Questions about this brief?
Email support@maxisnap.com.
Every Studio download also has a technical brief, docs/Cloudflare setup - technical brief.html.