Skip to main content
This reference describes the JavaScript environment that celld, the Judt runtime, gives your App. celld embeds V8 and implements the Cloudflare Workers runtime API. Use this page to look up handlers, the env and ctx objects, imports, web API differences, crypto support, limits, and compatibility flags. For a summary of the differences from Cloudflare, see Differences from Cloudflare Workers.

Entry module

The entry module is the file that main names in the wrangler.json file, for example index.js. It’s a module Worker: it exports a default object with handlers, and it exports each class that the wrangler.json file names.
index.js
The entry module can export only handler objects and classes. It must export each class that the wrangler.json file names, such as a Durable Object class or a Workflow class. If the entry module exports any other value, such as export const x = 1, then the App doesn’t start.

Handlers

The default export can define the following handlers. celld has no tail or email handler. The entry module also exports the classes that celld runs. The following table lists the base classes, which you import from the cloudflare:workers module.

The env object

The env object holds the bindings that you declare in the wrangler.json file, the vars values, and the Secrets. Each binding name is a property: a D1 binding named DB is env.DB. If a Secret and a var have the same name, then the Secret replaces the var. Pass env to your functions as a parameter. Don’t store it in a module-level variable. For more information, see Vars and Secrets.

The ctx object

The ctx object is the third argument of each handler. The following table lists its members. In a Durable Object, this.ctx.exports gives the same stubs. A Durable Object doesn’t need ctx.waitUntil, because pending I/O stays alive after the handler returns.

The public origin

In production, request.url has the App’s public origin, such as https://<slug>.judt.dev. On the dev server, request.url names the sandbox over http. Judt sends the X-Forwarded-Host and X-Forwarded-Proto headers with the public origin on each request, in preview and in production. Judt replaces any value that the client sent. Use these headers when you build an absolute URL, such as an OAuth redirect, a feed link, or a webhook signature, or when you compare the Origin header:
You can trust these headers only on Judt. Behind a different proxy, a client can set them.

Imports

The entry module can import relative modules, such as ./src/db.js, the cloudflare:* modules in the following table, and the Node.js built-in modules that celld supports. At deploy, esbuild bundles the entry module, its relative imports, and the npm packages that it imports. For more information, see npm packages and builds. The following table lists the cloudflare:* modules that work.

Node.js built-in modules

The following Node.js built-in modules work without the nodejs_compat flag. Node.js built-ins are always on.

Other modules

All other node:* and cloudflare:* modules import without an error, but they throw an error on the first call. An npm package that depends on one of these modules fails when it calls the module. Use web APIs instead.

Web API differences

celld supports the standard web APIs of Workers. The following table lists each API group and how it behaves on celld.

Crypto

The crypto.subtle interface supports the following algorithms:
  • SHA digests
  • HMAC
  • HKDF
  • PBKDF2
  • AES-GCM
  • RSASSA-PKCS1-v1_5
  • RSA-PSS
  • RSA-OAEP
  • ECDSA
  • Ed25519
  • X25519
JSON Web Tokens (JWTs) that use HS256, RS256, PS256, ES256, or EdDSA work.

Crypto limits

The crypto implementation has the following limits:
  • ECDSA supports only the P-256 curve with SHA-256, so ES384 and ES512 don’t work.
  • AES-GCM tags must be 96 to 128 bits long.
  • You can’t export or wrap a secret key in jwk format.
  • The node:crypto module has no ciphers, no Diffie-Hellman, no RSA-PSS, no DSA, and no streaming signatures. Use crypto.subtle for these operations.

Hash passwords

To hash a password, use PBKDF2 with SHA-256, a random 16-byte salt, and at least 100,000 iterations. Store the salt and the iteration count with the hash.

Compare secrets in constant time

To compare a secret, such as a token or a webhook signature, use a comparison that takes the same time for every input. The following function signs both values with a random HMAC key and compares the results:
To verify an inbound webhook, compute the HMAC of the request body, and compare it with the signature by using safeEqual.

Limits

The following table lists the runtime limits. Don’t read large bodies into memory. Stream them instead. Each binding has its own limits. For more information, see the page for that binding.

Outbound calls

Keep the API keys for outbound calls in Secrets, and read them from env, for example env.ANTHROPIC_API_KEY. For more information, see Vars and Secrets.

Set a timeout

Set a timeout on each outbound call:

Compatibility flags

Keep the compatibility_date value of your App. celld obeys only the following compatibility flags and their off flags:
  • assets_navigation_prefers_asset_serving
  • delete_all_deletes_alarm
  • fetcher_no_get_put_delete
  • js_rpc
  • queue_json_messages
  • sqlite_vec
  • websocket_standard_binary_type
celld accepts all other flags in the compatibility_flags list without effect. For the effect of each flag, its off flag, and the date from which it’s on by default, see Compatibility settings. You don’t need the nodejs_compat flag, because Node.js built-in modules are always on.

Patterns

Route requests without a dependency

You can route requests with URLPattern and no framework. The following entry module matches each request against a list of routes, and catches errors at the top level:
index.js
The getLink and createLink functions are your route handlers. Each one receives the request, env, ctx, and the named groups from the path.

Handle input and errors

We recommend the following practices for each route:
  • Validate all input. Check the type, the length, and the format of each value.
  • Catch errors at the top level, and write them with console.error. Logs from console.* and uncaught exceptions go to the App logs.
  • Escape user text before you put it into HTML.