Rule Cascade
LearnThe process, end to end

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

  1. 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.

  2. 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"}
  3. 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:

    • /rulesets lists 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-Match answers 304: nothing to download;
    • the bundle needs the bearer token (401 without it) and has its own ETag;
    • ?checksum= asks for one exact version: 200 while it is served, 409 with the served checksum otherwise.

Done when

  • Each version has its own immutable bundle.
  • A second request for the manifest with If-None-Match answers 304.
  • The bundle endpoint answers 401 without the token.

Go deeper

Course overview

On this page