Turqoa Docs

Rules

The Decision Engine uses a declarative Policy DSL to define security rules. Each rule specifies conditions that match against incoming events and actions that execute when conditions are met. Rules are version-controlled, auditable, and can be tested in Shadow mode before going live.

Rule Syntax

Every rule follows this structure:

RULE "rule name"
  PRIORITY <0-100>
  MODE <shadow | advisory | live>
  WHEN
    <condition>
    AND <condition>
    ...
  THEN
    action: <action_type>(<parameters>)
    action: <action_type>(<parameters>)
    ...
  COOLDOWN <duration>

Minimal Example

RULE "Alert on perimeter motion"
  PRIORITY 50
  WHEN
    event.type = "motion_detected"
  THEN
    action: notify_operator(priority: "medium")

Full Example

RULE "Critical: Unauthorized vessel in LNG exclusion zone"
  PRIORITY 95
  MODE live
  WHEN
    event.type = "zone_entry"
    AND event.zone.name = "LNG Terminal Exclusion Zone"
    AND context.vessel.authorized = false
    AND context.vessel.risk_score >= 50
  THEN
    action: create_incident(severity: "critical", assign_to: "maritime_operator")
    action: dispatch_drone(mission: "alarm_verification", target: event.location)
    action: escalate(to: ["security_manager", "port_authority"])
    action: notify_all_operators(
      message: "Unauthorized vessel {context.vessel.name} entered LNG exclusion zone",
      sound: "critical"
    )
    action: send_webhook(endpoint: "coast_guard_api", payload: event)
  COOLDOWN 5m

Condition Operators

Conditions compare event fields and context data against values using the following operators:

Comparison Operators

OperatorDescriptionExample
=Equalevent.type = "motion_detected"
!=Not equalevent.severity != "low"
>Greater thancontext.vessel.risk_score > 75
>=Greater than or equalevent.confidence >= 0.85
<Less thancontext.vessel.speed_knots < 2
<=Less than or equalcontext.drone.battery_percent <= 20

Set Operators

OperatorDescriptionExample
INValue is in setevent.zone.type IN ["exclusion", "restricted"]
NOT INValue is not in setevent.source NOT IN ["test_camera"]
CONTAINSString/array containscontext.vessel.flags CONTAINS "sanctioned"

Logical Operators

OperatorDescriptionExample
ANDAll conditions must be trueevent.type = "breach" AND event.severity = "high"
ORAt least one condition trueevent.zone = "zone_a" OR event.zone = "zone_b"
NOTNegationNOT context.vessel.authorized

Pattern Operators

OperatorDescriptionExample
MATCHESRegex matchevent.source MATCHES "camera_north_.*"
STARTS_WITHString prefixcontext.vessel.name STARTS_WITH "MV"
ENDS_WITHString suffixevent.zone.name ENDS_WITH "_restricted"

Temporal Operators

OperatorDescriptionExample
WITHINTime windowevent.timestamp WITHIN "last 5m"
BETWEENTime rangeevent.time_of_day BETWEEN "22:00" AND "06:00"
DAY_OF_WEEKDay filterDAY_OF_WEEK IN ["sat", "sun"]
AFTERAfter eventAFTER event.type = "alarm" WITHIN 10m

Aggregation Operators

OperatorDescriptionExample
COUNTEvent count in windowCOUNT(event.type = "motion_detected", zone: event.zone) > 5 WITHIN 10m
RATEEvents per time unitRATE(event.type = "ais_gap", vessel: event.mmsi) > 3 PER 1h

Action Types

Actions define what the Decision Engine does when a rule matches.

Core Actions

ActionParametersDescription
notify_operatorpriority, message, sound, assign_toSend alert to operator console
notify_all_operatorsmessage, soundBroadcast to all active stations
create_incidentseverity, assign_to, title, tagsCreate a new incident
escalateto (list of roles/users), messageEscalate to supervisors or external parties

Gate Actions

ActionParametersDescription
open_gategate_idOpen specified gate
hold_gategate_id, duration, reasonHold gate closed pending review
lock_gategate_id, reasonLock gate until manual release
release_gategate_idRelease a held or locked gate

Drone Actions

ActionParametersDescription
dispatch_dronemission, target, priorityLaunch a drone mission
abort_dronedrone_id, reasonAbort an active mission
redirect_dronedrone_id, new_targetRedirect a drone mid-flight

Integration Actions

ActionParametersDescription
send_webhookendpoint, payload, methodSend HTTP webhook
send_emailto, subject, templateSend email notification
send_smsto, messageSend SMS alert
update_externalsystem, action, dataUpdate external system

Data Actions

ActionParametersDescription
log_eventlevel, tagsWrite to audit log
update_risk_scoreentity, adjustmentModify vessel/vehicle risk score
add_to_watchlistentity, list, durationAdd entity to a watchlist
tag_evidenceevent_id, tagsTag evidence for review

Rule Priorities

Priorities range from 0 (lowest) to 100 (highest) and determine evaluation order and conflict resolution:

RangeUsageExamples
90--100Critical safety rulesExclusion zone breach, watchlist hit
70--89High-priority securityPerimeter breach, unauthorized access
50--69Standard operationsMotion alerts, routine notifications
30--49Low-priority monitoringLogging, analytics triggers
0--29Background/housekeepingMetric collection, health checks

Priority Conflict Resolution

When multiple rules match the same event:

conflict_resolution:
  strategy: merge           # merge | priority | first_match
  on_conflicting_actions:
    gate_actions: highest_priority  # only one gate action per event
    drone_actions: merge            # can dispatch multiple drones
    notifications: merge            # send all notifications
    escalations: merge              # escalate to all targets

Cooldown

The COOLDOWN clause prevents a rule from firing repeatedly for the same trigger:

RULE "Fence breach alert"
  PRIORITY 80
  WHEN event.type = "fence_breach"
  THEN action: dispatch_drone(mission: "alarm_verification")
  COOLDOWN 5m

Cooldown is scoped to the rule + a configurable key:

cooldown:
  scope: rule_and_zone     # rule | rule_and_zone | rule_and_source | rule_and_entity
  duration: 5m
  reset_on: resolution     # resolution | timeout | manual

Examples

Gate Security

RULE "OCR mismatch - hold for review"
  PRIORITY 70
  MODE live
  WHEN
    event.type = "ocr_mismatch"
    AND event.confidence_delta > 0.15
  THEN
    action: hold_gate(gate_id: event.gate_id, duration: "5m", reason: "OCR mismatch")
    action: notify_operator(
      priority: "high",
      message: "OCR mismatch at {event.gate_id}: expected {event.expected} got {event.detected}",
      assign_to: "gate_operator"
    )
  COOLDOWN 1m

Maritime Anomaly Detection

RULE "AIS gap in restricted zone"
  PRIORITY 85
  MODE live
  WHEN
    event.type = "ais_gap"
    AND event.gap_duration_seconds > 300
    AND context.vessel.last_known_zone.type IN ["restricted", "exclusion"]
  THEN
    action: create_incident(
      severity: "high",
      title: "AIS gap detected for {context.vessel.name} in {context.vessel.last_known_zone.name}"
    )
    action: dispatch_drone(mission: "investigation", target: context.vessel.last_known_position)
    action: escalate(to: ["maritime_operator", "supervisor"])
  COOLDOWN 15m

Multi-Event Correlation

RULE "Coordinated perimeter probing"
  PRIORITY 90
  MODE live
  WHEN
    COUNT(event.type = "motion_detected", zone: "perimeter_*") >= 3 WITHIN 5m
    AND event.sources_distinct >= 2
  THEN
    action: create_incident(severity: "critical", title: "Possible coordinated perimeter probing")
    action: dispatch_drone(mission: "perimeter_sweep")
    action: escalate(to: ["security_manager"])
    action: notify_all_operators(message: "Multiple perimeter alerts detected - possible coordinated activity")
  COOLDOWN 30m

Time-Based Rules

RULE "After-hours gate activity"
  PRIORITY 60
  MODE live
  WHEN
    event.type = "gate_transaction"
    AND event.time_of_day BETWEEN "22:00" AND "05:00"
  THEN
    action: notify_operator(priority: "medium", message: "After-hours gate activity at {event.gate_id}")
    action: log_event(tags: ["after_hours", "gate"])

Weather-Aware Rules

RULE "Suspend drone patrol in high wind"
  PRIORITY 95
  MODE live
  WHEN
    event.type = "weather_update"
    AND event.wind_speed_ms > 15
  THEN
    action: abort_drone(drone_id: "all_patrol", reason: "High wind conditions")
    action: notify_operator(priority: "high", message: "Drone patrols suspended: wind {event.wind_speed_ms} m/s")

Rule Management

Version Control

All rules are stored in version-controlled YAML files:

rules/
  gate/
    ocr_mismatch.yaml
    seal_violation.yaml
    after_hours.yaml
  perimeter/
    motion_detection.yaml
    fence_breach.yaml
    coordinated_probing.yaml
  maritime/
    zone_entry.yaml
    ais_anomaly.yaml
    risk_threshold.yaml

Deploying Rules

# Validate rules before deployment
turqoa rules validate --path ./rules/

# Deploy to shadow mode first
turqoa rules deploy --path ./rules/ --mode shadow

# Promote to live after testing
turqoa rules promote --rule "OCR mismatch - hold for review" --mode live

Testing Rules

# Replay historical events against new rules
turqoa rules test --path ./rules/ --replay-from "2026-04-01" --replay-to "2026-04-05"

# Test a single rule with a synthetic event
turqoa rules test-single --rule ./rules/gate/ocr_mismatch.yaml --event '{
  "type": "ocr_mismatch",
  "confidence_delta": 0.25,
  "gate_id": "gate_1",
  "expected": "MSCU1234567",
  "detected": "MSCU1234568"
}'