JavaScript Runtime Options

We use QuickJS JavaScript Engine for JavaScript execution. QuickJS runtime options can be updated with a proposal. A sample proposal would look like:

{
  "actions": [
    {
      "name": "set_js_runtime_options",
      "args": {
        "max_heap_bytes": 1024,
        "max_stack_bytes": 1024,
        "max_execution_time_ms": 1200,
        "log_exception_details": false,
        "return_exception_details": false
      }
    }
  ]
}

Default values are max_heap_bytes = 100 * 1024 * 1024, max_stack_bytes = 1024 * 1024, max_execution_time_ms = 5000, log_exception_details = false, return_exception_details = false, and max_cached_interpreters = 10.

For application requests, these limits cover loading and initialising the entry module and its imports, calling the endpoint handler, nested JavaScript execution such as eval() and the Function constructor, and any JavaScript invoked while converting the handler’s response.

The same limits apply to governance JavaScript evaluation, including loading and initialising the module that contains each member ballot’s vote function and the constitution’s validate, resolve and apply functions, as well as the subsequent calls into those functions. Governance evaluation applies the configured js_runtime_options with a NO_LOWER_THAN_DEFAULTS floor so that operator-provided limits cannot be lowered below the built-in defaults for governance code.

The execution time limit is enforced by QuickJS interrupt checks while it executes bytecode. QuickJS does not perform these interrupt checks while parsing or compiling JavaScript source. Consequently, source compilation, including compilation performed by eval() or the Function constructor, is constrained by the heap and stack limits but not by max_execution_time_ms. CCF precompiles application modules when installing an application by default, so this exception normally applies at application installation rather than while handling a request. Disabling the bytecode cache causes module source compilation to occur while loading the first request in each interpreter, where the same exception applies.

The heap limit includes memory already allocated by the runtime, not just allocations made by the current request. Lowering it below current usage prevents further nonzero allocations, including reuse of existing arena slots. Uncaught out-of-memory errors are reported as request failures; constructing their JavaScript stack traces must not terminate the node.