762 lines
19 KiB
Markdown
762 lines
19 KiB
Markdown
# Attune Sensor Service
|
|
|
|
## Overview
|
|
|
|
The **Sensor Service** is responsible for monitoring trigger conditions and generating events in the Attune automation platform. It bridges the gap between external systems and the rule-based automation engine.
|
|
|
|
## Architecture
|
|
|
|
### Core Responsibilities
|
|
|
|
1. **Sensor Lifecycle Management**: Load, start, stop, and restart sensors
|
|
2. **Event Monitoring**: Execute sensors to detect trigger conditions
|
|
3. **Event Generation**: Create event records when triggers fire
|
|
4. **Rule Matching**: Find matching rules and create enforcements
|
|
5. **Event Publishing**: Publish events to the message queue for processing
|
|
|
|
### Service Components
|
|
|
|
```
|
|
sensor/src/
|
|
├── main.rs # Service entry point
|
|
├── service.rs # Main service orchestrator
|
|
├── sensor_manager.rs # Manage sensor instances
|
|
├── event_generator.rs # Generate events from sensor data
|
|
├── rule_matcher.rs # Match events to rules
|
|
├── monitors/ # Different trigger monitor types
|
|
│ ├── mod.rs
|
|
│ ├── custom.rs # Execute custom sensor code
|
|
│ ├── timer.rs # Cron/interval triggers
|
|
│ ├── webhook.rs # HTTP webhook triggers
|
|
│ └── file.rs # File watch triggers
|
|
└── runtime/ # Sensor runtime execution
|
|
├── mod.rs
|
|
└── sensor_executor.rs # Execute sensor code in runtime
|
|
```
|
|
|
|
## Event Flow
|
|
|
|
```
|
|
Sensor Poll → Condition Met → Generate Event → Match Rules → Create Enforcements
|
|
↓ ↓ ↓ ↓ ↓
|
|
Database Sensor Code attune.event Rule Query attune.enforcement
|
|
↓ ↓
|
|
EventCreated Msg EnforcementCreated Msg
|
|
↓ ↓
|
|
(to Notifier) (to Executor)
|
|
```
|
|
|
|
## Database Schema
|
|
|
|
### Trigger Table
|
|
|
|
```sql
|
|
CREATE TABLE attune.trigger (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
ref TEXT NOT NULL UNIQUE, -- Format: pack.name
|
|
pack BIGINT REFERENCES attune.pack(id),
|
|
pack_ref TEXT,
|
|
label TEXT NOT NULL,
|
|
description TEXT,
|
|
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
param_schema JSONB, -- Configuration schema
|
|
out_schema JSONB, -- Output payload schema
|
|
created TIMESTAMPTZ NOT NULL,
|
|
updated TIMESTAMPTZ NOT NULL
|
|
);
|
|
```
|
|
|
|
### Sensor Table
|
|
|
|
```sql
|
|
CREATE TABLE attune.sensor (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
ref TEXT NOT NULL UNIQUE, -- Format: pack.name
|
|
pack BIGINT REFERENCES attune.pack(id),
|
|
pack_ref TEXT,
|
|
label TEXT NOT NULL,
|
|
description TEXT NOT NULL,
|
|
entrypoint TEXT NOT NULL, -- Code entry point
|
|
runtime BIGINT NOT NULL REFERENCES attune.runtime(id),
|
|
runtime_ref TEXT NOT NULL, -- e.g., "core.sensor.python3"
|
|
trigger BIGINT NOT NULL REFERENCES attune.trigger(id),
|
|
trigger_ref TEXT NOT NULL, -- e.g., "core.webhook"
|
|
enabled BOOLEAN NOT NULL,
|
|
param_schema JSONB, -- Sensor configuration schema
|
|
created TIMESTAMPTZ NOT NULL,
|
|
updated TIMESTAMPTZ NOT NULL
|
|
);
|
|
```
|
|
|
|
### Event Table
|
|
|
|
```sql
|
|
CREATE TABLE attune.event (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
trigger BIGINT REFERENCES attune.trigger(id),
|
|
trigger_ref TEXT NOT NULL, -- Preserved even if trigger deleted
|
|
config JSONB, -- Snapshot of trigger/sensor config
|
|
payload JSONB, -- Event data
|
|
source BIGINT REFERENCES attune.sensor(id),
|
|
source_ref TEXT, -- Sensor that generated event
|
|
created TIMESTAMPTZ NOT NULL,
|
|
updated TIMESTAMPTZ NOT NULL
|
|
);
|
|
```
|
|
|
|
## Sensor Types
|
|
|
|
### 1. Custom Sensors
|
|
|
|
Custom sensors execute user-defined code that polls for conditions:
|
|
|
|
```python
|
|
# Example: GitHub webhook sensor
|
|
def poll():
|
|
# Check for new GitHub events
|
|
events = check_github_api()
|
|
|
|
for event in events:
|
|
# Return event payload
|
|
yield {
|
|
"event_type": event.type,
|
|
"repository": event.repo,
|
|
"author": event.author,
|
|
"data": event.data
|
|
}
|
|
```
|
|
|
|
**Features**:
|
|
- Support multiple runtimes (Python, Node.js)
|
|
- Poll on configurable intervals
|
|
- Handle failures and retries
|
|
- Restart on errors
|
|
|
|
### 2. Timer Triggers (Built-in)
|
|
|
|
Execute actions on a schedule:
|
|
|
|
```yaml
|
|
trigger:
|
|
ref: core.timer
|
|
type: cron
|
|
schedule: "0 0 * * *" # Daily at midnight
|
|
```
|
|
|
|
**Features**:
|
|
- Cron expressions
|
|
- Interval-based (every N seconds/minutes/hours)
|
|
- Timezone support
|
|
|
|
### 3. Webhook Triggers (Built-in)
|
|
|
|
HTTP endpoints for external systems:
|
|
|
|
```yaml
|
|
trigger:
|
|
ref: core.webhook
|
|
path: /webhook/github
|
|
method: POST
|
|
auth: bearer_token
|
|
```
|
|
|
|
**Features**:
|
|
- Dynamic webhook URL generation
|
|
- Authentication (API key, bearer token, HMAC)
|
|
- Payload validation
|
|
- Path parameters
|
|
|
|
### 4. File Watch Triggers (Future)
|
|
|
|
Monitor filesystem changes:
|
|
|
|
```yaml
|
|
trigger:
|
|
ref: core.file_watch
|
|
path: /var/log/app.log
|
|
patterns: ["ERROR", "FATAL"]
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Service Configuration
|
|
|
|
```yaml
|
|
sensor:
|
|
enabled: true
|
|
poll_interval: 30 # Default poll interval (seconds)
|
|
max_concurrent_sensors: 100 # Max sensors running concurrently
|
|
sensor_timeout: 300 # Sensor execution timeout (seconds)
|
|
restart_on_error: true # Restart sensors on error
|
|
max_restart_attempts: 3 # Max restart attempts before disabling
|
|
|
|
# Webhook server (if enabled)
|
|
webhook:
|
|
enabled: false
|
|
host: 0.0.0.0
|
|
port: 8083
|
|
base_path: /webhooks
|
|
|
|
# Timer triggers (if enabled)
|
|
timer:
|
|
enabled: false
|
|
tick_interval: 1 # Check timers every N seconds
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
```bash
|
|
# Override service settings
|
|
ATTUNE__SENSOR__ENABLED=true
|
|
ATTUNE__SENSOR__POLL_INTERVAL=30
|
|
ATTUNE__SENSOR__MAX_CONCURRENT_SENSORS=100
|
|
ATTUNE__SENSOR__WEBHOOK__ENABLED=true
|
|
ATTUNE__SENSOR__WEBHOOK__PORT=8083
|
|
```
|
|
|
|
## Message Queue Integration
|
|
|
|
### Consumes From
|
|
|
|
No messages consumed initially (standalone operation).
|
|
|
|
### Publishes To
|
|
|
|
#### EventCreated Message
|
|
|
|
Published to `attune.events` exchange with routing key `event.created`:
|
|
|
|
```json
|
|
{
|
|
"message_id": "uuid",
|
|
"correlation_id": "uuid",
|
|
"message_type": "EventCreated",
|
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
"payload": {
|
|
"event_id": 123,
|
|
"trigger_ref": "github.webhook",
|
|
"trigger_id": 45,
|
|
"sensor_ref": "github.listener",
|
|
"sensor_id": 67,
|
|
"payload": {
|
|
"event_type": "push",
|
|
"repository": "user/repo",
|
|
"author": "johndoe"
|
|
},
|
|
"config": {
|
|
"repo_url": "https://github.com/user/repo"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### EnforcementCreated Message
|
|
|
|
Published to `attune.events` exchange with routing key `enforcement.created`:
|
|
|
|
```json
|
|
{
|
|
"message_id": "uuid",
|
|
"correlation_id": "uuid",
|
|
"message_type": "EnforcementCreated",
|
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
"payload": {
|
|
"enforcement_id": 456,
|
|
"rule_id": 78,
|
|
"rule_ref": "github.deploy_on_push",
|
|
"event_id": 123,
|
|
"trigger_ref": "github.webhook",
|
|
"payload": {
|
|
"event_type": "push",
|
|
"repository": "user/repo",
|
|
"branch": "main"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Sensor Execution
|
|
|
|
### Sensor Manager
|
|
|
|
The `SensorManager` component:
|
|
|
|
1. **Loads Sensors**: Query database for enabled sensors
|
|
2. **Starts Sensors**: Spawn async tasks for each sensor
|
|
3. **Monitors Health**: Track sensor status and restarts
|
|
4. **Handles Errors**: Retry logic and failure tracking
|
|
|
|
```rust
|
|
pub struct SensorManager {
|
|
sensors: Arc<RwLock<HashMap<i64, SensorInstance>>>,
|
|
config: Arc<SensorConfig>,
|
|
db: PgPool,
|
|
}
|
|
|
|
impl SensorManager {
|
|
pub async fn start(&self) -> Result<()> {
|
|
let sensors = self.load_enabled_sensors().await?;
|
|
|
|
for sensor in sensors {
|
|
self.start_sensor(sensor).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn start_sensor(&self, sensor: Sensor) -> Result<()> {
|
|
let instance = SensorInstance::new(sensor, self.config.clone());
|
|
instance.start().await?;
|
|
|
|
self.sensors.write().await.insert(sensor.id, instance);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
```
|
|
|
|
### Sensor Instance
|
|
|
|
Each sensor runs in its own async task:
|
|
|
|
```rust
|
|
pub struct SensorInstance {
|
|
sensor: Sensor,
|
|
runtime: RuntimeConfig,
|
|
poll_interval: Duration,
|
|
status: Arc<RwLock<SensorStatus>>,
|
|
}
|
|
|
|
impl SensorInstance {
|
|
pub async fn start(&self) -> Result<()> {
|
|
tokio::spawn(self.run_loop());
|
|
Ok(())
|
|
}
|
|
|
|
async fn run_loop(&self) {
|
|
loop {
|
|
match self.poll().await {
|
|
Ok(events) => {
|
|
for event_data in events {
|
|
self.generate_event(event_data).await;
|
|
}
|
|
}
|
|
Err(e) => {
|
|
self.handle_error(e).await;
|
|
}
|
|
}
|
|
|
|
tokio::time::sleep(self.poll_interval).await;
|
|
}
|
|
}
|
|
|
|
async fn poll(&self) -> Result<Vec<JsonValue>> {
|
|
// Execute sensor code in runtime
|
|
// Similar to Worker's ActionExecutor
|
|
todo!()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Runtime Execution
|
|
|
|
Sensors execute in runtimes (Python, Node.js) similar to actions:
|
|
|
|
```rust
|
|
pub struct SensorExecutor {
|
|
runtime_manager: RuntimeManager,
|
|
}
|
|
|
|
impl SensorExecutor {
|
|
pub async fn execute(&self, sensor: &Sensor, config: JsonValue) -> Result<Vec<JsonValue>> {
|
|
// 1. Prepare execution environment
|
|
// 2. Inject sensor code and configuration
|
|
// 3. Execute sensor
|
|
// 4. Collect yielded events
|
|
// 5. Return event data
|
|
|
|
let runtime = self.runtime_manager.get_runtime(&sensor.runtime_ref)?;
|
|
let result = runtime.execute_sensor(sensor, config).await?;
|
|
|
|
Ok(result)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Event Generation
|
|
|
|
When a sensor detects a trigger condition:
|
|
|
|
1. **Create Event Record**: Insert into `attune.event` table
|
|
2. **Snapshot Configuration**: Capture trigger/sensor config at event time
|
|
3. **Store Payload**: Save event data from sensor
|
|
4. **Publish Message**: Send `EventCreated` to message queue
|
|
|
|
```rust
|
|
pub struct EventGenerator {
|
|
db: PgPool,
|
|
mq: MessageQueue,
|
|
}
|
|
|
|
impl EventGenerator {
|
|
pub async fn generate_event(
|
|
&self,
|
|
sensor: &Sensor,
|
|
trigger: &Trigger,
|
|
payload: JsonValue,
|
|
) -> Result<i64> {
|
|
// Create event record
|
|
let event_id = sqlx::query_scalar!(
|
|
r#"
|
|
INSERT INTO attune.event
|
|
(trigger, trigger_ref, config, payload, source, source_ref)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
RETURNING id
|
|
"#,
|
|
Some(trigger.id),
|
|
&trigger.r#ref,
|
|
self.build_config_snapshot(trigger, sensor),
|
|
&payload,
|
|
Some(sensor.id),
|
|
Some(&sensor.r#ref)
|
|
)
|
|
.fetch_one(&self.db)
|
|
.await?;
|
|
|
|
// Publish EventCreated message
|
|
self.publish_event_created(event_id, trigger, sensor, &payload).await?;
|
|
|
|
Ok(event_id)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Rule Matching
|
|
|
|
After generating an event, find matching rules:
|
|
|
|
1. **Query Rules**: Find rules for the trigger
|
|
2. **Evaluate Conditions**: Check if event matches rule conditions
|
|
3. **Create Enforcements**: Insert enforcement records
|
|
4. **Publish Messages**: Send `EnforcementCreated` to executor
|
|
|
|
```rust
|
|
pub struct RuleMatcher {
|
|
db: PgPool,
|
|
mq: MessageQueue,
|
|
}
|
|
|
|
impl RuleMatcher {
|
|
pub async fn match_rules(&self, event: &Event) -> Result<Vec<i64>> {
|
|
// Find enabled rules for this trigger
|
|
let rules = sqlx::query_as!(
|
|
Rule,
|
|
r#"
|
|
SELECT * FROM attune.rule
|
|
WHERE trigger_ref = $1 AND enabled = true
|
|
"#,
|
|
&event.trigger_ref
|
|
)
|
|
.fetch_all(&self.db)
|
|
.await?;
|
|
|
|
let mut enforcement_ids = Vec::new();
|
|
|
|
for rule in rules {
|
|
// Evaluate rule conditions
|
|
if self.evaluate_conditions(&rule, event).await? {
|
|
let enforcement_id = self.create_enforcement(&rule, event).await?;
|
|
enforcement_ids.push(enforcement_id);
|
|
}
|
|
}
|
|
|
|
Ok(enforcement_ids)
|
|
}
|
|
|
|
async fn evaluate_conditions(&self, rule: &Rule, event: &Event) -> Result<bool> {
|
|
// Evaluate JSON conditions against event payload
|
|
// Simple implementation: check if all conditions match
|
|
todo!()
|
|
}
|
|
|
|
async fn create_enforcement(&self, rule: &Rule, event: &Event) -> Result<i64> {
|
|
let enforcement_id = sqlx::query_scalar!(
|
|
r#"
|
|
INSERT INTO attune.enforcement
|
|
(rule, rule_ref, trigger_ref, event, status, payload, condition, conditions)
|
|
VALUES ($1, $2, $3, $4, 'created', $5, $6, $7)
|
|
RETURNING id
|
|
"#,
|
|
Some(rule.id),
|
|
&rule.r#ref,
|
|
&rule.trigger_ref,
|
|
Some(event.id),
|
|
event.payload.clone().unwrap_or_default(),
|
|
rule.condition,
|
|
&rule.conditions
|
|
)
|
|
.fetch_one(&self.db)
|
|
.await?;
|
|
|
|
// Publish EnforcementCreated message
|
|
self.publish_enforcement_created(enforcement_id, rule, event).await?;
|
|
|
|
Ok(enforcement_id)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Condition Evaluation
|
|
|
|
Rule conditions are evaluated against event payloads:
|
|
|
|
### Condition Format
|
|
|
|
```json
|
|
{
|
|
"conditions": [
|
|
{
|
|
"field": "payload.branch",
|
|
"operator": "equals",
|
|
"value": "main"
|
|
},
|
|
{
|
|
"field": "payload.author",
|
|
"operator": "not_equals",
|
|
"value": "bot"
|
|
}
|
|
],
|
|
"condition": "all" // or "any"
|
|
}
|
|
```
|
|
|
|
### Supported Operators
|
|
|
|
- `equals`: Exact match
|
|
- `not_equals`: Not equal
|
|
- `contains`: String contains
|
|
- `starts_with`: String prefix
|
|
- `ends_with`: String suffix
|
|
- `matches`: Regex match
|
|
- `greater_than`: Numeric comparison
|
|
- `less_than`: Numeric comparison
|
|
- `in`: Value in array
|
|
- `not_in`: Value not in array
|
|
|
|
## Error Handling
|
|
|
|
### Sensor Failures
|
|
|
|
When a sensor fails:
|
|
|
|
1. Log error with context
|
|
2. Increment failure count
|
|
3. Restart sensor (if configured)
|
|
4. Disable sensor after max retries
|
|
5. Create notification
|
|
|
|
```rust
|
|
async fn handle_sensor_error(&self, sensor_id: i64, error: Error) {
|
|
error!("Sensor {} failed: {}", sensor_id, error);
|
|
|
|
let failure_count = self.increment_failure_count(sensor_id).await;
|
|
|
|
if failure_count >= self.config.max_restart_attempts {
|
|
warn!("Sensor {} exceeded max restart attempts, disabling", sensor_id);
|
|
self.disable_sensor(sensor_id).await;
|
|
} else {
|
|
info!("Restarting sensor {} (attempt {})", sensor_id, failure_count);
|
|
self.restart_sensor(sensor_id).await;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Event Generation Failures
|
|
|
|
If event generation fails:
|
|
|
|
1. Log error
|
|
2. Retry with backoff
|
|
3. Create alert notification
|
|
4. Continue sensor operation
|
|
|
|
## Monitoring
|
|
|
|
### Metrics
|
|
|
|
- `sensors_active`: Number of active sensors
|
|
- `sensors_failed`: Number of failed sensors
|
|
- `events_generated_total`: Total events generated
|
|
- `events_generated_rate`: Events per second
|
|
- `enforcements_created_total`: Total enforcements created
|
|
- `sensor_poll_duration`: Time to poll sensor
|
|
- `event_generation_duration`: Time to generate event
|
|
- `rule_matching_duration`: Time to match rules
|
|
|
|
### Health Checks
|
|
|
|
```rust
|
|
pub struct HealthCheck {
|
|
sensor_manager: Arc<SensorManager>,
|
|
}
|
|
|
|
impl HealthCheck {
|
|
pub async fn check(&self) -> HealthStatus {
|
|
let active_sensors = self.sensor_manager.active_count().await;
|
|
let failed_sensors = self.sensor_manager.failed_count().await;
|
|
|
|
if active_sensors == 0 {
|
|
HealthStatus::Unhealthy("No active sensors".to_string())
|
|
} else if failed_sensors > 10 {
|
|
HealthStatus::Degraded(format!("{} sensors failed", failed_sensors))
|
|
} else {
|
|
HealthStatus::Healthy
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Unit Tests
|
|
|
|
```rust
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_event_generation() {
|
|
let generator = EventGenerator::new(db_pool(), mq_client());
|
|
|
|
let event_id = generator.generate_event(
|
|
&test_sensor(),
|
|
&test_trigger(),
|
|
json!({"test": "data"}),
|
|
).await.unwrap();
|
|
|
|
assert!(event_id > 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rule_matching() {
|
|
let matcher = RuleMatcher::new(db_pool(), mq_client());
|
|
|
|
let event = test_event_with_payload(json!({
|
|
"branch": "main",
|
|
"author": "alice"
|
|
}));
|
|
|
|
let enforcements = matcher.match_rules(&event).await.unwrap();
|
|
assert_eq!(enforcements.len(), 1);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Integration Tests
|
|
|
|
```rust
|
|
#[tokio::test]
|
|
async fn test_sensor_to_enforcement_flow() {
|
|
// 1. Create sensor
|
|
let sensor = create_test_sensor().await;
|
|
|
|
// 2. Create trigger
|
|
let trigger = create_test_trigger().await;
|
|
|
|
// 3. Create rule
|
|
let rule = create_test_rule(trigger.id, action.id).await;
|
|
|
|
// 4. Start sensor
|
|
sensor_manager.start_sensor(sensor).await;
|
|
|
|
// 5. Wait for event
|
|
let event = wait_for_event(trigger.id).await;
|
|
|
|
// 6. Verify enforcement created
|
|
let enforcement = wait_for_enforcement(rule.id).await;
|
|
|
|
assert_eq!(enforcement.event, Some(event.id));
|
|
}
|
|
```
|
|
|
|
## Deployment
|
|
|
|
### Docker
|
|
|
|
```dockerfile
|
|
FROM rust:1.75 as builder
|
|
WORKDIR /app
|
|
COPY . .
|
|
RUN cargo build --release --bin attune-sensor
|
|
|
|
FROM debian:bookworm-slim
|
|
RUN apt-get update && apt-get install -y \
|
|
ca-certificates \
|
|
python3 \
|
|
nodejs \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY --from=builder /app/target/release/attune-sensor /usr/local/bin/
|
|
CMD ["attune-sensor"]
|
|
```
|
|
|
|
### Kubernetes
|
|
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: attune-sensor
|
|
spec:
|
|
replicas: 2
|
|
selector:
|
|
matchLabels:
|
|
app: attune-sensor
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: attune-sensor
|
|
spec:
|
|
containers:
|
|
- name: sensor
|
|
image: attune/sensor:latest
|
|
env:
|
|
- name: ATTUNE__DATABASE__URL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: attune-db
|
|
key: url
|
|
- name: ATTUNE__MESSAGE_QUEUE__URL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: attune-mq
|
|
key: url
|
|
resources:
|
|
requests:
|
|
memory: "256Mi"
|
|
cpu: "250m"
|
|
limits:
|
|
memory: "512Mi"
|
|
cpu: "500m"
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
1. **Sensor Code Isolation**: Execute sensor code in sandboxed environments
|
|
2. **Secret Management**: Use secrets for sensor authentication (API keys, tokens)
|
|
3. **Rate Limiting**: Limit sensor poll frequency to prevent abuse
|
|
4. **Input Validation**: Validate event payloads before storage
|
|
5. **Access Control**: Restrict sensor management to authorized users
|
|
6. **Audit Logging**: Log all sensor operations and events
|
|
|
|
## Future Enhancements
|
|
|
|
1. **Distributed Sensors**: Run sensors across multiple nodes
|
|
2. **Sensor Clustering**: Group related sensors for coordination
|
|
3. **Event Deduplication**: Prevent duplicate events
|
|
4. **Event Filtering**: Pre-filter events before rule matching
|
|
5. **Sensor Hot Reload**: Update sensor code without restart
|
|
6. **Advanced Scheduling**: Complex polling schedules
|
|
7. **Webhook Security**: HMAC validation, IP whitelisting
|
|
8. **Sensor Metrics Dashboard**: Real-time sensor monitoring UI |