Guides

Alert rule format (kyr/1)

The portable JSON document an alert is: its fields, conditions, derived signals and semantics.

Fields

NameTypeDescription
kyr1The format version.
idstringA unique id (a UUID).
versionnumberGrows by one on every saved edit.
namestring1 to 120 characters. Visible wherever the alert is listed: keep values out of it.
severity'info' | 'warning' | 'critical'How the alert is shown.
enabledbooleanA disabled rule is kept but not checked.
targets{ streams?, histories?, datasets? }Optional lists of stream ids, history ids and file names the rule applies to; absent or null: all.
signalsRecord<alias, { channel, stream?, history?, dataset? } | { formula }>Aliases (letters, digits, underscore; up to 32 characters) for channel names. A scope (stream id, history id or file name) restricts where the channel is looked for. formula names a math channel (browser only).
derivedRecord<alias, Derived>{ op: "rate" | "mean" | "min" | "max", of, windowS }, { op: "abs", of } or { op: "diff", a, b }. A derived signal may use another.
whenConditionThe condition tree (below).
forSnumberSeconds the condition must hold before the alert is raised.
clearForSnumberSeconds the condition must be false before the alert clears.
staleSnumber | nullA signal with no sample for this long counts as unknown. Absent or null: never.
runWhere('browser' | …)[]Where the rule runs. ['browser'].
origin{ workbookId, worksheetId, chartId, field, seriesId? } | nullThe chart alarm line the alert was made from, if any.

Conditions

ConditionTrue when
{ above: a, value, hysteresis? }a > value; false again once a ≤ value − hysteresis.
{ below: a, value, hysteresis? }a < value; false again once a ≥ value + hysteresis.
{ outside: a, lo, hi, hysteresis? }a < lo or a > hi; false again once lo + h ≤ a ≤ hi − h.
{ inside: a, lo, hi, hysteresis? }lo ≤ a ≤ hi; false again once a < lo − h or a > hi + h.
{ equals: a, value }a equals value (state numbers, positions).
{ changes: a }At a sample whose value differs from the previous one.
{ noData: a, forS }No sample of a for forS seconds.
{ all: [...] }, { any: [...] }, { not: c }Combinations, up to 3 levels deep, 16 conditions in all.

Examples

Threshold with hysteresis
{
  "kyr": 1,
  "id": "6f0c1c1e-0000-4000-8000-000000000001",
  "version": 1,
  "name": "Oil temperature high",
  "severity": "critical",
  "enabled": true,
  "signals": { "oil": { "channel": "Oil_Temp" } },
  "when": { "above": "oil", "value": 120, "hysteresis": 2 },
  "forS": 3,
  "clearForS": 10,
  "runWhere": ["browser"]
}
Rate of change
{
  "kyr": 1, "id": "c1a2…", "version": 1, "name": "Pressure rising fast", "severity": "warning", "enabled": true,
  "signals": { "p": { "channel": "Line_Pressure" } },
  "derived": { "pRate": { "op": "rate", "of": "p", "windowS": 2 } },
  "when": { "above": "pRate", "value": 5 },
  "forS": 0, "clearForS": 5, "runWhere": ["browser"]
}
No-data watchdog on one stream
{
  "kyr": 1, "id": "9d3e…", "version": 1, "name": "Press 2 silent", "severity": "warning", "enabled": true,
  "signals": { "rpm": { "channel": "RPM", "stream": "<stream id>" } },
  "when": { "noData": "rpm", "forS": 60 },
  "forS": 0, "clearForS": 0, "runWhere": ["browser"]
}

Semantics

  • Time is the data’s own time (the sample’s t), not the clock of the computer checking it. Only “has no data for” on a live stream also uses arrival time.
  • A rule is checked at every sample of any channel it uses, in time order. The other channels keep their last value (zero-order hold): channels sampled at different rates combine naturally.
  • A channel with no value yet, or none for staleS seconds, is unknown. A condition on it is unknown; All of is false if any part is false, else unknown if any is unknown; Any of is true if any part is true. An unknown rule neither raises nor clears.
  • The alert’s start time is the first sample where the condition held (the onset), even when For at least delays the alert; its end is the first sample where it was false.
  • A sample older than the last one checked is skipped and counted, so out-of-order data can’t make an alert flicker.
  • If a channel the rule uses disappears from a stream, an open alert ends at the last checked sample and the rule waits again.

JSON Schema

The schema below describes every field and limit; it ships with the SDK as @kymoscope/sdk/rules/kyr1.schema.json. The SDK’s validateRule applies the same rules at runtime, plus the ones a schema can’t express (aliases that exist, no cycles between derived signals).

kyr1.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://kymoscope.com/schemas/kyr1.schema.json",
  "title": "Kymoscope alert rule (kyr/1)",
  "description": "A portable alert rule. Channels are matched by exact, case-sensitive name on each target; a rule whose channels a target lacks waits there. Runtimes check the semantic limits (aliases referenced exist, derived signals are acyclic, at most 16 conditions) themselves.",
  "type": "object",
  "additionalProperties": true,
  "required": [
    "kyr",
    "id",
    "version",
    "name",
    "severity",
    "enabled",
    "signals",
    "when",
    "forS",
    "clearForS",
    "runWhere"
  ],
  "properties": {
    "kyr": {
      "const": 1
    },
    "id": {
      "type": "string",
      "minLength": 1
    },
    "version": {
      "type": "integer",
      "minimum": 1
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120,
      "pattern": "\\S"
    },
    "severity": {
      "enum": [
        "info",
        "warning",
        "critical"
      ]
    },
    "enabled": {
      "type": "boolean"
    },
    "targets": {
      "type": "object",
      "properties": {
        "streams": {
          "$ref": "#/definitions/idList"
        },
        "histories": {
          "$ref": "#/definitions/idList"
        },
        "datasets": {
          "$ref": "#/definitions/idList"
        }
      }
    },
    "signals": {
      "type": "object",
      "minProperties": 1,
      "propertyNames": {
        "$ref": "#/definitions/alias"
      },
      "additionalProperties": {
        "$ref": "#/definitions/signal"
      }
    },
    "derived": {
      "type": "object",
      "propertyNames": {
        "$ref": "#/definitions/alias"
      },
      "additionalProperties": {
        "$ref": "#/definitions/derived"
      }
    },
    "when": {
      "$ref": "#/definitions/condition"
    },
    "forS": {
      "$ref": "#/definitions/seconds"
    },
    "clearForS": {
      "$ref": "#/definitions/seconds"
    },
    "staleS": {
      "oneOf": [
        {
          "$ref": "#/definitions/seconds"
        },
        {
          "type": "null"
        }
      ]
    },
    "notify": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "runWhere": {
      "type": "array",
      "minItems": 1,
      "items": {
        "enum": [
          "browser",
          "collector",
          "watcher",
          "cloud"
        ]
      }
    },
    "keep": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "required": [
            "beforeS",
            "afterS"
          ],
          "properties": {
            "beforeS": {
              "$ref": "#/definitions/seconds"
            },
            "afterS": {
              "$ref": "#/definitions/seconds"
            }
          }
        }
      ]
    },
    "origin": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "required": [
            "workbookId",
            "worksheetId",
            "chartId",
            "field"
          ],
          "properties": {
            "workbookId": {
              "type": [
                "string",
                "null"
              ]
            },
            "worksheetId": {
              "type": "string"
            },
            "chartId": {
              "type": "string"
            },
            "field": {
              "type": "string"
            },
            "seriesId": {
              "type": "string"
            }
          }
        }
      ]
    }
  },
  "definitions": {
    "alias": {
      "type": "string",
      "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,31}$"
    },
    "channel": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "seconds": {
      "type": "number",
      "minimum": 0,
      "maximum": 86400
    },
    "hysteresis": {
      "type": "number",
      "minimum": 0
    },
    "idList": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "signal": {
      "oneOf": [
        {
          "type": "object",
          "required": [
            "channel"
          ],
          "additionalProperties": false,
          "properties": {
            "channel": {
              "$ref": "#/definitions/channel"
            },
            "stream": {
              "type": "string",
              "description": "Only on this stream id."
            },
            "history": {
              "type": "string",
              "description": "Only on this history id."
            },
            "dataset": {
              "type": "string",
              "description": "Only on loaded files with this name."
            },
            "tag": {
              "type": "string",
              "description": "Collector only: bind by the source address (OPC UA node id) instead of the published name."
            }
          }
        },
        {
          "type": "object",
          "required": [
            "formula"
          ],
          "additionalProperties": false,
          "description": "A math channel by name. The rule's runWhere must be exactly [\"browser\"].",
          "properties": {
            "formula": {
              "$ref": "#/definitions/channel"
            }
          }
        }
      ]
    },
    "derived": {
      "oneOf": [
        {
          "type": "object",
          "required": [
            "op",
            "of",
            "windowS"
          ],
          "additionalProperties": false,
          "properties": {
            "op": {
              "enum": [
                "rate",
                "mean",
                "min",
                "max"
              ]
            },
            "of": {
              "$ref": "#/definitions/alias"
            },
            "windowS": {
              "type": "number",
              "exclusiveMinimum": 0,
              "maximum": 86400
            }
          }
        },
        {
          "type": "object",
          "required": [
            "op",
            "of"
          ],
          "additionalProperties": false,
          "properties": {
            "op": {
              "const": "abs"
            },
            "of": {
              "$ref": "#/definitions/alias"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "op",
            "a",
            "b"
          ],
          "additionalProperties": false,
          "properties": {
            "op": {
              "const": "diff"
            },
            "a": {
              "$ref": "#/definitions/alias"
            },
            "b": {
              "$ref": "#/definitions/alias"
            }
          }
        }
      ]
    },
    "condition": {
      "description": "Nesting depth at most 3, at most 16 leaves. Other condition keys are refused (unsupported_condition).",
      "oneOf": [
        {
          "type": "object",
          "required": [
            "above",
            "value"
          ],
          "additionalProperties": false,
          "properties": {
            "above": {
              "$ref": "#/definitions/alias"
            },
            "value": {
              "type": "number"
            },
            "hysteresis": {
              "$ref": "#/definitions/hysteresis"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "below",
            "value"
          ],
          "additionalProperties": false,
          "properties": {
            "below": {
              "$ref": "#/definitions/alias"
            },
            "value": {
              "type": "number"
            },
            "hysteresis": {
              "$ref": "#/definitions/hysteresis"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "outside",
            "lo",
            "hi"
          ],
          "additionalProperties": false,
          "properties": {
            "outside": {
              "$ref": "#/definitions/alias"
            },
            "lo": {
              "type": "number"
            },
            "hi": {
              "type": "number"
            },
            "hysteresis": {
              "$ref": "#/definitions/hysteresis"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "inside",
            "lo",
            "hi"
          ],
          "additionalProperties": false,
          "properties": {
            "inside": {
              "$ref": "#/definitions/alias"
            },
            "lo": {
              "type": "number"
            },
            "hi": {
              "type": "number"
            },
            "hysteresis": {
              "$ref": "#/definitions/hysteresis"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "equals",
            "value"
          ],
          "additionalProperties": false,
          "properties": {
            "equals": {
              "$ref": "#/definitions/alias"
            },
            "value": {
              "type": "number"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "changes"
          ],
          "additionalProperties": false,
          "properties": {
            "changes": {
              "$ref": "#/definitions/alias"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "noData",
            "forS"
          ],
          "additionalProperties": false,
          "properties": {
            "noData": {
              "$ref": "#/definitions/alias"
            },
            "forS": {
              "$ref": "#/definitions/seconds"
            }
          }
        },
        {
          "type": "object",
          "required": [
            "all"
          ],
          "additionalProperties": false,
          "properties": {
            "all": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/definitions/condition"
              }
            }
          }
        },
        {
          "type": "object",
          "required": [
            "any"
          ],
          "additionalProperties": false,
          "properties": {
            "any": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/definitions/condition"
              }
            }
          }
        },
        {
          "type": "object",
          "required": [
            "not"
          ],
          "additionalProperties": false,
          "properties": {
            "not": {
              "$ref": "#/definitions/condition"
            }
          }
        }
      ]
    }
  }
}