Runtime·Gas and JS processing

Runtime

Gas and JS processing

Blue documents may use executable JavaScript-based logic. That power needs boundaries. This page explains the role of JS processing, gas limits, and bounded execution.

Why this matters

Without bounded execution, a document runtime can become expensive, unpredictable, unsafe, and hard to reason about. With bounded execution, you can still write useful logic while keeping the system practical.

What "gas" means here

Gas is a bounded execution budget. It exists so that:

  • one document cannot consume unbounded compute
  • processors can stay predictable
  • pricing can remain sane
  • complex documents stay governable

This is especially important when documents contain JavaScript logic, many events are processed, or agent-generated structures become more common.

Why JS exists at all

Many useful contract behaviors cannot be expressed declaratively without inventing a domain-specific scripting language. Blue lets you embed real JavaScript inside a workflow step (Conversation/JavaScript Code) executed in a sandboxed QuickJS WebAssembly runtime. That gives expressive power without giving up determinism.

What is allowed inside deterministic processing

  • pure computation — math, string manipulation, branching, loops
  • reading the current document via the bound document(...) helper
  • reading the current event via event
  • emitting Conversation/Trigger Event outputs that other contracts in the same document can react to
  • writing state through Conversation/Update Document changesets

What is not allowed

  • network access (no fetch, no sockets)
  • file system access
  • access to system clocks beyond a deterministic timestamp surface
  • random sources (deterministic seeds only)
  • calling external models or LLMs

These are not philosophical restrictions — they are the difference between "the same document and the same events always produce the same state" and "sometimes it does, depending on the network."

Where LLM and external calls live

LLM calls, RPC calls, network requests, and other non-deterministic side effects happen outside the document processing boundary. The pattern is:

  1. An external service does whatever non-deterministic work it needs (LLM call, web fetch, model inference).
  2. The result is wrapped in a Blue message and posted to a timeline as a new entry.
  3. The document processes that entry deterministically.

The non-determinism becomes data. The document never sees uncertainty.

Two micro-examples

Good JS step — derive next status from current state:

javascript
const current = document('/status');
const amount  = event.message.request.amount;

if (current === 'pending' && amount > 0) {
  return { newStatus: 'accepted' };
}
return { newStatus: current };

Bad JS step — would smuggle non-determinism into processing:

javascript
// ❌ Not allowed: network call.
const exchangeRate = await fetch("https://example.com/usd").then(r => r.json());
return { amount: event.message.request.amount * exchangeRate.value };

The fix is to publish the exchange rate to a timeline as data. The document then reads it deterministically.

How gas usage is reported

The processor returns the actual gas it spent. With @blue-labs/document-processor, the value is on the result object as totalGas. The document stepper shows it directly on every epoch — no estimate, no guess.

Do not use JS processing to smuggle an entire hidden application into one document.
Model logic declaratively wherever possible. Save JS for cases where it genuinely cannot be expressed otherwise.