Chapter 01

Getting Started

Two source backends: a local file pack (no DocumentForge required) and the DocumentForge HTTP source. Both produce identical envelopes. Start local, switch to DF when you're ready.

Prerequisites

1. Clone and build

git clone https://github.com/tailwind-retailing/ruleforge.git
cd ruleforge
dotnet build
dotnet test                           # 126/126 green

2. Run a sample rule, in-process, against the bundled fixtures

The repo ships with versioned rule snapshots under fixtures/rules/ and matching scenarios under fixtures/scenarios/. The default binding points POST /v1/ancillary/bag-policy at rule-bag-policy@7.

dotnet run --project src/RuleForge.Cli -- run \
    --endpoint /v1/ancillary/bag-policy \
    --request  '@fixtures/scenarios/s-bag-3pc-markup15.json' \
    --debug

You'll see the rule's full per-node trace plus the assembled result:

{
  "ruleId": "rule-bag-policy",
  "ruleVersion": 7,
  "decision": "apply",
  "evaluatedAt": "2026-04-27T10:10:20.141Z",
  "result": {
    "code": "BAG",  "weightKg": 23,
    "currency": "AED", "fee": 517.5, "pieces": 3
  },
  "trace": [
    { "nodeId": "n5-bag",    "outcome": "pass", "output": { /* base bag */ } },
    { "nodeId": "n6-pieces", "outcome": "pass", "output": { /* +pieces:3 */ } },
    { "nodeId": "n7-fee",    "outcome": "pass", "output": { /* fee:450 */ } },
    { "nodeId": "n8-markup", "outcome": "pass", "output": { /* fee:517.5 */ } }
  ],
  "durationMs": 47
}

3. Stand up the HTTP API

dotnet run --project src/RuleForge.Api

Boot output lists every endpoint that has an environment binding:

info: Bound POST /v1/ancillary/bag-policy → rule-bag-policy@7
info: Bound POST /v1/ancillary/tier-bonus → rule-tier-bonus@1
info: Now listening on: http://localhost:5050
curl -X POST http://localhost:5050/v1/ancillary/bag-policy \
  -H 'content-type: application/json' \
  --data @fixtures/scenarios/s-bag-3pc-markup15.json

4. Switch to DocumentForge

Swap the rule source by setting environment variables. The engine resolves endpoint + method → ruleId → version → snapshot on first call and caches each step.

# local DocumentForge (recommended for prod-like perf)
RULEFORGE_RULE_SOURCE=df \
RULEFORGE_DF_BASE_URL=http://localhost:5000 \
RULEFORGE_DF_API_KEY=your-key \
RULEFORGE_ENV=staging \
  dotnet run --project src/RuleForge.Api

5. Add API auth

Set RULEFORGE_API_KEY and the engine starts rejecting requests that don't supply X-AERO-Key or Authorization: Bearer with the matching key. /health stays open for liveness probes.

RULEFORGE_API_KEY=secret dotnet run --project src/RuleForge.Api

curl -X POST http://localhost:5050/v1/ancillary/bag-policy \
  -H 'X-AERO-Key: secret' \
  -H 'content-type: application/json' \
  --data @fixtures/scenarios/s-bag-3pc-markup15.json

What's next