# 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)
