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
| Operator | Description | Example |
|---|
= | Equal | event.type = "motion_detected" |
!= | Not equal | event.severity != "low" |
> | Greater than | context.vessel.risk_score > 75 |
>= | Greater than or equal | event.confidence >= 0.85 |
< | Less than | context.vessel.speed_knots < 2 |
<= | Less than or equal | context.drone.battery_percent <= 20 |
Set Operators
| Operator | Description | Example |
|---|
IN | Value is in set | event.zone.type IN ["exclusion", "restricted"] |
NOT IN | Value is not in set | event.source NOT IN ["test_camera"] |
CONTAINS | String/array contains | context.vessel.flags CONTAINS "sanctioned" |
Logical Operators
| Operator | Description | Example |
|---|
AND | All conditions must be true | event.type = "breach" AND event.severity = "high" |
OR | At least one condition true | event.zone = "zone_a" OR event.zone = "zone_b" |
NOT | Negation | NOT context.vessel.authorized |
Pattern Operators
| Operator | Description | Example |
|---|
MATCHES | Regex match | event.source MATCHES "camera_north_.*" |
STARTS_WITH | String prefix | context.vessel.name STARTS_WITH "MV" |
ENDS_WITH | String suffix | event.zone.name ENDS_WITH "_restricted" |
Temporal Operators
| Operator | Description | Example |
|---|
WITHIN | Time window | event.timestamp WITHIN "last 5m" |
BETWEEN | Time range | event.time_of_day BETWEEN "22:00" AND "06:00" |
DAY_OF_WEEK | Day filter | DAY_OF_WEEK IN ["sat", "sun"] |
AFTER | After event | AFTER event.type = "alarm" WITHIN 10m |
Aggregation Operators
| Operator | Description | Example |
|---|
COUNT | Event count in window | COUNT(event.type = "motion_detected", zone: event.zone) > 5 WITHIN 10m |
RATE | Events per time unit | RATE(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
| Action | Parameters | Description |
|---|
notify_operator | priority, message, sound, assign_to | Send alert to operator console |
notify_all_operators | message, sound | Broadcast to all active stations |
create_incident | severity, assign_to, title, tags | Create a new incident |
escalate | to (list of roles/users), message | Escalate to supervisors or external parties |
Gate Actions
| Action | Parameters | Description |
|---|
open_gate | gate_id | Open specified gate |
hold_gate | gate_id, duration, reason | Hold gate closed pending review |
lock_gate | gate_id, reason | Lock gate until manual release |
release_gate | gate_id | Release a held or locked gate |
Drone Actions
| Action | Parameters | Description |
|---|
dispatch_drone | mission, target, priority | Launch a drone mission |
abort_drone | drone_id, reason | Abort an active mission |
redirect_drone | drone_id, new_target | Redirect a drone mid-flight |
Integration Actions
| Action | Parameters | Description |
|---|
send_webhook | endpoint, payload, method | Send HTTP webhook |
send_email | to, subject, template | Send email notification |
send_sms | to, message | Send SMS alert |
update_external | system, action, data | Update external system |
Data Actions
| Action | Parameters | Description |
|---|
log_event | level, tags | Write to audit log |
update_risk_score | entity, adjustment | Modify vessel/vehicle risk score |
add_to_watchlist | entity, list, duration | Add entity to a watchlist |
tag_evidence | event_id, tags | Tag evidence for review |
Rule Priorities
Priorities range from 0 (lowest) to 100 (highest) and determine evaluation order and conflict resolution:
| Range | Usage | Examples |
|---|
| 90--100 | Critical safety rules | Exclusion zone breach, watchlist hit |
| 70--89 | High-priority security | Perimeter breach, unauthorized access |
| 50--69 | Standard operations | Motion alerts, routine notifications |
| 30--49 | Low-priority monitoring | Logging, analytics triggers |
| 0--29 | Background/housekeeping | Metric 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"
}'