Load in applications
Embed a runtime and load the bundle at start-up, or let the application ask the rule server. Either way, log the checksum.
An application gets rules in one of two ways. Embedded: it loads the bundle into a runtime in its own process and evaluates with a function call. Rule server: it sends each request over HTTP to a server that holds the rules. Embedded is faster and has no network hop; the rule server suits a language without a runtime and keeps one place to update.
Hands-on
-
Embedded. Load the bundle once, at start-up. A bundle that cannot be used throws, so the service never starts with bad rules. This is the Node.js example service:
examples/backend-node/src/app.mjs /** Reads a precompiled bundle. A bad bundle throws here, so the service never starts with it. */ export function loadRules(bundlePath) { return RuleSet.fromBundle(JSON.parse(readFileSync(bundlePath, 'utf8'))); }examples/backend-node/src/main.mjs const rules = loadRules(bundle); // throws, and the process stops, when the bundle is not usable const { server } = createApp({ rules, publish: (command) => console.log(`command ${command.name} -> ${command.ref ?? command.type}`, JSON.stringify(command.payload)), }); server.listen(port, () => console.log(`${rules.id}@${rules.version} (${rules.checksum}) on :${port}`));The service logs the ruleset id, version and checksum it loaded. Java, Go, Python and the other languages do the same: see Use it in your language.
-
In the browser. Fetch the client manifest from the rule server. The manifest client keeps it and revalidates it with the ETag:
packages/typescript/README.md const manifests = createManifestClient({ baseUrl: '/api/rules' }); const manifest = await manifests.get('acme.payments.transfer'); // cached; revalidated with the ETag -
Rule server. Send the request with the ruleset id. The answer carries the checksum of the rules that decided:
POST /evaluations (real exchange) > POST /evaluations > Authorization: Bearer learn-demo-token < 200 { "ruleset": "shop.orders", "version": "1.0.0", "checksum": "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82", "decision": "deny", "findings": [ { "rule": "order.quantity.max", "code": "SHOP-ORD-001", "severity": "error", "message": "You can order at most 10 items.", "fields": [ "/quantity" ], "blocking": true, "status": "open", "resolution": "none", "source": "shop.orders@1.0.0" } ], "effects": [], "commands": [] } > POST /evaluations > Authorization: Bearer learn-demo-token < 200 { "ruleset": "shop.orders", "version": "1.0.0", "checksum": "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82", "decision": "allow", "findings": [], "effects": [], "commands": [ { "name": "order.placed", "type": "event", "rule": "order.placed", "idempotencyKey": "order.placed:o-4", "payload": { "orderId": "o-4", "quantity": 3 }, "ref": "OrderPlaced" } ] }
Done when
- The application refuses to start when the bundle cannot be loaded.
- Its start-up log names the ruleset id, version and checksum.
- An evaluation answer carries the same checksum.
Go deeper
- Backend API playbook: load, evaluate and refuse in a real service.
- The rule server and deploy it on Kubernetes.