Files
attune/docs/packs/pack-structure.md

609 lines
16 KiB
Markdown

# Pack Structure Documentation
**Last Updated**: 2024-01-20
**Status**: Reference Documentation
---
## Overview
Attune packs are bundles of automation components (actions, sensors, triggers, rules, workflows) organized in a standardized directory structure. This document defines the canonical pack structure and file formats.
---
## Pack Directory Structure
```
packs/<pack_name>/
├── pack.yaml # Pack manifest (required)
├── README.md # Pack documentation (recommended)
├── CHANGELOG.md # Version history (recommended)
├── LICENSE # License file (recommended)
├── requirements.txt # Python dependencies (optional)
├── package.json # Node.js dependencies (optional)
├── actions/ # Action definitions
│ ├── <action_name>.yaml # Action metadata
│ ├── <action_name>.sh # Shell action implementation
│ ├── <action_name>.py # Python action implementation
│ └── <action_name>.js # Node.js action implementation
├── sensors/ # Sensor definitions
│ ├── <sensor_name>.yaml # Sensor metadata
│ ├── <sensor_name>.py # Python sensor implementation
│ └── <sensor_name>.js # Node.js sensor implementation
├── triggers/ # Trigger type definitions
│ └── <trigger_name>.yaml # Trigger metadata
├── rules/ # Rule definitions (optional)
│ └── <rule_name>.yaml # Rule metadata
├── workflows/ # Workflow definitions (optional)
│ └── <workflow_name>.yaml # Workflow metadata
├── policies/ # Policy definitions (optional)
│ └── <policy_name>.yaml # Policy metadata
├── config.schema.yaml # Pack configuration schema (optional)
├── icon.png # Pack icon (optional)
└── tests/ # Tests (optional)
├── test_actions.py
└── test_sensors.py
```
---
## File Formats
### Pack Manifest (`pack.yaml`)
The pack manifest is the main metadata file for a pack. It defines the pack's identity, version, dependencies, and configuration.
**Required Fields:**
- `ref` (string): Unique pack reference/identifier (lowercase, alphanumeric, hyphens, underscores)
- `label` (string): Human-readable pack name
- `description` (string): Brief description of the pack
- `version` (string): Semantic version (e.g., "1.0.0")
**Optional Fields:**
- `author` (string): Pack author name
- `email` (string): Author email
- `system` (boolean): Whether this is a system pack (default: false)
- `enabled` (boolean): Whether pack is enabled by default (default: true)
- `conf_schema` (object): JSON Schema for pack configuration
- `config` (object): Default pack configuration
- `meta` (object): Additional metadata
- `tags` (array): Tags for categorization
- `runtime_deps` (array): Runtime dependencies (e.g., "python3", "nodejs", "shell")
**Example:**
```yaml
ref: core
label: "Core Pack"
description: "Built-in core functionality including timer triggers and basic actions"
version: "1.0.0"
author: "Attune Team"
email: "core@attune.io"
system: true
enabled: true
conf_schema:
type: object
properties:
max_action_timeout:
type: integer
description: "Maximum timeout for action execution in seconds"
default: 300
minimum: 1
maximum: 3600
config:
max_action_timeout: 300
meta:
category: "system"
keywords:
- "core"
- "utilities"
python_dependencies:
- "requests>=2.28.0"
documentation_url: "https://docs.attune.io/packs/core"
repository_url: "https://github.com/attune-io/attune"
tags:
- core
- system
- utilities
runtime_deps:
- shell
- python3
```
---
### Action Metadata (`actions/<action_name>.yaml`)
Action metadata files define the parameters, output schema, and execution details for actions.
**Required Fields:**
- `ref` (string): Full action reference (e.g., "core.echo")
- `label` (string): Human-readable action name
- `description` (string): Action description
- `runner_type` (string): Execution runtime (shell, python, nodejs, docker)
- `entry_point` (string): Script filename to execute
**Optional Fields:**
- `enabled` (boolean): Whether action is enabled (default: true)
- `parameters` (object): Parameter definitions (JSON Schema style)
- `output_schema` (object): Output schema definition
- `parameter_delivery` (string): How parameters are delivered - `env` (environment variables), `stdin` (standard input), or `file` (temporary file). Default: `env`. **Security Note**: Use `stdin` or `file` for actions with sensitive parameters.
- `parameter_format` (string): Parameter serialization format - `dotenv` (KEY='VALUE'), `json` (JSON object), or `yaml` (YAML format). Default: `dotenv`
- `tags` (array): Tags for categorization
- `timeout` (integer): Default timeout in seconds
- `examples` (array): Usage examples
**Example:**
```yaml
ref: core.echo
label: "Echo"
description: "Echo a message to stdout"
enabled: true
runner_type: shell
entry_point: echo.sh
# Parameter delivery (optional, defaults to stdin/json)
parameter_delivery: stdin
parameter_format: json
parameters:
type: object
properties:
message:
type: string
description: "Message to echo"
default: "Hello, World!"
uppercase:
type: boolean
description: "Convert message to uppercase"
default: false
output_schema:
type: object
properties:
stdout:
type: string
description: "Standard output from the command"
exit_code:
type: integer
description: "Exit code (0 = success)"
tags:
- utility
- testing
```
---
### Action Implementation
Actions receive parameters according to the `parameter_delivery` method specified in their metadata:
- **`env`** (default): Parameters as environment variables prefixed with `ATTUNE_ACTION_`
- **`stdin`**: Parameters via standard input in the specified format
- **`file`**: Parameters in a temporary file (path in `ATTUNE_PARAMETER_FILE` env var)
**Security Warning**: Environment variables are visible in process listings. Use `stdin` or `file` for sensitive data.
**Shell Example with Environment Variables** (`actions/echo.sh`):
```bash
#!/bin/bash
set -e
# Parse parameters from environment variables
MESSAGE="${ATTUNE_ACTION_MESSAGE:-Hello, World!}"
UPPERCASE="${ATTUNE_ACTION_UPPERCASE:-false}"
# Convert to uppercase if requested
if [ "$UPPERCASE" = "true" ]; then
MESSAGE=$(echo "$MESSAGE" | tr '[:lower:]' '[:upper:]')
fi
# Echo the message
echo "$MESSAGE"
# Exit successfully
exit 0
```
**Shell Example with Stdin/JSON** (more secure):
```bash
#!/bin/bash
set -e
# Read parameters from stdin (JSON format)
read -r PARAMS_JSON
MESSAGE=$(echo "$PARAMS_JSON" | jq -r '.message // "Hello, World!"')
UPPERCASE=$(echo "$PARAMS_JSON" | jq -r '.uppercase // "false"')
# Convert to uppercase if requested
if [ "$UPPERCASE" = "true" ]; then
MESSAGE=$(echo "$MESSAGE" | tr '[:lower:]' '[:upper:]')
fi
echo "$MESSAGE"
exit 0
```
**Python Example with Stdin/JSON** (recommended for security):
```python
#!/usr/bin/env python3
import json
import sys
def read_stdin_params():
"""Read parameters from stdin."""
content = sys.stdin.read()
parts = content.split('---ATTUNE_PARAMS_END---')
params = json.loads(parts[0].strip()) if parts[0].strip() else {}
secrets = json.loads(parts[1].strip()) if len(parts) > 1 and parts[1].strip() else {}
return {**params, **secrets}
def main():
params = read_stdin_params()
url = params.get("url")
method = params.get("method", "GET")
if not url:
print(json.dumps({"error": "url parameter required"}))
sys.exit(1)
# Perform action logic
result = {
"url": url,
"method": method,
"success": True
}
# Output result as JSON
print(json.dumps(result, indent=2))
sys.exit(0)
if __name__ == "__main__":
main()
```
**Python Example with Environment Variables** (legacy, less secure):
```python
#!/usr/bin/env python3
import json
import os
import sys
def get_env_param(name: str, default=None):
"""Get action parameter from environment variable."""
env_key = f"ATTUNE_ACTION_{name.upper()}"
return os.environ.get(env_key, default)
def main():
url = get_env_param("url")
method = get_env_param("method", "GET")
if not url:
print(json.dumps({"error": "url parameter required"}))
sys.exit(1)
# Perform action logic
result = {
"url": url,
"method": method,
"success": True
}
# Output result as JSON
print(json.dumps(result, indent=2))
sys.exit(0)
if __name__ == "__main__":
main()
```
---
### Sensor Metadata (`sensors/<sensor_name>.yaml`)
Sensor metadata files define sensors that monitor for events and fire triggers.
**Required Fields:**
- `ref` (string): Full sensor reference (e.g., "core.interval_timer_sensor")
- `label` (string): Human-readable sensor name
- `description` (string): Sensor description
- `runner_type` (string): Execution runtime (python, nodejs)
- `entry_point` (string): Script filename to execute
- `trigger_types` (array): List of trigger types this sensor monitors
**Optional Fields:**
- `enabled` (boolean): Whether sensor is enabled (default: true)
- `parameters` (object): Sensor configuration parameters
- `poll_interval` (integer): Poll interval in seconds
- `tags` (array): Tags for categorization
- `meta` (object): Additional metadata
**Example:**
```yaml
ref: core.interval_timer_sensor
label: "Interval Timer Sensor"
description: "Monitors time and fires interval timer triggers"
enabled: true
runner_type: python
entry_point: interval_timer_sensor.py
trigger_types:
- core.intervaltimer
parameters:
check_interval_seconds:
type: integer
description: "How often to check if triggers should fire"
default: 1
minimum: 1
maximum: 60
poll_interval: 1
tags:
- timer
- system
- builtin
meta:
builtin: true
system: true
```
---
### Sensor Implementation
Sensors run continuously and emit events to stdout as JSON, one per line.
**Python Example (`sensors/interval_timer_sensor.py`):**
```python
#!/usr/bin/env python3
import json
import time
from datetime import datetime
def check_triggers():
"""Check configured triggers and return events to fire."""
# Load trigger instances from environment
# Check if any should fire
# Return list of events
return []
def main():
while True:
events = check_triggers()
# Output events as JSON (one per line)
for event in events:
print(json.dumps(event))
sys.stdout.flush()
# Sleep until next check
time.sleep(1)
if __name__ == "__main__":
main()
```
---
### Trigger Metadata (`triggers/<trigger_name>.yaml`)
Trigger metadata files define event types that sensors can fire.
**Required Fields:**
- `ref` (string): Full trigger reference (e.g., "core.intervaltimer")
- `label` (string): Human-readable trigger name
- `description` (string): Trigger description
- `type` (string): Trigger type (interval, cron, one_shot, webhook, custom)
**Optional Fields:**
- `enabled` (boolean): Whether trigger is enabled (default: true)
- `parameters_schema` (object): Schema for trigger instance configuration
- `payload_schema` (object): Schema for event payload
- `tags` (array): Tags for categorization
- `examples` (array): Configuration examples
**Example:**
```yaml
ref: core.intervaltimer
label: "Interval Timer"
description: "Fires at regular intervals"
enabled: true
type: interval
parameters_schema:
type: object
properties:
unit:
type: string
enum: [seconds, minutes, hours]
description: "Time unit for the interval"
interval:
type: integer
minimum: 1
description: "Number of time units between triggers"
required: [unit, interval]
payload_schema:
type: object
properties:
type:
type: string
const: interval
interval_seconds:
type: integer
fired_at:
type: string
format: date-time
required: [type, interval_seconds, fired_at]
tags:
- timer
- interval
examples:
- description: "Fire every 10 seconds"
parameters:
unit: "seconds"
interval: 10
```
---
## Pack Loading Process
When a pack is loaded, Attune performs the following steps:
1. **Parse Pack Manifest**: Read and validate `pack.yaml`
2. **Register Pack**: Insert pack metadata into database
3. **Load Actions**: Parse all `actions/*.yaml` files and register actions
4. **Load Sensors**: Parse all `sensors/*.yaml` files and register sensors
5. **Load Triggers**: Parse all `triggers/*.yaml` files and register triggers
6. **Load Rules** (optional): Parse all `rules/*.yaml` files
7. **Load Workflows** (optional): Parse all `workflows/*.yaml` files
8. **Validate Dependencies**: Check that all dependencies are available
9. **Apply Configuration**: Apply default configuration from pack manifest
---
## Pack Types
### System Packs
System packs are built-in packs that ship with Attune.
- `system: true` in pack manifest
- Installed automatically
- Cannot be uninstalled
- Examples: `core`, `system`, `utils`
### Community Packs
Community packs are third-party packs installed from repositories.
- `system: false` in pack manifest
- Installed via CLI or API
- Can be updated and uninstalled
- Examples: `slack`, `aws`, `github`, `datadog`
### Ad-Hoc Packs
Ad-hoc packs are user-created packs without code-based components.
- `system: false` in pack manifest
- Created via Web UI
- May only contain triggers (no actions/sensors)
- Used for custom webhook integrations
---
## Best Practices
### Naming Conventions
- **Pack refs**: lowercase, alphanumeric, hyphens/underscores (e.g., `my-pack`, `aws_ec2`)
- **Component refs**: `<pack_ref>.<component_name>` (e.g., `core.echo`, `slack.send_message`)
- **File names**: Match component names (e.g., `echo.yaml`, `echo.sh`)
### Versioning
- Use semantic versioning (MAJOR.MINOR.PATCH)
- Update `CHANGELOG.md` with each release
- Increment version in `pack.yaml` when releasing
### Documentation
- Include comprehensive `README.md` with usage examples
- Document all parameters and output schemas
- Provide example configurations
### Testing
- Include unit tests in `tests/` directory
- Test actions/sensors independently
- Validate parameter schemas
### Dependencies
- Specify all runtime dependencies in pack manifest
- Pin dependency versions in `requirements.txt` or `package.json`
- Test with minimum required versions
### Security
- **Use `stdin` or `file` parameter delivery for actions with sensitive data** (not `env`)
- Use `secret: true` for sensitive parameters (passwords, tokens, API keys)
- Mark actions with credentials using `parameter_delivery: stdin` and `parameter_format: json`
- Validate all user inputs
- Sanitize command-line arguments to prevent injection
- Use HTTPS for API calls with SSL verification enabled
- Never log sensitive parameters in action output
---
## Example Packs
### Minimal Pack
```
my-pack/
├── pack.yaml
├── README.md
└── actions/
├── hello.yaml
└── hello.sh
```
### Full-Featured Pack
```
slack-pack/
├── pack.yaml
├── README.md
├── CHANGELOG.md
├── LICENSE
├── requirements.txt
├── icon.png
├── actions/
│ ├── send_message.yaml
│ ├── send_message.py
│ ├── upload_file.yaml
│ └── upload_file.py
├── sensors/
│ ├── message_sensor.yaml
│ └── message_sensor.py
├── triggers/
│ ├── message_received.yaml
│ └── reaction_added.yaml
├── config.schema.yaml
└── tests/
├── test_actions.py
└── test_sensors.py
```
---
## Related Documentation
- [Pack Management Architecture](./pack-management-architecture.md)
- [Pack Management API](./api-packs.md)
- [Trigger and Sensor Architecture](./trigger-sensor-architecture.md)
- [Parameter Delivery Methods](../actions/parameter-delivery.md)
- [Action Development Guide](./action-development-guide.md) (future)
- [Sensor Development Guide](./sensor-development-guide.md) (future)