Scrydon
Authoring: Ontologies

Watch rules

Declare behavioural-pattern detections in your ontology extension — score role build-ups against signature weights, suppress re-fires, and optionally trigger a response process flow when a detection fires.

Watch rules are authored inside an Ontology extension alongside your Object Types, Link Types, and Bindings. For the extension lifecycle — extension build, upload, install — see Extensions & Authoring SDK. For the detection runtime and how fired detections propagate, see the Ontology documentation.

A watch rule continuously monitors a time-windowed stream of ontology observations and fires a detection when the observed role composition crosses a threshold. You declare what to watch, how to score it, and what to do when it fires — all in code, versioned alongside the rest of your extension.

Declaring a watch rule

Add a watchRules array to your defineOntology call:

import { defineOntology } from '@scrydon/sdk-authoring/ontologies'

export default defineOntology({
  id: 'c2-threat-awareness',
  version: '1.0.0',
  displayName: 'C2 Threat Awareness',
  // ... object types, link types, bindings ...

  watchRules: [
    {
      slug: 'kaliningrad-air-buildup',
      displayName: 'Kaliningrad — allied air build-up',
      description: 'Detects a concentration of ELECTRONIC_WARFARE, AEW, and FIGHTER airframes near the Kaliningrad approaches.',

      target: {
        objectTypeSlug: 'Aircraft',
        entityProperty: 'icao24',    // collapse positional fixes → airframes
        timeProperty: 'seenAt',
        latProperty: 'latitude',
        lngProperty: 'longitude',
        roleVia: { linkTypeSlug: 'AircraftOfType', property: 'missionRole' },
      },

      area: { objectTypeSlug: 'AreaOfInterest', areaId: 'kaliningrad-approaches' },

      windowSeconds: 900,  // 15-minute rolling window

      signature: {
        weights: { ELECTRONIC_WARFARE: 5, AEW: 4, FIGHTER: 1 },
        roleSaturation: 2,   // cap each role's contribution at 2 entities
      },

      threshold: {
        minDistinctRoles: 2,
        minScore: 10,
      },

      suppression: {
        cooldownSeconds: 3600,     // suppress re-fires for 1 hour
        refireOnNewRole: true,     // unless a new role appears
        refireOnScoreDelta: 4,     // or the score changes by more than 4
      },

      emit: {
        objectTypeSlug: 'BuildUpDetection',
        clearanceCeiling: { schemeId: 'default', rank: 2 },
      },
    },
  ],
})

Field reference

Top-level fields

FieldTypeRequiredDescription
slugstringYesStable identifier for this watch rule. Must be a valid slug (lowercase letters, digits, and hyphens; no spaces). Used as the name of the emitted SCHEMA_WATCH declaration.
displayNamestring (1–100 chars)YesHuman-readable name shown in the platform UI.
descriptionstring (≤500 chars)NoOptional longer description of what this watch detects.
enabledbooleanNoWhether the watch is active. Defaults to true when omitted. Set to false to ship a disabled watch that can be turned on without an extension re-publish.
targetobjectYesDefines which Object Type to observe and how to resolve entity identity and roles. See target below.
areaobjectYesIdentifies the AreaOfInterest instance that defines the spatial boundary.
windowSecondsinteger (1–3600)YesRolling time window in seconds (max 3600 — 1 hour).
signatureobjectYesRole weights and saturation cap. See signature below.
thresholdobjectYesConditions that must both be met for a detection to fire. See threshold below.
suppressionobjectYesRe-fire control. See suppression below.
emitobjectYesOutput object type and clearance ceiling for the detection. See emit below.
dispatchobjectNoOptional process-flow to start on detection. See dispatch below.

target

FieldTypeDescription
objectTypeSlugstringThe Object Type whose observations are scored.
entityPropertystringProperty that identifies the persistent entity (e.g. icao24). Observations sharing this value are collapsed into one entity count.
timePropertystringTimestamp property used to apply the rolling window.
latPropertystringLatitude property (WGS-84 decimal degrees).
lngPropertystringLongitude property (WGS-84 decimal degrees).
roleVia{ linkTypeSlug, property }Traverse this link type and read this property to determine the entity's role for scoring purposes.
roleOverrideObjectTypeSlugstring?When set, restrict role-resolution to entities of this Object Type on the far end of the link.

area

An AreaOfInterest instance loaded from the ontology graph that defines the spatial boundary for the watch. Observations outside the area are excluded from scoring.

windowSeconds

Rolling time window in seconds. Maximum is 3600 (1 hour). Observations older than windowSeconds age out of the score.

signature

FieldTypeDescription
weightsRecord<string, number>Score contribution per role. Keys must be role values present in the linked Object Type. Values must be ≥ 0 and finite.
roleSaturationnumberMaximum entity count per role that contributes to the score. Prevents a single-role flood from masking a multi-role composition.

threshold

FieldTypeDescription
minDistinctRolesnumberMinimum number of distinct roles that must be present before the detection fires.
minScorenumberMinimum weighted score that must be reached.

Both conditions must be satisfied simultaneously for the detection to fire.

suppression

FieldTypeDescription
cooldownSecondsnumberAfter a detection fires, suppress re-fires for this many seconds (max 86 400 — 24 h).
refireOnNewRolebooleanWhen true (default), a new role entering the window bypasses the cooldown and fires a new detection.
refireOnScoreDeltanumberWhen the score changes by at least this amount, a new detection fires even within the cooldown window. Must be positive.

emit

FieldTypeDescription
objectTypeSlugstringThe Object Type to materialise for each fired detection.
clearanceCeiling{ schemeId, rank }Required. Caps the clearance of the emitted detection object. The evaluator also reads at this ceiling: observations marked above it are never read and cannot contribute, and each detection's marking is the join of the evidence it cites — so setting this explicitly is a hard requirement (fail-closed).

dispatch (optional)

dispatch: {
  extensionId: 'c2-drone-response',
  processFlowSlug: 'buildup-response',
}

When present, a fired detection starts the named process flow in addition to emitting the detection object. The flow receives a detectionId pointer only — no detection content is passed directly.

Clearance note. The dispatched flow resolves the detection under its own principals' clearance through the normal governed read path. It cannot access more of the detection than the principals executing it are cleared to see. Setting dispatch does not widen disclosure beyond the notification that already goes to the same audience.

FieldTypeConstraints
extensionIdstring1–256 characters. Must identify the extension that publishes the target process flow.
processFlowSlugstring1–256 characters. Slug of the process flow to start on detection.

Both fields are exact identifiers — no wildcards or patterns.

Complete example with dispatch

watchRules: [
  {
    slug: 'kaliningrad-air-buildup',
    displayName: 'Kaliningrad — allied air build-up',

    target: {
      objectTypeSlug: 'Aircraft',
      entityProperty: 'icao24',
      timeProperty: 'seenAt',
      latProperty: 'latitude',
      lngProperty: 'longitude',
      roleVia: { linkTypeSlug: 'AircraftOfType', property: 'missionRole' },
    },

    area: { objectTypeSlug: 'AreaOfInterest', areaId: 'kaliningrad-approaches' },
    windowSeconds: 900,

    signature: {
      weights: { ELECTRONIC_WARFARE: 5, AEW: 4, FIGHTER: 1 },
      roleSaturation: 2,
    },

    threshold: { minDistinctRoles: 2, minScore: 10 },

    suppression: {
      cooldownSeconds: 3600,
      refireOnNewRole: true,
      refireOnScoreDelta: 4,
    },

    emit: {
      objectTypeSlug: 'BuildUpDetection',
      clearanceCeiling: { schemeId: 'default', rank: 2 },
    },

    // When the detection fires, start the response flow in the c2-drone-response extension.
    // The flow receives a detectionId pointer and resolves content under its own clearance.
    dispatch: {
      extensionId: 'c2-drone-response',
      processFlowSlug: 'buildup-response',
    },
  },
],

Stability guarantee

watchRules is an optional field. Extensions that do not declare any watch rules keep compiling and hashing unchanged — adding this field to your extension version does not invalidate previously published manifests.

Within a watch rule, dispatch is also optional. Extensions that declared a watch rule before dispatch existed continue to parse and compile without modification.

On this page

On this page