Guides

Isolated-Mode Developer Guide for miniApps JavaScript Snippets

Isolated-Mode Developer Guide

A reference for developers writing miniApps JavaScript snippets that run on the isolated execution path (isolated-vm). It describes the runtime your code sees every global and library injected into it and the exact subset of each that is supported.

This guide covers the isolated path only (the default for all scripts). It is the runtime your code should target. The legacy in-process path is being retired and is not documented here.

The execution model

Each request runs your script in a fresh V8 context inside a shared isolated-vm isolate. Key consequences:

  • Bare V8, not Node. The context has standard ECMAScript built-ins (JSON, Math, Date, String, Array, Object, RegExp, Promise, Map, Set, etc.) but none of Node's runtime. There is no process, no fs, no http/net, no setTimeout/setInterval, no fetch, and no native Buffer or crypto. The capabilities below are explicitly injected.

  • Isolation per request. Globals you create do not leak across requests, the context is created fresh and released after every call. You cannot share state between invocations.

  • Memory limit. The isolate is capped (default 128 MB, ISOLATE_MEMORY_LIMIT). Exceeding it terminates execution.

  • Time limit. Your script must finish within TIMEOUT_MS − 100 ms (default 2900 ms, floor 100 ms). Long loops are killed.

  • No module system. require(...) is a curated shim, not Node's loader. Only four module names resolve; everything else throws.

Returning a result

There are two ways to produce the result returned to the caller:

  • ocpReturn(value)
    Call the injected ocpReturn function with the value you want returned.

JavaScript
ocpReturn("Hello, " + params.name);
  • Completion value (no ocpReturn)
    If your script never calls ocpReturn, the value of the last evaluated expression is returned.

JavaScript
const greeting = "Hi " + params.name;
greeting;            // <-- this becomes the result

Injected globals

These are available directly, without require:

Global

Type

Purpose

params

object

Request input - the params object from the request body.

ocpvars

object

Request input - the ocpvars object from the request body.

ocpReturn(value)

function

Sets the result returned to the caller.

print(...args)

function

Appends to the response logs.

console.*

object

log/info/warn/error/debug/trace all map to print.

Buffer

constructor

Minimal Buffer shim

btoa(str)

function

latin1 string → base64. Equivalent to: Buffer.from(str,'latin1').toString('base64');

Throws on char codes > 255.

atob(b64)

function

base64 → latin1 string. Equivalent to: Buffer.from(b64,'base64').toString('latin1').

crypto

object

Host-backed crypto facade (also via require)

parseXML(xml, options?)

function

XML → object parser

signJwt(payload, key, options?)

function

Signs a JWT

Handlebars

object

The Handlebars library (also via require).

eta

object

The Eta library (also via require).

Handlebars / eta are loaded on demand.

To keep per-request cost down, the runtime injects these two libraries only when it statically detects that your script uses them by a plain Handlebars / eta reference or by require('handlebars'|'eta').

Reaching them indirectly leaves the library undefined: globalThis['Handlebars'], global.Handlebars, this.eta, or a dynamic require(variableName) are not detected. Always reference them by their plain name or via a string-literal require.

  • params and ocpvars
    Read-only inputs copied in from the request. Treat them as data; mutating them has no effect outside your script.

  • print(...) and console.*
    Everything passed to print (and to any console method) is collected and returned in the response logs field. Use it for debugging and tracing. It does not affect the result.

JavaScript
print("debug:", params.id);
console.warn("careful", { value: 42 });

require(...) - the module shim

require resolves exactly four names. Any other argument throws Cannot find module '<name>'.

Names

Returns

Notes

require('handlebars')

Handlebars 4.7.9

Full library. Also the Handlebars global.

require('eta')

Eta 3.4.0

Full library. Also the eta global.

require('crypto')

crypto facade

Host-backed subset.

require('jsonwebtoken')

{ sign }

Only sign is implemented. verify/decode are absent.

JavaScript
const Handlebars = require('handlebars');
const tpl = Handlebars.compile("Hello {{name}}");
ocpReturn(tpl({ name: params.name }));

Buffer (shim)

A minimal pure-JS Buffer. It supports the common encode/decode and crypto plumbing patterns, not the full Node Buffer API.

Supported constructors / statics

API

Notes

Buffer.from(str, encoding?)

encoding defaults to 'utf8'.

Buffer.from(array)

From an array of byte values (0–255).

Buffer.from(uint8array)

From a Uint8Array.

Buffer.from(buffer)

Copy of another shim Buffer.

Buffer.alloc(size, fill?)

Zero-filled (or fill-filled) buffer.

Buffer.concat(list)

Concatenate buffers/byte arrays.

Buffer.isBuffer(x)

True for shim buffers.

Supported instance members

API

Notes

buf.toString(encoding?)

encoding defaults to 'utf8'.

buf.toJSON()

{ type: 'Buffer', data: [...] }.

buf.length

Byte length.

Supported encodings
'utf8'/'utf-8' (default), 'hex', 'base64', 'base64url', 'latin1'/'binary'/'ascii'. Any other encoding throws.
'base64url' is the URL-safe, unpadded base64 variant (RFC 4648). +// become -/_ and trailing = padding is dropped. It works both for encoding (buf.toString('base64url')) and decoding (Buffer.from(str, 'base64url')), and is also accepted as a digest() output encoding by crypto. Useful for OAuth flows (e.g. PKCE code_challenge, DPoP ath).

Not available
buf.slice, buf.write, buf.readUInt*/writeUInt*, buf.indexOf, indexed access (buf[0]), streaming, etc. If you need byte-level manipulation, work with Buffer.from(...).toString('hex') and back.

JavaScript
const b64 = Buffer.from("Grüße", "utf-8").toString("base64");
const back = Buffer.from(b64, "base64").toString("utf-8");   // "Grüße"

A Buffer returned as the result arrives at the caller as { "type": "Buffer", "data": [ ...bytes ] } - identical to Node's Buffer#toJSON()

parseXML(xmlString, options?)

Parses XML to a JavaScript object using fast-xml-parser. XXE is disabled (processEntities: false) and cannot be re-enabled. External/entity expansion is blocked. options are passed through to the parser otherwise.

JavaScript
const obj = parseXML("<root><id>42</id></root>");
ocpReturn(obj.root.id);

JWT signing

Two equivalent entry points, both backed by jsonwebtoken.sign:

JavaScript
// Global
const token = signJwt({ sub: params.userId }, secret, { expiresIn: "1h" });

// Via require - ONLY sign is available
const { sign } = require('jsonwebtoken');
const token2 = sign({ sub: params.userId }, secret, { algorithm: "HS256" });

verify and decode are not provided. Signing keys/secrets must be passed in (e.g. via params/ocpvars); do not hardcode secrets in scripts.

crypto (host-backed facade)

require('crypto') returns a facade that bridges to the host's real Node crypto. Inputs/outputs cross the boundary as hex internally; you use the normal API. The supported surface is driven by real production usage. It is a subset, listed exhaustively below.

crypto is also available as a bare global. crypto.randomUUID() works without require('crypto'), matching Node's global. It is the same facade object either way, so the supported subset below applies identically. Note this global mirrors node:crypto (this facade), not the browser WebCrypto API, so crypto.subtle is not present.

Supported

Hashing

JavaScript
const crypto = require('crypto');
const h = crypto.createHash('sha256').update('data').digest('hex');
  • createHash(algorithm).update(data, inputEncoding?).digest(outputEncoding?)

  • .update(...) is chainable and may be called multiple times.

  • digest() with no encoding returns a Buffer; 'hex'/'base64'/'base64url' etc. return a string.

HMAC

JavaScript
const mac = crypto.createHmac('sha256', key).update(msg).digest('hex');
  • createHmac(algorithm, key) - key may be a string or Buffer.

Random

JavaScript
const bytes = crypto.randomBytes(16);   // returns a Buffer
const id = crypto.randomUUID();          // returns a string

Symmetric ciphers (block modes)

JavaScript
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let out = cipher.update(plaintext, 'utf8', 'hex');
out += cipher.final('hex');
  • createCipheriv(algorithm, key, iv) / createDecipheriv(algorithm, key, iv)

  • Tested block modes: aes-128-ecb, aes-256-cbc (and similar block modes).

  • iv may be null/empty for modes without an IV (e.g. ECB).

  • setAutoPadding(false) is supported.

Buffering note
Input is buffered and the whole operation runs at final(). update() returns an empty chunk ('' or empty Buffer), so the idiomatic out = update(...); out += final(...) produces the correct, byte-identical result. Do not rely on update() returning partial ciphertext.

Signing (one-shot)

JavaScript
const sig = crypto.sign('sha256', Buffer.from(data), {
  key: privatePem,
  padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
  saltLength: 32,
});
  • crypto.sign(algorithm, data, key) - key may be a PEM string or an object
    { key, padding, saltLength }.

  • crypto.constants is available (e.g. RSA_PKCS1_PSS_PADDING).

Not supported

These have no implementation and will fail, design around them:

  • AEAD / GCM modes and authentication tags (getAuthTag/setAuthTag).

  • Streaming createSign(...) / createVerify(...) and signature
    verification of any kind.

  • Key derivation: pbkdf2, pbkdf2Sync, scrypt, hkdf.

  • Key generation: generateKeyPair*, createPrivateKey, etc.

  • getRandomValues (browser API), getCiphers, getHashes, and other
    introspection helpers.

If you need an unsupported primitive, raise it with the platform team rather
than working around it. The facade is intentionally minimal.

Quick reference

Need

Use

Read request input

params, ocpvars

Return a value

ocpReturn(value) or last expression

Log / debug

print(...), console.*

Hash / HMAC / random / cipher / sign

require('crypto')

Encode/decode bytes & base64/base64url/hex

Buffer

Sign a JWT

signJwt(...) or require('jsonwebtoken').sign

Parse XML (XXE-safe)

parseXML(...)

Render a template

require('handlebars') / require('eta')

Common pitfalls

  • Don't expect Node globals (process, fs, setTimeout, fetch), they
    don't exist here.

  • Don't require anything outside the four allowed names.

  • Don't return functions or non-serializable objects.

  • Don't rely on cipher update() returning partial output.

  • Don't expect jsonwebtoken.verify/decode or any crypto verification.

  • Keep work within the time and memory limits.