Guides
Alert rule format (kyr/1)
The portable JSON document an alert is: its fields, conditions, derived signals and semantics.
Fields
| Name | Type | Description |
|---|---|---|
kyr | 1 | The format version. |
id | string | A unique id (a UUID). |
version | number | Grows by one on every saved edit. |
name | string | 1 to 120 characters. Visible wherever the alert is listed: keep values out of it. |
severity | 'info' | 'warning' | 'critical' | How the alert is shown. |
enabled | boolean | A 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. |
signals | Record<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). |
derived | Record<alias, Derived> | { op: "rate" | "mean" | "min" | "max", of, windowS }, { op: "abs", of } or { op: "diff", a, b }. A derived signal may use another. |
when | Condition | The condition tree (below). |
forS | number | Seconds the condition must hold before the alert is raised. |
clearForS | number | Seconds the condition must be false before the alert clears. |
staleS | number | null | A 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? } | null | The chart alarm line the alert was made from, if any. |
Conditions
| Condition | True 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
{
"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"]
}{
"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"]
}{
"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
staleSseconds, 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).
{
"$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"
}
}
}
]
}
}
}