KUMODeck

Limits

KUMODeck runs your app or game on Cloudflare. The rule is: KUMODeck should not leave you less free than renting Cloudflare yourself. So every limit is one of three kinds:

KindWhat it isWhat to do
Cloudflare's own limitthe same limit you would meet on your own Cloudflare accountdesign within it; the values are below
A protection KUMODeck keepsit protects money (prepaid credit), accounts, or other people's apps on the same serversstay within it, or take the way around in the table
Anything elsea size or count KUMODeck chose; these are being raised to Cloudflare's leveldo not copy the number anywhere; read it from the refusal

There is no limit on how many projects or API keys you have.

Read the limit from the refusal#

Errors are JSON with code, message, details and often hint.

RefusalWhere the number is
a size or count (413 save_too_large, 400 too_many_variables, 413 too_many_files / file_too_large, …)details.limit (and usually details.size, what you sent)
a setting out of range (400 invalid_request, from config push or functions limits)details.issues[].message names the maximum; path names the field
too fast (429 rate_limited)details.retryAfter (seconds to wait) and details.rule (which rule)

Do not copy KUMODeck's numbers into your app's code: once a limit is raised, the copy keeps refusing what the server already accepts. On a 429, wait retryAfter seconds and retry once. Spreading load over extra projects or keys to dodge a rule is exactly the use the rule exists to stop, and gets the project paused.

Money: Cloudflare bills you later, KUMODeck is paid first#

This is the biggest difference from renting Cloudflare yourself.

  • Cloudflare (your own account) charges your card after the month. Nothing stops when you use more; the bill grows.
  • KUMODeck takes usage at cost from prepaid credit you added first. When the balance reaches $0 and nothing covers it (an auto top-up that has not failed), the paid parts stop until you add funds: your site, its API (saves, sign-in), multiplayer, Functions, and creating projects or deploying (402 balance_due in the CLI; your users see a neutral "temporarily unavailable", 503). Data is kept. The dashboard, kumodeck config push, reading data and adding funds keep working, and everything resumes by itself about 30 seconds after a top-up.
  • Why: KUMODeck pays Cloudflare for every project at once. Without prepaying, an unpaid project would be paid by KUMODeck with no ceiling. Prepaying is also your own ceiling: a bug or a flood never costs more than your balance.
  • So it never stops: auto top-up. On the dashboard's prepaid credit page, save a card and choose a threshold and an amount. When the balance drops below the threshold, the card is charged and nothing stops (if the card fails, you get an email). Turn it on before a launch, and whenever real people use your app or game.
  • A low-balance email comes first. kumodeck billing shows the balance and roughly how many days it lasts, kumodeck billing topup adds funds, kumodeck usage shows what used it. More in Pricing.
  • Adding funds, creating, deploying and secret keys need a confirmed email first (so a freshly taken-over account cannot charge someone else's card).

Protections KUMODeck keeps#

Current values are given where they help you design; the error is always the source of truth.

LimitWhyThe way around
Request rates (429 rate_limited): about 60 save writes a minute per user; per-IP rates on the API, the hosted site, Functions and realtime connects; rates on deploys, config push and database admin commandsone device, a buggy loop or someone else's flood must not burn your prepaid credit or slow other people's apps; admin commands share Cloudflare's per-account API budgetwrite less often (batch); wait retryAfter; batch admin changes. If real users (not a bug) hit a rule marked details.configurable: true, change it for your app; for the others, ask for a higher limit with the rule name from details.rule
Realtime messages per player: 30 a second by default (short bursts up to twice that); excess is dropped, and a player who keeps flooding is disconnecteda modified client must not multiply your billsend positions 10–20 times a second and interpolate; send changes, not every frame; batch values into one message. A game that needs more raises it per mode with limits.messagesPerSecond (up to 240, multiplayer)
Realtime channel sends (per player, channel and IP). Open channels (no account) are small: short messages, few people, 1 message a second, 30 minutes per staybans do not work without accounts, so the space for harassment stays smalluse signed-in channels for anything bigger; rejoin after 30 minutes
Voice calls: a cap on people per call, calls end after 4 hours, each listener hears the loudest 8a forgotten open tab must not keep paying for relayed audiorejoin to continue; leave when the match ends
Call / direct-connection credentials (TURN / SFU) per environment per minute (by default 3,000 credentials and 2,400 voice sessions a minute) and per player per hourCloudflare's relay budget is one per account and shared by every app on KUMODeckfetch once per session, not on every reconnect; for a big launch, raise your environment with multiplayer.relayQuota (turnIssuesPerMinute, sfuSessionsPerMinute, up to 15,000 each = half of the shared budget); beyond that, ask for a higher limit
Direct (P2P) signalling: small messages, about 10 a secondthe signalling server must not become a free relay that skips the room priceuse it only to connect; send game data over the direct connection or a server room
Invite links for direct rooms expire (default 30 minutes, at most 24 hours)a leaked link lets strangers in only brieflymake a new link; choose a longer expiry for a planned session
Player sessions: access token 15 minutes, sign-in about 90 daysa stolen token is useful only brieflynothing: the SDK refreshes
Sign-in protections: passwords of 8+ characters, lockout after repeated failures, email links that expireguessing passwords and codes; mail bombingshow the message
Outbound fetch from Functions is refused (403) to private IPs, cloud metadata addresses, names without a dot, KUMODeck's internal addresses and mining poolsstops server-side request forgery, and one creator getting the shared Cloudflare account suspendedcall public https:// addresses; put a private service behind a public HTTPS address with its own authentication
Automatic pause for mining, extreme CPU or outbound floods (checked about hourly)Cloudflare's terms: one abuser stops everyoneno miners or proxies; before a planned spike (a launch, an event), tell KUMODeck
Secret and variable names: UPPER_SNAKE_CASE; KUMO_ is reservedKUMODeck injects its own keys under KUMO_; your code must not replace themuse another prefix
Deploys skip dotfiles and node_modules (except .well-known/)a .env with secrets is never publishedmove a file you do want public out of a dot folder, or rename it
Unfinished deploys: a few per environment (429 too_many_pending_deployments), expiring after 24 hoursabandoned uploads must not fill storagefinish one before starting the next
Custom domains waiting for their TXT record: up to 20 per environment at a time (409 domain_limit, the number in details.limit). Verified domains do not count, so there is no limit on how many domains you have. A domain not verified in 7 days, or not active in 14 days, is removed (you get an email 2 days before)anyone can sign up any name before proving they own it: without a cap one app could queue thousands of sign-ups for domains it does not own, and the hourly ownership check, shared by every app, would reach other people's domains hours later. An abandoned claim must not let someone take the domain lateradd the DNS records soon, and let domains verify before adding more; add a domain again if it was removed
kumo.config.json: a total size cap; a push takes up to about 5 seconds to apply everywherethe config is read on every request (CPU)keep big content (texts, levels) in your own files or database; test a few seconds after a push
Idempotency-Key: up to 128 characterskeeps the database index smalluse a UUID
Top-ups: a cap per payment; auto top-up waits an hour between charges and a day after a failurestolen cards, double chargesadd funds in several payments; fix the card in the dashboard
KUMODeck's MCP: about 120 calls a minute per tool per developer; hosting_deploy carries files inside the conversationone server shared by every developer; a conversation cannot hold a big siteuse kumodeck (kumodeck deploy) for anything bigger than a small page and for many calls

Changing a rate limit for your app#

Rate limits are counted per app. A 429 marked details.configurable: true (flood guards per IP address, a player's own writes, calls from your own server code) can be raised, lowered or removed in kumo.config.json (rateLimits), then kumodeck config push:

{ "rateLimits": { "api.default.ip": { "multiplier": 5 }, "gamedata.save.player": { "limit": 600 }, "hosting.serve.ip": "off" } }

Requests above the default are normal usage billed at cost from your prepaid credit; with a flood guard removed, a flood from someone else is billed to you too, so lowering a rule is how a small app protects itself. Sign-in guards, your account and money rules cannot be changed there.

Functions CPU time follows your prepaid balance#

One Functions request may use Cloudflare's own CPU range (30 seconds by default, up to 5 minutes, set with kumodeck functions limits --cpu-ms <n>). How much applies right now depends on your prepaid balance: usage reaches the balance a few minutes after it happens, and tying the per-request ceiling to the balance keeps the worst case inside it. Today that is about 5 ms per request for every cent of balance, shared by the environments that run server code, never below 5 seconds and never above 5 minutes (one environment: $20 → 10 s, $60 → 30 s, $600 → 5 min). Auto top-up amounts count once charged. The value you chose is kept and applies by itself about a minute after a top-up. kumodeck functions status --json shows limits.cpuMs (applies now), limits.requested, limits.cpuMsCap (what the balance allows) and limits.requiredBalance (cents needed, or null).

Asking for a higher limit#

When real users (not a bug) hit a protection above — a request rate, call credentials, or a planned spike that could look like a flood — write to [email protected] with your project, the environment, the rule name from details.rule (or the error code), and what you expect (how many users, when). A limit can be raised for your project; the extra usage is billed at cost from your prepaid credit. Cloudflare's own limits cannot be raised this way.

Cloudflare's own limits (the same on your own account)#

From Cloudflare's "Limits" pages (developers.cloudflare.com) or a standard. They do not change by asking.

LimitValueThe way around
Server-rendered app (Worker) size64 MiB uncompressedmove large data to files or a database; drop unused dependencies
Memory per request128 MBstream; split work into Queues or Cron
One asset file of a server-rendered app25 MiB (static sites are not bound by it: their files are kept in KUMODeck's own storage, up to 100 MB each, see Hosting)compress (.ogg, .webm), split, or keep it in R2 and load it
Variables + secretsabout 124 per environment together (Cloudflare allows 128 per Worker, variables and secrets counted together; KUMODeck's own KUMO_ values use up to 4). 5 KB each (bytes: characters outside basic Latin, for example é or 漢, take 2–4 bytes); names up to 64 characterskeep one JSON secret instead of many small ones; keep large values in R2 or the database and only a key in the secret
D1 (SQLite)one SQL statement 100 KB; 100 bound parameters; 100 columns per table; one row 2 MB; one database 10 GB — Cloudflare cannot raise it, on your own account eitherinsert in several statements (db.batch); keep blobs in R2 and the key in the row; as a database nears 10 GB, move tables (or users) into more databases
Logskept 7 days (kumodeck logs)write what must last to your own database
One multiplayer room (one Durable Object)about 1,000 requests a second; one stored value of at most 2 MB (the room's shared state is one value)rule of thumb: players × messages per player per second ≤ about 1,000 (30 players at 30 a second, 100 players at 10). Every message goes to every player, so the work grows with players²: split big crowds into several rooms; keep shared state small
Your own live object (Functions, one Durable Object)about 1,000 requests a second each (every message a user sends counts); 32,768 WebSockets; one message 32 MiB; one stored value 2 MB; 10 GB of storage each; CPU 30 seconds a request by default, up to 5 minutesone object per room, user or channel (never one for the whole app); players × messages a second ≤ about 1,000; store big state as several rows
Durable Object classes500 per Cloudflare account, shared by every app on KUMODeck; up to 100 bindings per environmenta few classes per app: one class serves any number of rooms by name
Direct (P2P) roomsevery player sends to every other player, so a few players only (p2p.maxPlayers)use a server room for more players
QR invite code2,953 bytes (the QR standard)share the link instead of packing data into it
Numbers stored by KUMODeck (stats, quantities)integers up to 10¹² (JavaScript's safe range)store larger values as text in your own database
Custom domains (Cloudflare for SaaS)50,000 per Cloudflare account; on KUMODeck one account is shared by every app (409 custom_domain_capacity when it is full)keep your app on its KUMODeck URL meanwhile and try again later; remove domains you no longer use
Sign in with Apple inside the iOS appiOS 17.4 or laterolder iPhones sign in on the web page

Every other limit#

Other sizes and counts (save slots and their size, room size and message size, files per deploy, config counts, list lengths) were chosen by KUMODeck and are being raised to Cloudflare's level, so they are not written here.

  1. Do not build for a smaller limit than you hit. Do not pre-split data or pre-shrink assets "just in case".
  2. When refused, read details.limit and fit to it: split data over more slots or rows, page through lists, put large files in R2, split a room.
  3. Functions CPU time and subrequests: kumodeck functions status shows the current values and kumodeck functions limits --cpu-ms <n> --subrequests <n> changes them; above the maximum, details.issues names it. Add --app for a server-rendered app instead of Functions. The CPU time that runs is also capped by the prepaid balance (more balance, longer CPU time, up to Cloudflare's maximum): when the status says "you chose … ms", the balance it needs is requiredBalance in --json, and kumodeck billing topup adds funds. Code that needs the range first reads limitRanges ({ min, max, default } for cpuMs and subRequests) from GET …/functions. Heavy work that does not fit belongs in Queues or Cron.
  4. The range of one top-up is topUpLimits ({ min, max } in cents) in GET /v1/money/prepaid. Do not copy dollar amounts into a page or a script.
  5. Long lists page: when a response has nextCursor, pass it as cursor for the next page (Functions deployments, the prepaid ledger, …). The first page is not everything. A changed, cut-off or other list's cursor is 400 invalid_cursor on every list (details.cursor): start again without it.

Limits in the templates are your app's own#

Code from a template or a Skill is your code; its limits are choices, not KUMODeck's rules. Change them freely.

  • web-app: NOTES_PER_USER (notes per user) in functions/src/index.ts. The list pages through all notes (nextCursor).
  • functions-starter: the hourly cron deletes scores older than KEEP_DAYS (30 days). Raise it, or remove scheduled (and the cron in wrangler.jsonc) to keep every score.