Providers¶
Providers are the execution endpoints in Acteon. They receive actions and perform the actual work — sending emails, posting Slack messages, calling webhooks, or any custom operation.
Provider Architecture¶
flowchart LR
G[Gateway] --> R[Provider Registry]
R --> M[Messaging & on-call]
R --> C[Cloud services]
R --> GEN[Generic HTTP]
R --> CUST[Custom provider]
M --> M1[Slack / Teams / Discord]
M --> M2[PagerDuty / OpsGenie / VictorOps]
M --> M3[Pushover / Telegram / WeChat]
M --> M4[Email / Twilio SMS]
C --> C1[AWS: SNS, Lambda, SQS, S3, SES, EventBridge, EC2, ASG]
C --> C2[Azure: Blob Storage, Event Hubs]
C --> C3[GCP: Cloud Storage, Pub/Sub]
GEN --> GEN1[Webhook] The gateway looks up the provider by the action's provider field and dispatches accordingly. All providers — whether built-in or user-written — implement the same Provider trait and participate uniformly in circuit breaking, health checks, per-provider metrics, and tenant quotas.
Provider Traits¶
Acteon defines two provider traits:
Provider (Strongly Typed)¶
pub trait Provider: Send + Sync {
fn name(&self) -> &str;
async fn execute(
&self,
action: &Action,
) -> Result<ProviderResponse, ProviderError>;
async fn health_check(&self) -> Result<(), ProviderError>;
}
DynProvider (Object-Safe)¶
For dynamic dispatch via trait objects:
#[async_trait]
pub trait DynProvider: Send + Sync {
fn name(&self) -> &str;
async fn execute(
&self,
action: &Action,
) -> Result<ProviderResponse, ProviderError>;
async fn health_check(&self) -> Result<(), ProviderError>;
}
Any type implementing Provider automatically implements DynProvider through a blanket implementation.
Implementing a Custom Provider¶
use acteon_provider::{DynProvider, ProviderError};
use acteon_core::{Action, ProviderResponse};
use async_trait::async_trait;
struct MyWebhookProvider {
client: reqwest::Client,
base_url: String,
}
#[async_trait]
impl DynProvider for MyWebhookProvider {
fn name(&self) -> &str {
"my-webhook"
}
async fn execute(
&self,
action: &Action,
) -> Result<ProviderResponse, ProviderError> {
let response = self.client
.post(&self.base_url)
.json(&action.payload)
.send()
.await
.map_err(|e| ProviderError::Connection(e.to_string()))?;
if response.status().is_success() {
let body = response.json().await
.unwrap_or(serde_json::json!({}));
Ok(ProviderResponse::success(body))
} else {
Err(ProviderError::ExecutionFailed(
format!("HTTP {}", response.status())
))
}
}
async fn health_check(&self) -> Result<(), ProviderError> {
self.client
.get(format!("{}/health", self.base_url))
.send()
.await
.map_err(|e| ProviderError::Connection(e.to_string()))?;
Ok(())
}
}
Registering Providers¶
Register providers with the gateway via the builder:
use std::sync::Arc;
let gateway = GatewayBuilder::new()
.provider(Arc::new(EmailProvider::new(smtp_config)))
.provider(Arc::new(SlackProvider::new(slack_config)))
.provider(Arc::new(PagerDutyProvider::new(pagerduty_config)))
.provider(Arc::new(OpsGenieProvider::new(opsgenie_config)))
.provider(Arc::new(TwilioProvider::new(twilio_config)))
.provider(Arc::new(TeamsProvider::new(teams_config)))
.provider(Arc::new(DiscordProvider::new(discord_config)))
.provider(Arc::new(TelegramProvider::new(telegram_config)))
.provider(Arc::new(MyWebhookProvider {
client: reqwest::Client::new(),
base_url: "https://api.example.com".into(),
}))
.build()?;
Actions are routed to providers by matching the action's provider field to the provider's name(). Most real deployments register providers declaratively from TOML via [[providers]] blocks rather than building them in Rust — see Configuration for the TOML schema.
Cloud providers are feature-gated
AWS, Azure, and GCP providers live in the acteon-aws, acteon-azure, and acteon-gcp crates and are gated behind individual Cargo feature flags on acteon-server (e.g. aws-sns, azure-blob, gcp-pubsub). The default cargo build does not compile any of them. Pick the flags you need, or use the aws-all / azure-all / gcp-all group flags. See the AWS, Azure, and GCP feature pages for the full flag table and IAM / auth setup.
Built-in Providers¶
Acteon ships with built-in providers organized into four categories. Every provider below is a first-class citizen: uniform circuit breaking, health checks, per-provider metrics, tenant quotas, W3C trace context propagation, and ENC[...] encrypted-secret support where applicable.
Messaging and on-call¶
Providers that deliver human-facing notifications — chat messages, SMS, push notifications, and incident paging. Widely-used providers ship in the default acteon-server binary. Niche or regional providers are opt-in behind a Cargo feature flag so the default build does not pull in integrations most deployments will never touch — enable individual flags or use the extras-alerting group flag to turn them all on.
| Provider | Crate | type | Default build? | Transport / Auth | Feature docs |
|---|---|---|---|---|---|
| Email (SMTP) | acteon-email | email | ✅ Default | SMTP (Lettre) with STARTTLS / TLS | Native providers |
| Slack | acteon-slack | slack | ✅ Default | Bot token or incoming webhook | Native providers |
| Microsoft Teams | acteon-teams | teams | ✅ Default | Incoming webhook URL | Native providers |
| Twilio (SMS / MMS) | acteon-twilio | twilio | ✅ Default | Account SID + Auth Token (HTTP Basic) | Native providers |
| PagerDuty | acteon-pagerduty | pagerduty | ✅ Default | Events API v2 routing key | Native providers |
| Discord | acteon-discord | discord | --features discord | Webhook URL | Native providers |
| OpsGenie | acteon-opsgenie | opsgenie | --features opsgenie | Alert API v2 integration key (US / EU regions) | OpsGenie |
| VictorOps / Splunk On-Call | acteon-victorops | victorops | --features victorops | REST endpoint routing key | VictorOps |
| Pushover | acteon-pushover | pushover | --features pushover | App token + user / group key | Pushover |
| Telegram Bot | acteon-telegram | telegram | --features telegram | Bot token + chat ID | Telegram |
| WeChat Work (企业微信) | acteon-wechat | wechat | --features wechat | Corp ID + corp secret + agent ID (lazy token refresh) | WeChat Work |
How opt-in flags behave
If you run a binary compiled without the relevant feature and put type = "opsgenie" in your TOML config, the server fails fast at startup with an actionable error: provider 'X': unknown type 'opsgenie'. Hint: enable the 'opsgenie' feature on acteon-server (cargo build --features opsgenie). No silent fallthrough.
AWS cloud services¶
AWS providers are gated behind individual Cargo feature flags on acteon-server so the default build doesn't pay the cost of eight AWS SDK crates. Enable aws-all for the full set, or pick individual flags. See AWS providers for config, payload shapes, and IAM requirements.
Provider type | AWS service | Feature flag | Use case |
|---|---|---|---|
aws-sns | Simple Notification Service | aws-sns | Publish to topics (fan-out to email/SMS/Lambda/HTTP) |
aws-lambda | Lambda | aws-lambda | Synchronous or async function invocation |
aws-eventbridge | EventBridge | aws-eventbridge | Publish custom events to event buses |
aws-sqs | Simple Queue Service | aws-sqs | Enqueue messages for async processing |
aws-s3 | S3 | aws-s3 | Write objects, trigger event notifications |
aws-ses | Simple Email Service | aws-ses | Send transactional / bulk email |
aws-ec2 | EC2 | aws-ec2 | Start / stop / reboot instances (ops automation) |
aws-autoscaling | Auto Scaling | aws-autoscaling | Trigger scaling actions |
Azure and GCP¶
Provider type | Cloud service | Feature flag | Feature docs |
|---|---|---|---|
azure-blob | Azure Blob Storage | azure-blob | Azure providers |
azure-eventhubs | Azure Event Hubs | azure-eventhubs | Azure providers |
gcp-storage | Google Cloud Storage | gcp-storage | GCP providers |
gcp-pubsub | Google Cloud Pub/Sub | gcp-pubsub | GCP providers |
Generic and testing¶
| Provider | Crate | type | Purpose |
|---|---|---|---|
| Webhook | acteon-webhook | webhook | Generic HTTP dispatcher — Bearer / Basic / API key / HMAC-SHA256 auth, custom headers, configurable method, payload modes |
| Log | acteon-provider (built-in) | log | Writes the dispatched action to the tracing log at INFO. Ships with every Acteon binary and is the default target in most example configs. Ideal for local dev, tests, and dry runs. |
| Recording | acteon-simulation | — | Test-only in-memory provider that captures every dispatched action for assertions. Used by the simulation harness — see Simulation & Testing. |
Custom providers
None of these lists are exhaustive for your deployment — any type implementing Provider / DynProvider can be registered alongside the built-ins. Acteon routes to providers purely by the name() returned from the trait implementation, so a custom Rust provider is indistinguishable from a built-in one to the gateway and rule engine.
The rest of this page drills into a representative subset of payload shapes. For the remaining providers, follow the feature-doc links in the tables above.
Email (SMTP)¶
The acteon-email crate provides an SMTP provider via the Lettre library.
Expected payload:
{
"to": "recipient@example.com",
"cc": ["cc@example.com"],
"bcc": ["bcc@example.com"],
"subject": "Email subject",
"body": "Email body (text or HTML)",
"from": "sender@example.com"
}
Slack¶
The acteon-slack crate provides a Slack messaging provider.
Expected payload:
PagerDuty¶
The acteon-pagerduty crate provides a PagerDuty Events API v2 provider for incident management. It supports triggering, acknowledging, and resolving incidents.
Configuration:
Single service (routing key only):
Multiple services:
let config = PagerDutyConfig::new()
.with_service("PABC123", "routing-key-1")
.with_service("PXYZ789", "routing-key-2")
.with_default_service("PABC123");
When multiple services are configured, each action payload can specify a service_id to target a specific service. If omitted, the default service is used. With a single service, service_id is optional.
Trigger payload:
{
"event_action": "trigger",
"service_id": "PABC123",
"summary": "CPU usage exceeded 90% on web-01",
"severity": "critical",
"source": "monitoring",
"component": "web-01",
"group": "production",
"class": "cpu",
"dedup_key": "web-01/cpu-high",
"custom_details": { "cpu_percent": 95.2 },
"images": [{ "src": "https://example.com/graph.png", "alt": "CPU graph" }],
"links": [{ "href": "https://example.com/runbook", "text": "Runbook" }]
}
Acknowledge/resolve payload:
| Field | Required | Default | Description |
|---|---|---|---|
event_action | Yes | — | "trigger", "acknowledge", or "resolve" |
service_id | No | Config default | PagerDuty service ID to route the event to |
summary | Trigger only | — | Brief description of the event |
severity | No | Config default | "critical", "error", "warning", or "info" |
source | No | Config default | Event source (e.g. hostname or service) |
dedup_key | Ack/resolve | — | Deduplication key for correlating events |
component | No | — | Logical grouping component |
group | No | — | Logical grouping (e.g. "production") |
class | No | — | Event class/type (e.g. "cpu") |
custom_details | No | — | Arbitrary key-value details |
images | No | — | Images to display in the incident |
links | No | — | Links to display in the incident |
Twilio (SMS)¶
The acteon-twilio crate provides an SMS provider via the Twilio REST API.
Expected payload:
{
"to": "+15559876543",
"body": "Your verification code is 123456",
"from": "+15551234567",
"media_url": "https://example.com/image.jpg"
}
| Field | Required | Default | Description |
|---|---|---|---|
to | Yes | — | Destination phone number (E.164 format) |
body | Yes | — | Message body text |
from | No | Config default | Sender phone number (E.164 format) |
media_url | No | — | URL of media to attach (MMS) |
Microsoft Teams¶
The acteon-teams crate provides a Microsoft Teams provider via incoming webhooks.
MessageCard payload:
{
"text": "Deployment complete",
"title": "CI/CD Pipeline",
"summary": "Build #42 passed",
"theme_color": "00FF00"
}
Adaptive Card payload:
{
"adaptive_card": {
"type": "AdaptiveCard",
"version": "1.4",
"body": [{"type": "TextBlock", "text": "Hello!"}]
}
}
At least one of text or adaptive_card must be provided.
Discord¶
The acteon-discord crate provides a Discord provider via webhooks.
Expected payload:
{
"content": "Build passed!",
"embeds": [{
"title": "Build #42",
"description": "All tests passed",
"color": 65280,
"fields": [{"name": "Branch", "value": "main", "inline": true}],
"footer": {"text": "CI/CD"}
}],
"username": "Acteon Bot",
"avatar_url": "https://example.com/avatar.png",
"tts": false
}
At least one of content or embeds must be provided.
Webhook¶
The acteon-webhook crate provides a generic HTTP webhook provider for dispatching actions to any HTTP endpoint. It supports configurable HTTP methods, authentication (Bearer, Basic, API key, HMAC-SHA256), custom headers, and flexible payload modes.
Configuration:
use acteon_webhook::{WebhookConfig, WebhookProvider, AuthMethod, HttpMethod, PayloadMode};
// Simple webhook
let provider = WebhookProvider::new(
WebhookConfig::new("https://api.example.com/webhook")
);
// Webhook with authentication and custom settings
let provider = WebhookProvider::new(
WebhookConfig::new("https://api.example.com/webhook")
.with_method(HttpMethod::Put)
.with_auth(AuthMethod::Bearer("your-token".into()))
.with_header("X-Custom-Header", "value")
.with_payload_mode(PayloadMode::PayloadOnly)
.with_timeout_secs(10)
);
// Webhook with HMAC signing
let provider = WebhookProvider::new(
WebhookConfig::new("https://api.example.com/webhook")
.with_auth(AuthMethod::HmacSha256 {
secret: "your-secret".into(),
header: "X-Signature".into(),
})
);
Expected payload:
{
"url": "https://hooks.example.com/endpoint",
"method": "POST",
"body": {
"message": "Server is down",
"severity": "critical"
},
"headers": {
"X-Custom-Header": "value"
}
}
Client SDK helpers make constructing webhook payloads easy:
| Field | Required | Default | Description |
|---|---|---|---|
url | Yes | — | Target URL for the webhook request |
method | No | POST | HTTP method (GET, POST, PUT, PATCH, DELETE) |
body | Yes | — | JSON body to send to the endpoint |
headers | No | — | Additional HTTP headers |
Authentication methods:
| Method | Description |
|---|---|
Bearer(token) | Authorization: Bearer <token> header |
Basic { username, password } | HTTP Basic authentication |
ApiKey { header, value } | Custom header with API key value |
HmacSha256 { secret, header } | HMAC-SHA256 signature of the request body |
Payload modes:
| Mode | Description |
|---|---|
FullAction | Sends the entire serialized Action as the request body |
PayloadOnly | Sends only action.payload as the request body |
Provider Errors¶
| Error | Retryable | Description |
|---|---|---|
ExecutionFailed(msg) | No | Permanent provider error |
Timeout(msg) | Yes | Execution timed out |
Connection(msg) | Yes | Network connectivity failure |
RateLimited | Yes | Provider rate limit hit |
Configuration(msg) | No | Invalid provider configuration |
The executor retries retryable errors up to max_retries times with the configured backoff strategy. Non-retryable errors immediately return ActionOutcome::Failed.
Provider Response¶
// Success response
ProviderResponse::success(json!({"message_id": "abc123"}))
// Failure response
ProviderResponse::failure(json!({"error": "invalid recipient"}))
Health Checks¶
Providers implement health_check() for monitoring. The server's /health endpoint aggregates provider health status.