Core Concepts
The NILScript DSL
If NIL is the syscall interface, the NILScript DSL is the program. A declarative, event-driven language describing multi-step intent over business systems — a JSON DAG of typed nodes that is deliberately not Turing-complete, so unsafe steps are unrepresentable.
The program envelope
Every program carries a fixed set of top-level fields.
| Field | Meaning |
|---|---|
wosool | Language version (“0.1”) |
workspace | Tenant / isolation unit |
locale | BCP 47 tag for previews (e.g. “ar”) |
entry | Node id where execution starts |
pipeline | The array of nodes (the DAG) |
on_error | halt · continue · compensate |
{
"wosool": "0.1",
"workspace": "ws_acme",
"locale": "ar",
"entry": "step_1",
"on_error": "compensate",
"pipeline": [ /* nodes */ ]
}wosool version refuses the program rather than guessing. There is no “best effort” interpretation of an unknown dialect.Node types — a closed set
Eight node types. The validator rejects any node whose type is not one of them — no escape hatch, no custom, no eval.
| Type | Side effects | NIL mapping | Purpose |
|---|---|---|---|
action | Yes (gated) | PROPOSE → COMMIT | Invoke a verb |
query | No | QUERY | Read state |
condition | No | — | Boolean guard, routes only |
parallel | Via branches | — | Fan out branches |
foreach | Via body | — | Loop over a bounded array |
await_approval | No | waits for DECIDE | Block for owner decision |
notify | No | — | User-facing message |
wait | No | — | Durable pause until a time |
action & compensation
One node, one verb. args are hints; the system resolves the authoritative facts. An action may carry compensate_with — a compensating verb the runtime runs to undo the step if a later step fails.
{
"id": "step_3",
"type": "action",
"verb": "commerce.create_purchase_order",
"args": { "supplier_hint": "default", "sku": "SKU-1042", "quantity": 50 },
"retry_policy": { "max_attempts": 3, "backoff": "exponential" },
"on_error": "compensate",
"compensate_with": {
"verb": "commerce.cancel_purchase_order",
"args": { "po_id": "$.step_3.output.po_id" }
}
}query & condition
A query is read-only and produces output later nodes can reference. A condition is a side-effect-free, total boolean guard — it routes, it never acts. Both branches are explicit; a branch may be null to stop.
{
"id": "step_2",
"type": "condition",
"expr": "$.step_1.output.stock < 5",
"on_true": "step_3",
"on_false": null
}parallel, foreach, await
parallel fans out independent branches with a join policy of all or any. foreach loops over a bounded array — no unbounded iteration. await_approval blocks until the owner decides on a parked proposal and routes on the outcome.
{
"id": "step_4",
"type": "foreach",
"items": "$.step_1.output.line_items",
"item_var": "line",
"do": ["step_4a"]
}Data references
Nodes pass data through backward-only references of the form $.step_N.output.field. A node may only reference earlier nodes, which guarantees the graph is acyclic — a DAG with no cycles, provable before execution.
Validation
Before any side effect, the validator proves: the schema is valid (additionalProperties: false); every reference resolves backward; the graph is acyclic; every verb is within the grant’s scope; and arg types match the verb’s profile. A rejected program comes back with structured diagnostics the generator reads to self-heal.
Least power, by design
The DSL is the least expressive language that does the job: no unbounded loops, no arbitrary code, no file or network access beyond declared verbs. Expressiveness is intentionally capped so that the set of representable programs is exactly the set of safe ones.