# gorules/zen: Business Rules Engine in Rust with Node.js, Python and Go bindings

> gorules/zen is a Rust-based rules engine that keeps business logic in JSON files rather than application code. Multi-language packages let teams evaluate those files from Go, JavaScript, Python and JVM environments; version 2.0 made the arbitrary_precision flag opt-in for direct Rust crate users.

**gorules/zen** — Open-source Business Rules Engine for your Rust, NodeJS, Python, Go, Java, C#, Kotlin (JVM), Kotlin (Android) and Swift (iOS) applications.

- Repository: https://github.com/gorules/zen
- Website: https://gorules.io
- Stars: 2,112 · Forks: 229
- Language: Rust
- License: MIT
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/gorules-zen

## Decision tables, graphs and policy documents all compile to portable JDM JSON

ZEN Engine addresses a coordination gap between business logic owners and developers. Rules maintained in code require developer effort to read and change; rules kept in spreadsheets drift from what the application actually runs. The engine stores all decisions as JSON in the JSON Decision Model (JDM) format, and evaluates that JSON directly, making the JSON file the only copy the engine reads and runs.

Two authoring modes are available. The canvas suits analysts who think about decisions as flowcharts with branching conditions; the policy document format suits teams that prefer a written specification alongside typed data declarations. The engine uses the same JDM JSON regardless of which authoring mode produced it; teams can mix formats in one workspace without affecting evaluation.

Conditions use the ZEN Expression Language. A toggle in the editor switches between how a developer sees the condition and how a business reviewer sees it; both are presentations of the same underlying definition. ZEN Engine is MIT licensed; zen-types, zen-tmpl and zen-macros each shipped v2.1.2 on 2026-10-02, and the last push to master was on 2026-10-06.

## Installing zen-engine for Rust, Node.js and Python and running a first evaluation

For Rust, add the dependency to Cargo.toml:

```toml
[dependencies]
zen-engine = "2"
```

For Node.js:

```bash
npm i @gorules/zen-engine
```

For Python:

```bash
pip install zen-engine
```

All three expect a JDM JSON file to load. A Python evaluation reads that file and passes it to the engine:

```python
import zen

with open("./jdm_graph.json", "r") as f:
    content = f.read()

engine = zen.ZenEngine()

decision = engine.create_decision(content)
result = decision.evaluate({"input": 15})
```

Node.js uses `ZenEngine` and `createDecision` with the same pattern. Go has a separate repository at github.com/gorules/zen-go. Java, Kotlin, .NET, iOS and Android each have their own packages; the README links to Maven Central, NuGet and the binding directories for each, but none of those languages has a quickstart in the main README.

## Pre-compiled decisions and what the evaluate call actually does

Calling `create_decision` parses the JDM JSON once at load time; subsequent calls against the same loaded decision share that parsed structure rather than re-reading the file. Documentation makes performance claims without benchmark context: no hardware specification, decision complexity measure or concurrency model is given, so the figures are not independently verifiable.

In Rust, `DecisionEngine::default()` creates the engine and `create_decision` receives the parsed content. In Node.js the equivalent uses `ZenEngine` and `createDecision`. In Python it is `zen.ZenEngine()` and `create_decision`. The API shape is consistent across all three.

The fetch strategy is the caller's choice; the engine does not include a loading layer. Caching and refresh logic are application-layer concerns. The engine receives the JDM content and does not care how or from where it was obtained.

Multi-decision graphs and batch evaluation are documented in separate language-specific guides linked from the README, not in the main quickstart.

## Workspace analysis checks type flow, exhaustiveness and write conflicts before runtime

Version 2.0 adds a workspace analysis pass that examines the JDM project without running the engine. Dynamic testing can only check combinations that test inputs happen to exercise; workspace analysis checks the decision structure itself, so a table gap or a type mismatch is caught even when no test case reaches that path. The analysis applies to the entire workspace regardless of whether the decisions are graphs, policy documents or a mix.

One pass checks whether two or more rules in the same table can write contradictory values to the same output field. Another verifies that every possible combination of input values has at least one matching rule in the relevant table.

The README does not document how exhaustiveness checking handles open-ended input types such as arbitrary strings or floating-point numbers. Whether developers must declare finite domain bounds, or whether a table with unbounded inputs is flagged as non-exhaustive by default, is not stated. That gap matters if exhaustiveness results are used as a compliance signal, since a passing result could mean the table is complete or simply that its inputs were declared narrowly enough to appear complete.

Workspace analysis addresses the authoring phase rather than the runtime. An analyst who can identify gaps or conflicts before deployment has more useful information than a test suite that only surfaces gaps when an unlucky input lands in a test case.

## Per-column collect and the arbitrary_precision break between 0.x and 2.0 for Rust crates

Per-column collect is a new 2.0 feature for decision tables. Before 2.0, evaluation always stopped at the first row whose conditions matched. Adding `tags[]` to a column header turns it into an accumulator: where a normal column stops at the first qualifying row, the annotated column continues through all of them and builds a list. Columns without that annotation in the same table keep the early-stop behavior, so scalar and list outputs can coexist in one table.

The 2.0 change creates a split between Rust crate callers and binding callers. In 2.0, `arbitrary_precision` became opt-in across the Rust workspace crates; adding `features = ["arbitrary_precision"]` to the Cargo dependency entry restores prior decimal behavior for Rust callers. Python, Node.js, C and UniFFI callers are explicitly listed as unaffected; those binding packages do not require any configuration change.

That split means a Python or Node.js caller running the same decision gets arbitrary precision in 2.0 without any change, while a direct Rust caller does not. The README gives no guidance on scenarios where the two numeric paths would produce different outputs for the same input, which makes a mixed deployment that calls the engine from both Rust and a binding layer worth testing explicitly.

## GoRules platform: governance workflow, scenario testing and AI copilot above the open-source core

The engine is the open-source core; GoRules provides a commercial platform around it in managed, self-hosted and embedded configurations. The README states SOC 2 Type II compliance but does not specify which configuration carries that certification.

Every environment transition needs a human approval before it proceeds. Each environment receives the same unmodified decision file; a complete history of every configuration change is preserved. An AI component connected through an MCP server can draft rule edits and run test scenarios against them, but it cannot publish. Publishing requires a human decision.

The open-source engine can be used without the GoRules platform. The README does not document which governance features require the platform and which work purely with the engine and the open-source JDM Editor (a React component hosted at github.com/gorules/jdm-editor). Teams that need scenario testing infrastructure, AI editing or approval workflows must evaluate the platform separately from the engine itself.

## Where zen-engine fits poorly and what the JDM format costs in portability

Three situations where the engine is the wrong tool. First, teams whose rules are developer-owned and already covered by unit tests get no benefit unless non-developers actually need to read or edit the JSON. The portability advantage requires that analysts adopt the JDM Editor or another JDM-compatible tool. Without that participation, the added dependency and tooling deliver no advantage over tested application code.

Second, the JDM format is specific to gorules/zen. It is not DMN (Decision Model and Notation), PMML or any other portable standard. Switching to a different rules engine would require converting all existing decision files, and the README documents no import or export path for other formats.

For teams doing purely in-code rule evaluation, if-else chains or switch statements with unit tests remain a lower-complexity alternative. In-code rules are readable only by developers and changing them requires a code deployment, but they carry no external dependency and no file format to manage. ZEN Engine's value proposition depends on business-side stakeholders actually participating in the rules lifecycle; if they do not, the JDM tooling adds overhead without a matching benefit.

## Conclusion

ZEN Engine suits teams where business-side reviewers need to read and approve rules without a developer intermediary. A pricing table or a fraud-risk threshold can be authored and tested in JDM format without modifying application code. Skip it if rules are developer-owned and unit-tested in code directly: the JDM format, tooling and version management add overhead that conditional logic with tests does not. Before shipping with the Rust crate, confirm numeric behavior if the decisions do financial arithmetic; the 2.0 default change for arbitrary_precision is a Rust-crate concern only, not applicable to binding users.

## FAQ

### What is gorules/zen and what does it do?

gorules/zen (ZEN Engine) is a Rust-based rules engine. Business logic is stored separately from application code in JDM format JSON files; the engine evaluates those files at request time. Language-specific packages are available for Node.js, Python, Go, Java/Kotlin, .NET, iOS and Android.

### How do I install zen-engine for Node.js?

Run npm i @gorules/zen-engine to install the package. Then import ZenEngine from @gorules/zen-engine, load a JDM JSON file, call engine.createDecision(content), and call evaluate with an input object. Full guides are linked from the repository README.

### What changed in zen-engine version 2.0 for Rust users?

In v2.0, the Rust workspace crates changed their feature defaults. Rust developers using zen-engine or related crates who need exact decimal arithmetic must now list `features = ["arbitrary_precision"]` in Cargo.toml. Node.js and Python callers need no changes; their packages already carry the flag.

### Does gorules/zen require the GoRules cloud platform?

No. The engine works independently without the GoRules platform. The commercial platform wraps the engine with additional tooling for governance and AI editing, but the README does not document the boundary between what requires the platform and what the open-source engine supports on its own.

### What is the ZEN Expression Language in gorules/zen?

The ZEN Expression Language writes conditions inside decision tables and expression nodes. It has two visual representations sharing one underlying definition; a change through either view applies to the same rule.

## Sources

- [gorules/zen on GitHub](https://github.com/gorules/zen)
- [License: MIT](https://github.com/gorules/zen/blob/master/LICENSE)
- [Project website](https://gorules.io)
- [README](https://github.com/gorules/zen/blob/master/README.md)
- [Releases](https://github.com/gorules/zen/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gorules-zen
