Publish and version
Store each bundle once and never change it. Serve manifests with an ETag that is the checksum, so caches stay correct.
A published bundle never changes. A new rule is a new metadata.version and a new bundle with a
new checksum. The rule server serves the bundle and the manifests with an ETag made from the
checksum, so a browser or a CDN revalidates cheaply and never keeps rules that changed.
Hands-on
-
Store the bundle under a name that includes its version, for example
rules/shop.orders/1.0.0/shop.orders.bundle.json, in storage that refuses overwrites. Keep every version: rolling back means serving an old one again. -
Serve the rules with the rule server. It compiles the rulesets in its rules directory at start and logs the checksum of each one:
start the rule server (real output) $ RULE_SERVER_TOKEN=learn-demo-token RULES_DIR=./rules node packages/server/dist/main.js server log: {"level":"info","message":"ruleset loaded","id":"shop.orders.eu","version":"1.0.0","checksum":"sha256:b31b76fa5a5b8755a2f6df8de0e3d4152c78fc9a2d2c86df6703d51abda1d5c2","source":"document"} server log: {"level":"info","message":"ruleset loaded","id":"shop.orders","version":"1.0.0","checksum":"sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82","source":"document"} -
Ask for the client manifest, then ask again with its ETag:
manifests, ETags and checksum URLs (real exchange) > GET /rulesets < 200 [ { "id": "shop.orders.eu", "version": "1.0.0", "checksum": "sha256:b31b76fa5a5b8755a2f6df8de0e3d4152c78fc9a2d2c86df6703d51abda1d5c2" }, { "id": "shop.orders", "version": "1.0.0", "checksum": "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82" } ] > GET /rulesets/shop.orders/manifest?channel=client < 200 < etag: "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82-client" < cache-control: public, max-age=0, must-revalidate { "id": "shop.orders", "version": "1.0.0", "checksum": "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82", "channel": "client", "rules": [ "order.quantity.max" ] } > GET /rulesets/shop.orders/manifest?channel=client > If-None-Match: "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82-client" < 304 < etag: "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82-client" < cache-control: public, max-age=0, must-revalidate > GET /rulesets/shop.orders/bundle < 401 { "type": "about:blank", "title": "a bearer token is required for this endpoint", "status": 401 } > GET /rulesets/shop.orders/bundle > Authorization: Bearer learn-demo-token < 200 < etag: "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82-bundle" < cache-control: private, no-cache { "ruleCascadeBundle": "1.0.0", "id": "shop.orders", "version": "1.0.0", "checksum": "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82" } > GET /rulesets/shop.orders/manifest?channel=client&checksum=sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82 < 200 < etag: "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82-client" < cache-control: public, max-age=0, must-revalidate { "id": "shop.orders", "version": "1.0.0", "checksum": "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82", "channel": "client", "rules": [ "order.quantity.max" ] } > GET /rulesets/shop.orders/manifest?channel=client&checksum=sha256:0000000000000000000000000000000000000000000000000000000000000000 < 409 < cache-control: no-store { "type": "about:blank", "title": "ruleset shop.orders is served with another checksum", "status": 409, "checksum": "sha256:4583c90bbfcacb54bf78e11d9e8190ecc086ce4b69aade609b7a0928d452ea82" }Read the exchange from the top:
/rulesetslists what is served, with each checksum;- the client manifest is public, and its ETag is the checksum plus the channel;
- the same request with
If-None-Matchanswers304: nothing to download; - the bundle needs the bearer token (
401without it) and has its own ETag; ?checksum=asks for one exact version:200while it is served,409with the served checksum otherwise.
Done when
- Each version has its own immutable bundle.
- A second request for the manifest with
If-None-Matchanswers304. - The bundle endpoint answers
401without the token.
Go deeper
- The rule server: every endpoint and setting.
- Caching and refreshing rules: cache policies and checksum URLs.
Compile to a checksummed bundle
rcas compile turns the ruleset and its parents into one JSON bundle with a checksum. Every runtime evaluates the bundle the same way.
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.