blue-bex-java is a compiled Java engine for Blue Expression Objects
(BEX): a deterministic scripting model written as Blue data.
BEX is for logic that should live inside Blue documents instead of host code. A BEX program can read a Blue document view, host-provided bindings, constants, variables, and prior results, then compute structured Blue-compatible data. Because BEX programs are Blue data, they can participate in the same content identity and BlueId model as the documents that carry them.
BEX is not JavaScript, WASM, a network runtime, an LLM extension point, or a
contract processor. It does not mutate documents or perform external actions.
It computes a deterministic BexExecutionResult; the host decides how to use
that result.
BEX programs are Blue object trees. An object with exactly one key beginning
with $ is an operator. Objects with normal keys are literal Blue-compatible
objects. Lists and scalar values are literal values unless nested inside an
operator body.
BEX is useful when a host wants deterministic, document-owned logic for policy checks, projections, validation rules, event payload construction, patch construction, fixture generation, or other Blue-native computation.
BEX keeps logic close to the data it describes. That matters when logic should be versioned, hashed, inspected, signed, transported, or reproduced as Blue data instead of hidden in host application code.
The runtime is intentionally small and deterministic: no time, random, network, filesystem, JavaScript, WASM, or arbitrary host callbacks.
do:
- $let:
name: status
expr:
$document: /status
- $let:
name: limit
expr:
$integer:
$binding:
name: policy
path: /maxAmount
- $return:
approved:
$and:
- $eq:
- $var: status
- active
- $gte:
- $var: limit
- 1000
message:
$concat:
- "Status is "
- $var: statusWith /status = active and a policy binding containing
/maxAmount = 1000, the result value is:
approved: true
message: Status is activeFrozenNode programNode = FrozenNode.fromResolvedNode(program);
FrozenNode documentNode = FrozenNode.fromResolvedNode(document);
Map<String, Object> policyMap = new LinkedHashMap<>();
policyMap.put("maxAmount", 1000);
BexExecutionContext context = BexExecutionContext.builder()
.document(new FrozenBexDocumentView(documentNode))
.binding("policy", BexValues.fromSimple(policyMap))
.binding("event", BexValues.nodeSnapshot(eventNode))
.gasLimit(100_000)
.build();
BexExecutionResult result = BexEngine.builder()
.build()
.compileAndExecute(BexProgramSource.inline(programNode), context);Programs that use shared functions/constants can use a definition node:
BexProgramSource source = BexProgramSource.withDefinition(
programNode,
definitionNode,
"entryFunction"
);BexDocumentView owns document pointer resolution, canonical reads, resolved
reads, and the current scope path.
FrozenBexDocumentView view = new FrozenBexDocumentView(
canonicalRoot,
resolvedRoot,
"/orders/123"
);$document reads the canonical view by default:
$document: /statusResolved view reads are explicit:
$document:
path: /status
view: resolvedHosts can provide arbitrary named bindings:
BexExecutionContext.builder()
.document(view)
.binding("policy", policyValue)
.binding("actor", actorValue)
.binding("event", eventValue)
.build();Use $binding for arbitrary host bindings:
$binding:
name: policy
path: /maxAmountShort form is supported:
$binding: actor/nameHosts often provide common bindings such as event, steps, and
currentContract. BEX includes shortcuts for those common names:
$event;$steps;$currentContract.
For every other host binding, use $binding.
BEX operators are Blue objects whose single key starts with $.
| Operator | Purpose |
|---|---|
$document |
Read from the Blue document view. |
$binding |
Read a host-provided runtime binding. |
$event |
Shortcut for the common event binding. |
$steps |
Shortcut for prior step/result bindings when a host provides them. |
$currentContract |
Shortcut for a host-provided current contract binding. |
$var |
Read a local variable. |
$const |
Read a program constant. |
$get |
Read an object field by key. |
| Operator | Purpose |
|---|---|
$unwrap |
Unwrap Blue scalar wrapper values. |
$text |
Convert to text. |
$integer |
Convert to exact integer. |
$number |
Convert to exact decimal/number. |
$boolean |
Convert to boolean. |
$object |
Require an object, or default undefined to an empty object. |
$list |
Require a list, or default undefined to an empty list. |
| Operator | Purpose |
|---|---|
$concat |
Concatenate text. |
$join |
Join list items with a separator. |
$split |
Split text. |
$startsWith |
Check a prefix. |
$sliceAfter |
Return text after a prefix. |
| Operator | Purpose |
|---|---|
$eq, $ne |
Equality and inequality. |
$gt, $gte, $lt, $lte |
Numeric comparisons. |
$and, $or, $not |
Boolean logic with short-circuiting. |
$truthy, $empty |
Truthiness checks. |
$coalesce |
First non-empty value. |
| Operator | Purpose |
|---|---|
$add |
Add exact integers. |
$subtract |
Subtract exact integers. |
$multiply |
Multiply exact integers. |
$divide |
Divide exact integers; non-exact division fails. |
| Operator | Purpose |
|---|---|
$keys |
Sorted object keys. |
$entries |
Sorted object entries as { key, val }. |
$size |
Size of a list, object, or scalar value. |
$listGet |
Read a list item. |
$listConcat |
Concatenate lists. |
$merge |
Shallow object merge. |
$objectSet |
Set a dynamic object key without mutating the input. |
$pointerGet |
Read by JSON Pointer from any value. |
$pointerSet |
Return a value with a JSON Pointer update. |
| Operator | Purpose |
|---|---|
$changeset |
Return accumulated patch entries. |
$events |
Return accumulated event/data entries. |
$resultValue |
Read the document value implied by accumulated changes. |
| Operator | Purpose |
|---|---|
$choose |
Conditional expression. |
$call |
Call a local function. |
$literal |
Return payload without compiling nested operators. |
| Statement | Purpose |
|---|---|
$let |
Define or initialize a local variable. |
$set |
Update an existing local variable. |
$if |
Conditional branch. |
$forEach |
Iterate list items or object entries. |
$appendChange |
Append a patch entry to the result changeset. |
$appendChanges |
Append many patch entries. |
$appendEvent |
Append an event/data value. |
$appendEvents |
Append many event/data values. |
$call |
Call a local function for side effects and return handling. |
$return |
Return the result value. |
$fail |
Fail deterministically. |
BexExecutionResult contains:
value, the primary return value;changeset, the standard patch accumulator;events, the standard event/data accumulator;gasUsed;metrics.
BEX computes these values only. The host decides whether patches are applied, events are emitted, or accumulators are treated as ordinary data.
BEX execution is deterministic for a fixed program, context, document view, bindings, gas schedule, and immutable host boundary values.
Use BexValues.nodeSnapshot(node) for untrusted mutable Node values. Use
BexValues.nodeCursorTrustedImmutable(node) only when the host can guarantee
the node will not be mutated during execution.
The engine is compiled-first:
- selected programs compile lazily;
- compiled programs are cacheable by stable Blue node identity and entry name;
- variables use slot frames;
- static pointers are parsed at compile time;
- document and binding reads use cursor-backed values where possible;
$objectSetand$pointerSetuse overlay values;$resultValueuses an indexed overlay;- output conversion to
Node,FrozenNode, or simple Java values is explicit.
Every result includes BexMetrics:
BexMetrics metrics = result.metrics();
metrics.compiledExecutions();
metrics.compileCacheHits();
metrics.frozenDocumentReads();
metrics.nodeMaterializations();./gradlew test
./gradlew buildMIT. See LICENSE.