Open Source · Built for Airline Retailing

Author once.
Evaluate
at the
edge.

The open-source rules engine for IATA Modern Airline Retailing — Offer Management, Order Management, ancillary pricing, eligibility, taxation. Airlines design and automate their business processes as JSON rule graphs and run them anywhere, with sub-millisecond evaluation and zero proprietary dependencies.

Version 0.2.0
License MIT
Runtime .NET 9 (cross-platform)
Storage DocumentForge or local file
73K
Evaluations / sec @ 16-way
70µs
p50 warm steady-state
11
Composable node categories
0
Proprietary dependencies

Floor numbers from a benchmark you can run yourself in five minutes. Designed to clear the IATA MAR latency bar by 70× at single-node scale and scale further by sharding.

Why RuleForge

The foundation under every Offer & Order rule.

Modern Airline Retailing is built on rules: who is eligible for what, at what price, in which fare family, with which taxes, under which conditions. Today those rules live inside expensive proprietary engines — locked, opaque, and built for the legacy PNR/EMD world. RuleForge is the open alternative: a runtime that airlines own, read line by line, and extend on their own clock.

01 · Open Source

Airlines own the rule engine

MIT-licensed, on GitHub, runs on a laptop or a single VM. No vendor lock-in, no per-evaluation pricing, no audit risk. The rules and the engine that runs them belong to the airline. Read the code, fork it, contribute back.

02 · Modern Airline Retailing

Aligned with IATA Offer & Order

Rules consume and produce JSON in the exact shape of IATA Offers, Orders, Services, and Bundles. Filter on $.passengers[*].type, mutate $.bundles[0].products[], look up tax rates by $.journey.origin. No ORM, no shred-and-rebuild, no schema drift.

03 · Configure the business

Process as data

Eligibility, tier bonuses, ancillary fees, fare conditions, taxation, surcharge stacks — every business rule becomes an authored JSON graph that anyone with the right admin app can version, review, and publish. Code-deploys for code; rule-deploys for rules.

04 · Sub-millisecond hot path

Engineered for offer-velocity

~70µs p50 for a typical 8-node rule. 73,000 evaluations/sec on 16 workers. Rule snapshots are immutable per version; the engine resolves endpoint → ruleId → version → snapshot with all three steps cached. Production mode skips trace and duration overhead entirely.

05 · Cross-platform binaries

Runs anywhere .NET runs

Self-contained binaries for Windows, Linux, macOS — no .NET install needed on the target machine. Drop on a VM, ship in a container, or co-locate alongside DocumentForge as a sidecar where the cold-path is 2.5ms instead of 1500ms across the public internet.

06 · Trace + governance

Every decision is explainable

Add ?debug=true and the engine returns a per-node trace plus wall-clock duration. Every rule is a versioned snapshot in DocumentForge. Approval workflows, audit logs, environment pinning — all built on data the airline already owns.

Try it now

Download. Run. Evaluate.

Self-contained binary. No .NET install needed. Drop on a laptop, evaluate the bundled fixtures against four worked scenarios, and see real numbers in under five minutes.

Walk through the full quickstart →

Embed it · or talk to it over REST

Two ways in. Same engine.

RuleForge is a .NET library at heart, but ships as two self-contained binaries — ruleforge-cli for one-shot evaluation and ruleforge-api for HTTP service. Pick the tab that matches your stack — every example evaluates the bundled rule-bag-policy@7 against a sample request and returns the same envelope.

using System.Text.Json;
using RuleForge.Core;
using RuleForge.Core.Loader;
using RuleForge.Core.Graph;

// Point at the bundled fixture pack — no DocumentForge required
var rules  = new LocalFileRuleSource("./fixtures/rules");
var refs   = new LocalFileReferenceSetSource("./fixtures/refs");
var runner = new RuleRunner();

// Resolve and evaluate
var rule    = await rules.GetByEndpointAsync("/v1/ancillary/bag-policy", HttpMethodKind.POST);
var request = JsonDocument.Parse("""
    { "pnr": "MKP800", "cabin": "Y", "orig": "LHR", "dest": "DXB",
      "bagPieces": 3, "markup": 0.15, "pax": [{ "id": "p1", "type": "ADT" }] }
    """);
var envelope = await runner.RunAsync(rule!, request.RootElement,
    new RuleRunner.Options(Debug: true, ReferenceSetSource: refs));

Console.WriteLine(envelope.Decision);     // "apply"
Console.WriteLine(envelope.Result);       // { code: "BAG", fee: 517.5, currency: "AED", pieces: 3 }
# Start the HTTP engine
RULEFORGE_FIXTURES_DIR=./fixtures/rules \
RULEFORGE_REFS_DIR=./fixtures/refs \
  ./ruleforge-api --urls http://localhost:5050

# Evaluate
curl -X POST "http://localhost:5050/v1/ancillary/bag-policy?debug=true" \
     -H "Content-Type: application/json" \
     -d '{
       "pnr":      "MKP800",
       "cabin":    "Y",
       "orig":     "LHR",
       "dest":     "DXB",
       "bagPieces": 3,
       "markup":    0.15,
       "pax":      [{ "id": "p1", "type": "ADT" }]
     }'

# Returns: { ruleId, ruleVersion, decision, evaluatedAt, result, trace }
// Any HTTP client works — here HttpClient (JDK 11+)
var http = HttpClient.newHttpClient();
var body = """
    { "pnr": "MKP800", "cabin": "Y", "orig": "LHR", "dest": "DXB",
      "bagPieces": 3, "markup": 0.15, "pax": [{ "id": "p1", "type": "ADT" }] }
    """;

var req = HttpRequest.newBuilder()
    .uri(URI.create("http://localhost:5050/v1/ancillary/bag-policy?debug=true"))
    .header("Content-Type", "application/json")
    .POST(BodyPublishers.ofString(body))
    .build();

var resp = http.send(req, BodyHandlers.ofString());
System.out.println(resp.body());
import httpx

resp = httpx.post(
    "http://localhost:5050/v1/ancillary/bag-policy",
    params={"debug": "true"},
    json={
        "pnr": "MKP800", "cabin": "Y",
        "orig": "LHR", "dest": "DXB",
        "bagPieces": 3, "markup": 0.15,
        "pax": [{"id": "p1", "type": "ADT"}],
    },
)
envelope = resp.json()
print(envelope["decision"], envelope["result"])
// Node 20+ — global fetch, no dependencies
const resp = await fetch(
  "http://localhost:5050/v1/ancillary/bag-policy?debug=true",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      pnr: "MKP800", cabin: "Y",
      orig: "LHR", dest: "DXB",
      bagPieces: 3, markup: 0.15,
      pax: [{ id: "p1", type: "ADT" }],
    }),
  }
);
const envelope = await resp.json();
console.log(envelope.decision, envelope.result);

One request, one envelope

Every evaluation produces the same envelope shape: a ruleId, the evaluated version, a decision (apply | skip | error), the rule-specific result, and an optional trace. The decision tells the caller what to do; the result is the rule's payload.

// POST /v1/ancillary/bag-policy?debug=true
{
  "ruleId": "rule-bag-policy",
  "ruleVersion": 7,
  "decision": "apply",
  "evaluatedAt": "2026-04-30T10:14:04.614Z",
  "result": {
    "code": "BAG",
    "weightKg": 23,
    "currency": "AED",
    "fee": 517.5,
    "pieces": 3
  },
  "trace": [/* per-node, only in debug mode */],
  "durationMs": 47
}
Self-describing contracts

Author rules with just the binary.

Three layers make RuleForge rules self-describing — no separate SDK, no external doc, no drift between editor and engine. Anyone with the binary can build new rules and have them validated against the same contract the engine enforces at runtime.

01 · Rule shape

11 JSON Schemas, dumped from the binary

Run ruleforge-cli schemas --out ./schemas. The output is generated from the live C# runtime types via JsonSchemaExporter — they are the contract. Validate authored rules with any off-the-shelf JSON Schema validator (ajv, jsonschema, NJsonSchema). If it passes, the engine accepts it.

02 · Request shape

Every rule carries its own inputSchema

Open any rule JSON and the inputSchema field declares exactly what request body the engine will accept. Use it to render forms, codegen client types, or drive contract tests downstream — without ever calling the engine.

03 · Worked references

Four bundled rules cover 8 of 11 node categories

Copy the closest one as your starting point. rule-bag-policy · rule-tier-bonus · rule-pnr-taxes · rule-seat-assignments. All valid, runnable JSON — the canonical examples for filter / lookup / iteration / sub-rule patterns.

Step-by-step authoring walkthrough →

Where to next

Pick your path.

01

Use Cases →

Why RuleForge for IATA Modern Airline Retailing. Eligibility, ancillary pricing, taxation, fare conditions — and the business case for owning the engine.

02

Quickstart →

Download the binary, evaluate four pre-built scenarios from basic to advanced, drive the engine from Postman. Five minutes end-to-end.

03

Reference →

Tech specs for engineers: rule schema, every node category, evaluators, iteration, sub-rules, storage, CLI, deployment.

Status: RuleForge ships 138 unit + integration tests, a 5-project .NET 9 solution, four CLI verbs (run, publish, mirror, bench), two source backends (DocumentForge HTTP + local file), self-contained binaries for five platforms, and end-to-end live demos. Production-ready for sub-millisecond rule evaluation; pair with whatever rule editor your team prefers.