Files
attune/crates/core-timer-sensor/README.md
2026-02-04 17:46:30 -06:00

404 lines
12 KiB
Markdown

# Attune Timer Sensor
A standalone sensor daemon for the Attune automation platform that monitors timer-based triggers and emits events. This sensor manages multiple concurrent timer schedules based on active rules.
## Overview
The timer sensor is a lightweight, event-driven process that:
- Listens for rule lifecycle events via RabbitMQ
- Manages per-rule timer tasks dynamically
- Emits events to the Attune API when timers fire
- Supports interval-based, cron-based, and datetime-based timers
- Authenticates using service account tokens
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Timer Sensor Process │
│ │
│ ┌────────────────┐ ┌──────────────────┐ │
│ │ Rule Lifecycle │───▶│ Timer Manager │ │
│ │ Listener │ │ │ │
│ │ (RabbitMQ) │ │ ┌──────────────┐ │ │
│ └────────────────┘ │ │ Rule 1 Timer │ │ │
│ │ ├──────────────┤ │ │
│ │ │ Rule 2 Timer │ │───┐ │
│ │ ├──────────────┤ │ │ │
│ │ │ Rule 3 Timer │ │ │ │
│ │ └──────────────┘ │ │ │
│ └──────────────────┘ │ │
│ │ │
│ ┌────────────────┐ │ │
│ │ API Client │◀──────────────────────────┘ │
│ │ (Create Events)│ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ ▲
│ Events │ Rule Lifecycle
▼ │ Messages
┌─────────────────┐ ┌─────────────────┐
│ Attune API │ │ RabbitMQ │
└─────────────────┘ └─────────────────┘
```
## Features
- **Per-Rule Timers**: Each rule gets its own independent timer task
- **Dynamic Management**: Timers start/stop automatically based on rule lifecycle
- **Multiple Timer Types**:
- **Interval**: Fire every N seconds/minutes/hours/days
- **Cron**: Fire based on cron expression (planned)
- **DateTime**: Fire at a specific date/time
- **Resilient**: Retries event creation with exponential backoff
- **Secure**: Token-based authentication with trigger type restrictions
- **Observable**: Structured JSON logging for monitoring
## Installation
### From Source
```bash
cargo build --release --package core-timer-sensor
sudo cp target/release/attune-core-timer-sensor /usr/local/bin/
```
### Using Cargo Install
```bash
cargo install --path crates/core-timer-sensor
```
## Configuration
### Environment Variables
The sensor requires the following environment variables:
| Variable | Required | Description | Example |
|----------|----------|-------------|---------|
| `ATTUNE_API_URL` | Yes | Base URL of the Attune API | `http://localhost:8080` |
| `ATTUNE_API_TOKEN` | Yes | Service account token | `eyJhbGci...` |
| `ATTUNE_SENSOR_REF` | Yes | Sensor reference (must be `core.timer`) | `core.timer` |
| `ATTUNE_MQ_URL` | Yes | RabbitMQ connection URL | `amqp://localhost:5672` |
| `ATTUNE_MQ_EXCHANGE` | No | RabbitMQ exchange name | `attune` (default) |
| `ATTUNE_LOG_LEVEL` | No | Logging verbosity | `info` (default) |
### Example: Environment Variables
```bash
export ATTUNE_API_URL="http://localhost:8080"
export ATTUNE_API_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
export ATTUNE_SENSOR_REF="core.timer"
export ATTUNE_MQ_URL="amqp://localhost:5672"
export ATTUNE_LOG_LEVEL="info"
attune-core-timer-sensor
```
### Example: stdin Configuration
```bash
echo '{
"api_url": "http://localhost:8080",
"api_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sensor_ref": "core.timer",
"mq_url": "amqp://localhost:5672",
"mq_exchange": "attune",
"log_level": "info"
}' | attune-core-timer-sensor --stdin-config
```
## Service Account Setup
Before running the sensor, you need to create a service account with the appropriate permissions:
```bash
# Create service account (requires admin token)
curl -X POST http://localhost:8080/service-accounts \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "sensor:core.timer",
"scope": "sensor",
"description": "Timer sensor for interval-based triggers",
"ttl_hours": 72,
"metadata": {
"trigger_types": ["core.timer"]
}
}'
# Response will include the token (save this - it's only shown once!)
{
"identity_id": 123,
"name": "sensor:core.timer",
"scope": "sensor",
"token": "eyJhbGci...", # Use this as ATTUNE_API_TOKEN
"expires_at": "2025-01-30T12:34:56Z" # 72 hours from now
}
```
**Important**:
- The token is only displayed once. Store it securely!
- Sensor tokens expire after 24-72 hours and must be rotated
- Plan to rotate the token before expiration (set up monitoring/alerts)
## Timer Configuration
Rules using the `core.timer` trigger must provide configuration in `trigger_params`:
### Interval Timer
Fires every N units of time:
```json
{
"type": "interval",
"interval": 30,
"unit": "seconds" // "seconds", "minutes", "hours", "days"
}
```
Examples:
- Every 5 seconds: `{"type": "interval", "interval": 5, "unit": "seconds"}`
- Every 10 minutes: `{"type": "interval", "interval": 10, "unit": "minutes"}`
- Every 1 hour: `{"type": "interval", "interval": 1, "unit": "hours"}`
- Every 1 day: `{"type": "interval", "interval": 1, "unit": "days"}`
### DateTime Timer
Fires at a specific date/time (one-time):
```json
{
"type": "date_time",
"fire_at": "2025-01-27T15:00:00Z"
}
```
### Cron Timer (Planned)
Fires based on cron expression:
```json
{
"type": "cron",
"expression": "0 0 * * *" // Daily at midnight
}
```
**Note**: Cron timers are not yet implemented.
## Running the Sensor
### Development
```bash
# Terminal 1: Start dependencies
docker-compose up -d postgres rabbitmq
# Terminal 2: Start API
cd crates/api
cargo run
# Terminal 3: Start sensor
export ATTUNE_API_URL="http://localhost:8080"
export ATTUNE_API_TOKEN="your_sensor_token_here"
export ATTUNE_SENSOR_REF="core.timer"
export ATTUNE_MQ_URL="amqp://localhost:5672"
cargo run --package core-timer-sensor
```
### Production (systemd)
Create a systemd service file at `/etc/systemd/system/attune-core-timer-sensor.service`:
```ini
[Unit]
Description=Attune Timer Sensor
After=network.target rabbitmq-server.service
[Service]
Type=simple
User=attune
WorkingDirectory=/opt/attune
ExecStart=/usr/local/bin/attune-core-timer-sensor
Restart=always
RestartSec=10
# Environment variables
Environment="ATTUNE_API_URL=https://attune.example.com"
Environment="ATTUNE_SENSOR_REF=core.timer"
Environment="ATTUNE_MQ_URL=amqps://rabbitmq.example.com:5671"
Environment="ATTUNE_LOG_LEVEL=info"
# Load token from file
EnvironmentFile=/etc/attune/sensor-timer.env
# Security
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
```
Create `/etc/attune/sensor-timer.env`:
```bash
ATTUNE_API_TOKEN=eyJhbGci...
```
Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable attune-core-timer-sensor
sudo systemctl start attune-core-timer-sensor
sudo systemctl status attune-core-timer-sensor
```
**Token Rotation:**
Sensor tokens expire after 24-72 hours. To rotate:
```bash
# 1. Create new service account token (via API)
# 2. Update /etc/attune/sensor-timer.env with new token
sudo nano /etc/attune/sensor-timer.env
# 3. Restart sensor
sudo systemctl restart attune-core-timer-sensor
```
Set up a cron job or monitoring alert to remind you to rotate tokens every 72 hours.
View logs:
```bash
sudo journalctl -u attune-core-timer-sensor -f
```
## Monitoring
### Logs
The sensor outputs structured JSON logs:
```json
{
"timestamp": "2025-01-27T12:34:56Z",
"level": "info",
"message": "Timer fired for rule 123, created event 456",
"rule_id": 123,
"event_id": 456
}
```
### Health Checks
The sensor verifies API connectivity on startup. Monitor the logs for:
- `"API connectivity verified"` - Sensor connected successfully
- `"Timer started for rule"` - Timer activated for a rule
- `"Timer fired for rule"` - Event created by timer
- `"Failed to create event"` - Event creation error (check token/permissions)
## Troubleshooting
### "Invalid sensor_ref: expected 'core.timer'"
The `ATTUNE_SENSOR_REF` must be exactly `core.timer`. This sensor only handles timer triggers.
### "Failed to connect to Attune API"
- Verify `ATTUNE_API_URL` is correct and reachable
- Check that the API service is running
- Ensure no firewall blocking the connection
### "Insufficient permissions to create event for trigger type 'core.timer'"
The service account token doesn't have permission to create timer events. Ensure the token's metadata includes `"trigger_types": ["core.timer"]`.
### "Failed to connect to RabbitMQ"
- Verify `ATTUNE_MQ_URL` is correct
- Check that RabbitMQ is running
- Ensure credentials are correct in the URL
### "Token expired"
The service account token has exceeded its TTL (24-72 hours). This is expected behavior.
**Solution:**
1. Create a new service account token via API
2. Update `ATTUNE_API_TOKEN` environment variable
3. Restart the sensor
**Prevention:**
- Set up monitoring to alert 6 hours before token expiration
- Plan regular token rotation (every 72 hours maximum)
### Timer not firing
1. Check that the rule is enabled
2. Verify the rule's `trigger_type` is `core.timer`
3. Check the sensor logs for "Timer started for rule"
4. Ensure `trigger_params` is valid JSON matching the timer config format
## Development
### Running Tests
```bash
cargo test --package core-timer-sensor
```
### Building
```bash
# Debug build
cargo build --package core-timer-sensor
# Release build
cargo build --release --package core-timer-sensor
```
### Code Structure
```
crates/core-timer-sensor/
├── src/
│ ├── main.rs # Entry point, initialization
│ ├── config.rs # Configuration loading (env/stdin)
│ ├── api_client.rs # Attune API communication
│ ├── timer_manager.rs # Per-rule timer task management
│ ├── rule_listener.rs # RabbitMQ message consumer
│ └── types.rs # Shared types and enums
├── Cargo.toml
└── README.md
```
## Contributing
When adding new timer types:
1. Add variant to `TimerConfig` enum in `types.rs`
2. Implement spawn logic in `timer_manager.rs`
3. Add tests for the new timer type
4. Update this README with examples
## License
MIT License - see LICENSE file for details.
## See Also
- [Sensor Interface Specification](../../docs/sensor-interface.md)
- [Service Accounts Documentation](../../docs/service-accounts.md)
- [Sensor Authentication Overview](../../docs/sensor-authentication-overview.md)