celld implements the Cloudflare Workers APIs that this page links to. The notes under each API list only the celld gaps and differences.
- Yes means that an application that uses the API runs on celld. The notes list each difference from Cloudflare and each method that is not available.
- Partial means that a large part of the API is missing, so an application that depends on that part does not run.
- Experimental means that the API needs an opt-in setting, and it can change without notice.
- No means that celld does not implement the API.
celld must reject an unsupported configuration or API at deployment or first use. An unsupported feature that does not cause an error is a defect. This page identifies each known exception.
| service | status |
|---|---|
| Workers | Yes |
| Durable Objects | Yes |
| Durable Object Facets | Experimental |
| Static assets | Yes |
| Cron Triggers | Yes |
| Dynamic Workers | Experimental |
| KV | Yes |
| Queues | Yes |
| D1 | Yes |
| Workflows | Yes |
| R2 | Yes |
| Workers AI | No |
| Vectorize | No |
| Hyperdrive | No |
| Browser Rendering | No |
| Email Workers | No |
| Python Workers | No |
- The runtime API table lists the Worker runtime gaps.
- celld does not manage a custom domain or terminate TLS. Terminate TLS in the ingress proxy.
- An outbound
fetchaccepts anAbortSignalin its options. An abort rejects the call with the reason of the signal, and it cancels the request that is in flight, so the destination gets a disconnect. celld reads only a signal that the caller supplies. An incoming request carries its own signal for the disconnect of the client, and a subrequest does not inherit it. - celld does not host a model, so Workers AI is not available. An experimental
HTTP adapter lets an application that calls
env.AI.run()deploy unchanged: withCELLD_AI_URLset, therunmethod posts the model and the input as JSON to that URL. Therunmethod takes a third options argument.returnRawResponse: truegives back the unconsumed upstreamResponse, so an application can read a streaming completion chunk by chunk. The raw option gives back theResponsefor an error status too, so the application must testokorstatusitself. The default parses the upstream body as JSON, and it throws anErrorfor a status that is not 2xx. The message of thatErrorgives the status and the first 512 characters of the upstream body, so an application can see the cause. Asignaloption cancels the request through the samefetchmechanism, and it also cancels a response body that the application is reading.
- An RPC stub cannot cross an isolate boundary. See RPC.
- An outbound WebSocket does not continue after the object moves to another node.
- celld refuses invalid UTF-8 from a SQLite
TEXTvalue. Store arbitrary bytes in aBLOB. SqlStorage.Cursor.toArray()gives a celld-specific error when the isolate is near its V8 heap limit.storage.sync()resolves only after the object store or the fleet ensemble holds every write that the object committed before the call. The promise rejects when celld cannot prove the writes durable before the shorter of the durability budget (CELLD_LTX_DURABILITY_TIMEOUT_SECS, default: 10 seconds, counted from the start of the write's upload) andCELLD_OPERATION_DEADLINE_MS(default: 15000 milliseconds). celld then resets the object and restores the proven history. The reset can stop the object before the handler observes the rejection, so an application must not depend on the rejection to recover.storage.sync()rejects while atransaction()is open on the object, because the transaction is not committed and no proof can cover its writes. Callsync()after the transaction commits. Async()promise that atransactionSync()callback creates settles after the callback returns, so it covers the committed transaction. If the callback rolls back, an existingsync()promise resolves after the rollback without preserving canceled writes.storage.sync()rejects afterctx.abort()or a failedblockConcurrencyWhile()aborted the object.- A transaction handle rejects new storage operations after the transaction
commits or rolls back. Calling
rollback()again after a rollback has no effect. A rollback also cancels writes submitted without anawaitinside that transaction. A nested rollback preserves the outer transaction's writes. - A callback that finishes after an object abort cannot release a newer critical section's input gate or stop the node with a stale gate release.
- A
transaction()callback runs under the input gate, as in Workerd: no other event starts in the object until the callback returns. celld does not hold an event that was already waiting on a subrequest or a timer when the callback started. That event resumes during the callback, and its writes join the open transaction. A callback that runs for more than 30 seconds resets the object and rolls the transaction back, because the limit ofblockConcurrencyWhile()applies to it. A callback that throws rolls the transaction back and rejects, and the object continues. A block that starts nested blocks and does not await them holds the gate until they settle, each within its own 30 seconds, as nested critical sections do in Workerd. - A
blockConcurrencyWhile()callback continues after the client of its request disconnects. The object serves the next event when the callback settles. - With
CELLD_OUTPUT_GATE=0, or with no object store configured,storage.sync()resolves after the local commit and does not wait for a durability proof. - A handler that fails after it writes answers only after the write is
durable. celld holds the error behind the same output gate as a success,
because the write is committed and a later request can read it. This rule
applies to a
fetchor RPC handler that throws or returns an invalid response, to awebSocketMessage,webSocketOpen, orwebSocketClosehandler, and to analarmhandler. When celld cannot prove the write durable before the shorter ofCELLD_LTX_DURABILITY_TIMEOUT_SECSandCELLD_OPERATION_DEADLINE_MS, the request fails with a durability error and celld resets the object. WithCELLD_OUTPUT_GATE=0, the error leaves after the local commit. - A handler that fails without a write of its own answers only after every write that the object holds is durable. The message of a handler that throws can carry a value that the handler read, so celld holds the error as it holds a read-only response. A failure that celld raises, and not the handler — a handler over its time budget, or a handler that waits on nothing — carries no value from the object, so it leaves at once.
- celld does not compress an asset response. Put a compressing proxy in front of the fleet when a client needs gzip or brotli.
- celld has no edge cache. A node downloads an asset from the bucket on the
first request, and it keeps a local disk cache of 512 MiB
(
CELLD_ASSET_CACHE_BYTES). Each response carries anetagandcache-control: public, max-age=0, must-revalidate, so a browser revalidates withIf-None-Matchand gets304. - A node that has not restarted after a new deployment serves the new deployment's content-hashed assets. celld checks the deployment pointer after an asset miss, at most one time in 5 seconds.
- A
_headersfile cannot set or removeconnection,content-length, ortransfer-encoding. - celld enforces the Cloudflare rule limits at deployment, so a
_headersfile with more than 100 rules or a_redirectsfile with more than 2,000 static or 100 dynamic rules stops the deploy. Each directive file has a 100 KiB limit. celld deploydoes not read.assetsignore, and a project that contains one must deploy with Wrangler. The command refuses a symbolic link, a special file, a non-UTF-8 name, a path above 1024 bytes, a path that changes under percent-decoding, and a_worker.jsentry.- A deployment can contain at most 20,000 assets, 25 MiB for each asset, and 1 GiB in total.
- celld rejects a descending range such as
SAT-SUNorNOV-FEB. - celld rejects
*inside a list such as1,*. - celld runs one handler for each occurrence across the complete fleet.
- After fleet downtime, celld runs the most recent missed occurrence one time.
- celld runs one handler at a time for each script. It retries a failed handler
until the next occurrence unless the handler calls
noRetry(). - A service-binding target cannot run its own Cron Triggers.
- Dynamic Workers are experimental.
CELLD_WORKER_LOADER=LOADERexposes the Worker Loader binding atenv.LOADER. getDurableObjectClass()returns a class for a Durable Object facet.- A call into a loaded Worker from a Durable Object waits for a durability proof of the object's writes, because the call carries the object's state. A service-binding call waits in the same way.
- A
globalOutboundFetcher is not available. - A loaded Worker cannot receive a capability stub in
env. - Awaitable and pipelined properties are not available.
- Facets are experimental, and they require Dynamic Workers.
ctx.facetssupportsget(),abort(), anddelete()for a class from the Worker Loader binding. - A facet has an isolated SQLite database, and celld replicates this database with the root Durable Object.
- A facet writes into the database of the root Durable Object, so an outbound
effect from inside a facet waits for the durability proof of that object.
storage.sync()inside a facet makes the same statement about that object. - A facet class from
ctx.exportsor a Durable Object binding is not available. Theclone()method is also not available. - A
propsvalue forgetDurableObjectClass()must be JSON-serializable.
- celld has no edge cache.
cacheTtlhas no effect, andcacheStatusisnull. - A value above 1 MiB requires a fleet bucket.
- celld stores a separate object when an application writes an identical large value after a namespace changes owners. The separate object prevents an old owner from deleting the current value.
- celld reads a large-value row from an older release, but it does not reclaim the legacy object after the row is removed.
- A namespace has one writer. Use more namespaces to increase write capacity.
- A namespace ID can use the Cloudflare hexadecimal form or another stable string.
- A queue has one writer. Use more queues to increase write capacity.
- A Queue owner admits at most 256 concurrent producer calls. It commits concurrent calls in shared transactions, and each reply still waits for its own durability proof. This bound lets alarms and settlements run, and it limits the message bodies that wait for durability. The owner refuses an additional call, and the producer can retry it.
- A queue can have one consumer script. The consumer cannot also export a
fetch()handler. - A message id is a UUID version 7. The first 48 bits hold the enqueue time in milliseconds, so the ids of one Queue owner sort in enqueue order.
- celld retains a message for four days. You cannot configure this period.
- Pull consumers and the Queues HTTP API are not available.
- Dashboard controls, manual consumer attachment, R2 event notifications, and Queue event subscriptions are not available.
- A binding result can contain at most 100,000 rows or 32 MiB.
- celld refuses invalid UTF-8 from a SQLite
TEXTvalue. Store arbitrary bytes in aBLOB.
create()replaces a terminal instance that has the same ID. Cloudflare refuses each duplicate ID.- celld replays
run()from the start, so code outside a step runs again. - A crash after a step side effect can run the step callback again.
retentionandlocationHintare not available.- Non-step work cannot remain pending for more than 60 seconds.
- A step result, an event payload, and the workflow parameters each have a 1 MiB limit.
pause()stops a queued or waiting instance immediately. It lets an active step finish, and the status changes fromwaitingForPausetopausedbefore the next step starts.resume()starts a paused instance and cancels a pending pause. A paused retry, sleep, or event wait keeps its remaining wait duration.restart()starts a new generation from the beginning by default. Itsfromoption can select a step by itsname,count, andtype. Thecountvalue defaults to1, and thetypevalue defaults todo.- A selected restart reuses each result before the selected step and reruns that step and each later step. The runtime rejects a selector that does not match the execution history.
delete(),deleteBatch(), and rollback are not available.- A sensitive step result and a
ReadableStreamstep result are not available.
- An R2 binding uses the fleet bucket under
r2/<bucket_name>/. - The
versionof an object equals its content ETag, so two uploads of identical bytes to one key return one version. ssecKeyis not available.- A conditional write cannot use a streamed body larger than 8 MiB.
createMultipartUpload()does not accept a checksum.- A multipart upload cannot resume on another node or after a restart.
- celld cannot replace a multipart part that the object store already holds.
- Out-of-order parts can use at most 256 MiB of memory, and completion cannot change the order of stored parts.
jurisdictionis not available.
| API | status |
|---|---|
| Fetch, Request, Response, and Headers | Yes |
| Bindings | Yes |
| Context | Yes |
| Handlers | Yes |
| RPC | Yes |
| Streams | Yes |
| Encoding | Yes |
| WebSockets | Yes |
| Web Crypto | Yes |
| Web standards | Yes |
| WebAssembly | Yes |
| Performance and timers | Yes |
| Console | Yes |
| Node.js compatibility | Partial |
| Cache | Partial |
| HTMLRewriter | Yes |
| TCP sockets | Yes |
| EventSource | Yes |
| MessageChannel | Yes |
| BroadcastChannel | No |
- The
cacherequest option is not available. - The
redirectoption acceptsfollow,manual, anderror. Theerrorvalue rejects a redirect with aTypeErrorbefore celld sends a request to the destination. celld rejects each other value. - celld removes
Content-Lengthfrom a Worker response. It preserves the header for aHEADresponse. - A remote Durable Object call streams the request body to the owner. The call cannot retry after body transmission starts because celld keeps no replay copy.
- A remote Durable Object call waits for a rejected owner generation to change.
The wait cannot exceed
CELLD_OPERATION_DEADLINE_MS.
- The services table lists the available binding types.
- celld supports Durable Objects, services, variables, assets, D1, KV, Queues, Workflows, and R2 bindings. Each other binding type is not available.
passThroughOnException()has no effect because celld has no CDN fallback.ctx.facetsis available only inside a Durable Object.
- The
tailandemailhandlers are not available.
- A cross-isolate named service binding supports only a single method call. It
does not support
fetch(), awaitable properties, or pipelined paths. - An RPC stub cannot cross an isolate boundary.
ctx.exportscontains only the entrypoints that the configuration declares.- A remote RPC retries only when the failed peer attempt did not start the method. An application must use a stable operation ID for another retry.
- celld expires an unclaimed and inactive HTTP stream after 60 seconds. A successful stream operation starts a new 60-second period.
- A natural stream end or the explicit cancellation of an active read produces EOF. A read from an expired or unknown stream produces an error.
- A response body tee uses one bounded buffer for each branch, so the slowest live branch controls the source backpressure. celld cancels the source after both branches close.
- A caller must call
accept()on the socket from a subrequest upgrade. - An outbound Worker socket closes after the response and
waitUntilwork end. - A Worker socket that the response returns stays open after the response. The response takes ownership of the socket, so the end of the request cannot close it.
- celld applies a 1 MiB byte budget to the non-terminal frames in each isolate-polled WebSocket input queue. The budget includes the message data and a fixed charge for each frame.
- The queue reserves one terminal close outside this budget. Therefore, a full queue cannot prevent celld from releasing the host socket task.
- If the isolate does not continue to poll, celld discards its unread frames during socket cleanup. A later pull reports an abnormal close.
- A message larger than 1 MiB uses the complete queue budget. Therefore, celld waits for the Worker to consume that message before it reads another message.
- celld rejects an upgrade when the response status is not 101.
- celld removes Worker-supplied protocol and connection headers from an upgrade response.
- An outbound upgrade combines repeated values for one header name.
- celld rejects a close frame from a client when its status code is not valid
for the WebSocket protocol. The close event has code
1002, andwasCleanisfalse. - A WebSocket transport cannot move to a new cell owner. A client must reconnect and keep the same application operation ID.
acceptWebSocket()throws when the isolate uses more than 90 percent of its V8 heap limit.
- HMAC signing and verification accept MD5, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512.
- ECDSA signatures support only the P-256 curve and the SHA-256 hash.
- AES-GCM supports authentication tags from 96 through 128 bits in 8-bit
increments. It returns an
OperationErrorfor every other tag length. - RSA-OAEP supports SHA-1, SHA-256, SHA-384, and SHA-512. A nonempty label must contain valid UTF-8 bytes.
- A secret key cannot use the
jwkformat inexportKey()orwrapKey().
- An
AbortSignaldoes not abort an RPC call. signal.onaborthas no effect. UseaddEventListener("abort", ...).structuredClone()does not clone anAbortSignal.
performance.timeOrigin is 0, and performance.now() matches Date.now().
Both clocks advance at an I/O boundary and stay fixed during JavaScript
execution.
- celld implements
node:assert,node:async_hooks,node:buffer,node:diagnostics_channel,node:events,node:fs,node:os,node:path,node:stream,node:timers/promises, andnode:util. node:diagnostics_channelimplements named channels, subscriptions,AsyncLocalStoragebinding, and tracing helpers. It does not export channel messages to a tail Worker.node:osreturns the deterministic operating-system values that Workerd returns. Itstmpdir()andhomedir()functions return/tmp/.node:cryptodoes not implement Diffie-Hellman, streaming signatures, ciphers, RSA-PSS, or DSA signatures and key generation.node:zlibimplements only the synchronous gzip and deflate functions.node:fsprovides an empty, memory-backed/tmpfor each request. The promise, callback, and synchronous APIs implementaccess,mkdir,realpath,stat,lstat, andreadFile.- A callback runs after the current turn, therefore an application cannot observe a result before its call returns. celld reports a filesystem error through the callback, but throws an argument error, because a bad argument can make the callback unusable.
- These APIs accept a string, a
Buffer, or a fileURL. ThestatandlstatAPIs support bigint metadata. fs.constantsgives the four access modes. TheaccessAPI reports what the filesystem permits and not a POSIX permission, so a check forX_OKalways fails, because no file is executable. A check forW_OKon a read-only path fails with theENOENTerror that a missing path also gives, therefore an application cannot tell the two apart.node:fsalso provides a read-only/bundledirectory. It contains one file for each module in the Worker bundle, so an application can read its own source. The entry module is/bundle/worker.js.- A module name is a path, so a module named
a/b.jsbecomes the file/bundle/a/b.jsand celld creates the directory/bundle/a. - A
/bundlefile contains the raw bytes of the module, therefore a read returns the source text and not a compiled module. Each attempt to change/bundlethrowsEPERM. - The temporary filesystem does not contain file data. A read of a
/tmppath returnsENOENT, and each unsupported filesystem call throws an error. - The celld bundler supports a synchronous CommonJS
require()of a Node.js built-in module. A raw ESM Worker does not receive a globalrequire(). - An import of each other Node.js module succeeds, and the first call into the module throws an error.
- celld has no shared edge cache, so the Cache API cannot serve a stored
response.
caches.defaultandcaches.open()give a cache with always-miss semantics. put()validates its arguments and reads the response body to completion, and it stores nothing.match()givesundefined, anddelete()givesfalse.
- A socket cannot outlive the event that created it. celld closes the socket when the event ends, so a Durable Object must reconnect in a later event.
- celld verifies a TLS server against the bundled Mozilla root store.
- Cloudflare blocks some destination ports, and celld does not block a port. The operator controls the egress policy of the fleet network.
- Cloudflare Workers does not provide BroadcastChannel, and celld does not provide it. The class is defined, so a bundle can reference it at load time, and the constructor throws an error.
celld honors these compatibility switches:
delete_all_deletes_alarmjs_rpcfetcher_no_get_put_deletesqlite_vecwebsocket_standard_binary_type- The static-assets navigation flags
celld accepts each other compatibility flag without effect.
Cloudflare.compatibilityFlags reports only the flags that celld honors.
celld deploy accepts wrangler.jsonc or wrangler.json. It does not
accept wrangler.toml.
The name value must contain 1 to 63 lowercase ASCII letters, digits, or
internal hyphens. The value must not start or end with a hyphen.
The deployment accepts these top-level keys:
$schema,name,main, andno_bundlecompatibility_dateandcompatibility_flagsdurable_objectsandmigrationsassets,services,triggers, andvarsd1_databases,kv_namespaces,queues,workflows, andr2_buckets
Each other top-level key, including routes, stops the deployment.
An asset-only project can omit main. celld refuses a symlink or special file
in an asset directory. The native deploy command refuses an asset path that
does not remain unchanged after UTF-8 percent-decoding, and .assetsignore
requires Wrangler.
See Limitations for the operating-system, networking, security, pressure, and update boundaries.