AgentOwl

Architecture Overview

How AgentOwl is built — packages, runtime model, data stores, API surface, ingress paths, and the OAuth 2.1 server.

AgentOwl receives email, text messages, and calls for your agents and delivers them to your app. It is built on Cloudflare Workers, with a self-hosted SMTP server for inbound mail.

At a high level:

  • operators manage configuration with the dashboard, the CLI, or the API
  • the Worker exposes the API and persists configuration
  • providers send inbound traffic directly to Worker ingress endpoints
  • ingress handlers normalize events and enqueue them
  • queue consumers resolve routing and dispatch to tenant endpoints

Current Package Layout

packages/shared

Shared types and protocol contracts used by both the CLI and the Worker:

  • canonical resource types such as Tenant, Binding, Endpoint, Route, and Policy
  • event envelope definitions
  • ID generation and error helpers
  • internal protocols for ingress, queue, dispatch, and reconciliation

packages/worker

Cloudflare Worker application built on Hono (migrated from a custom router) that combines several responsibilities behind one service boundary:

  • control-plane API under /v1/*
  • ingress endpoints under /ingress/*
  • authentication endpoints under /auth/* (OAuth, device code flow, API key CRUD)
  • telemetry proxy routes under /v1/admin/telemetry/* and /v1/admin/usage/*
  • queue consumer for deferred processing
  • runtime config publication to KV
  • reconciler and provider integration code (Cloudflare, Telnyx)

Hono middleware handles auth, request-id injection, idempotency keys, and audit logging.

packages/telemetry-collector

OTEL collector Worker wrapping obs-collector. Receives OpenTelemetry traces and metrics from sendmailrs and other instrumented components, and exposes them via the telemetry proxy routes on the main Worker.

packages/cli

Operator-facing CLI that talks to the Worker API. It covers:

  • authentication: login (device code flow), logout, whoami
  • tenant lifecycle (account-scoped creation within plan limits)
  • endpoint, binding, route, and policy management
  • event inspection and replay
  • log tailing
  • config validation and diff
  • version and compatibility checks
  • opt-in telemetry (enabled via AGENTOWL_TELEMETRY=true)

Runtime Model

The main production Worker combines the API, HTTP ingress, queue consumer, and maintenance tasks. The recorded agent-work release also deployed the email-ingress environment for Cloudflare Email Routing, with no HTTP routes. Other per-role environments (sms-ingress, voice-ingress, queue-consumer, reconciler) are defined in packages/worker/wrangler.toml but require their own bindings and secrets before use. Environment names do not by themselves restrict the shared entrypoint to a single handler.

Dashboard / CLI -> Worker API -> D1 / KV
Provider webhooks -> Worker ingress -> Queue -> Queue consumer -> Tenant endpoint

Main Data Stores and Bindings

  • D1: durable storage for tenants, bindings, endpoints, routes, policies, events, published config revisions, accounts (email, provider, plan, plan_limits), and api_keys (key_hash, scopes)
  • KV: published runtime configuration used by ingress resolution
  • Queues: decouple provider-facing ingress from downstream dispatch
  • sendmailrs filesystem spool: temporary on-disk spool on the OVH VPS used by the SMTP server before dispatch; no database or Redis on the VPS

Account Model

AgentOwl uses an account-based access model stored in D1:

  • accounts table: stores email, OAuth provider (GitHub/Google), plan tier, and plan limits
  • api_keys table: stores hashed API keys with scopes for programmatic access
  • OAuth login: GitHub and Google providers via /auth/login and /auth/callback/*
  • OAuth 2.1 authorization server: authorization code flow with PKCE, refresh token rotation, device code grant, and token revocation
  • Device code flow: /auth/device/code and /auth/device/token enable CLI-based login
  • API key CRUD: /auth/api-keys supports create, list, and revoke operations
  • Plan enforcement: Free plan allows 1 tenant, 100 messages/day, 3 bindings; Pro plan is waitlisted
  • Reserved slugs: infrastructure and product subdomains such as api, mx, sms, voice, www, docs, admin, status, and billing cannot be used as workspace IDs

Any authenticated user can create tenants within their plan limits (no platform-admin role required).

Telemetry Architecture

AgentOwl includes an optional telemetry stack:

  • packages/telemetry-collector: a Cloudflare Worker wrapping obs-collector that receives OTEL traces and metrics
  • sendmailrs OTEL integration: the Rust SMTP server emits traces via opentelemetry and tracing-opentelemetry crates
  • CLI opt-in: set AGENTOWL_TELEMETRY=true to enable CLI telemetry reporting
  • Proxy routes: the main Worker exposes GET /v1/admin/telemetry/* and /v1/admin/usage/* for the admin dashboard to query telemetry and usage data

API Surface

The Worker entrypoint currently registers routes for:

  • tenants
  • endpoints
  • bindings
  • routes
  • policies
  • events and replay
  • version and compatibility
  • provider sync and drift checks
  • config validate, diff, and publication
  • logs tail
  • OAuth login and callback (GitHub, Google)
  • OAuth 2.1 authorization server (authorize, token, revoke)
  • OAuth metadata endpoints (.well-known/oauth-authorization-server, .well-known/oauth-protected-resource)
  • OAuth device code flow (MCP-standard path)
  • device code flow (code, token)
  • API key management (create, list, revoke)
  • telemetry and usage proxy (admin)
  • SMS and voice ingress (/ingress/sms, /ingress/voice)
  • platform support-number ingress (/ingress/platform-sms, /ingress/platform-voice)
  • config rollback, deletion jobs, and event analytics
  • Cloudflare email ingress through the Worker email handler
  • SMTP email ingress via /ingress/smtp (receives normalized events from sendmailrs)

packages/worker/src/index.ts is the authoritative route registration.

Ingress Paths

AgentOwl accepts inbound traffic through three ingress paths:

  1. Cloudflare Email Routing — Cloudflare forwards inbound email to the Worker's email() handler.
  2. Telnyx SMS / Voice — Telnyx webhooks POST to /ingress/sms and /ingress/voice.
  3. Self-hosted SMTP (sendmailrs) — A Rust binary on an OVH VPS receives mail on port 25 and POSTs normalized events to /ingress/smtp.

All three paths normalize into a canonical event envelope, persist to D1, and enqueue for downstream dispatch.

Self-hosted SMTP Ingress (sendmailrs)

sendmailrs is a Rust binary deployed on an OVH VPS running Ubuntu 24.04. It receives email directly on port 25 via MX DNS records pointing to the VPS.

The processing pipeline has nine modules:

  1. Network / TLS — accepts connections and optionally upgrades to STARTTLS.
  2. SMTP session — implements the SMTP state machine (EHLO, MAIL FROM, RCPT TO, DATA).
  3. Recipient validation — checks the recipient against known tenants and agents.
  4. Spool — writes the raw message to a filesystem spool directory.
  5. MIME parse — decodes MIME structure, extracts body parts and attachments.
  6. Sender auth — validates SPF, DKIM, and DMARC alignment.
  7. Normalize — maps the parsed message into a NormalizedEmailEvent.
  8. Dispatch — POSTs the JSON event to the Worker's /ingress/smtp endpoint.
  9. Observability — structured logging and metrics for each pipeline stage.

On the Worker side, /ingress/smtp persists the event to the D1 events table and enqueues it for downstream dispatch, identical to the other ingress paths.

Tenant subdomain routing: the recipient address encodes tenant and agent identity. For example, hello@beta.agentowl.dev routes to tenant beta, agent hello.

Hosted inbox pages: each tenant gets an authenticated rolling 24-hour inbox view at {tenant}.agentowl.dev/inbound/email with auto-refresh, served by the Worker's pages handler. Access requires operator authentication and tenant permission.

VPS resource model: sendmailrs intentionally avoids external dependencies on the VPS. The spool is filesystem-based, rate limits are held in memory, and tenant configuration is cached from the Worker API. There is no Redis or database on the VPS.

OAuth 2.1 Authorization Server

AgentOwl implements an OAuth 2.1 authorization server for MCP client authentication. The server supports:

  • Authorization code flow with PKCE (S256 challenge method)
  • Refresh token rotation with automatic old-token invalidation
  • Device code grant (RFC 8628) for CLI and headless environments
  • Token revocation (RFC 7009)
  • Metadata discovery at standard well-known endpoints (RFC 9728, RFC 8414)

All tokens are opaque (oat_<48-hex> for access, ort_<48-hex> for refresh) and stored as SHA-256 hashes in the D1 oauth_tokens table.

Scopes: tenants:read, tenants:write, endpoints:read, endpoints:write, bindings:read, bindings:write, routes:read, routes:write, policies:read, policies:write, events:read, events:replay, config:read, config:write, admin:*. API keys get wildcard scope ["*"].

Design Priorities Visible in the Codebase

  • multi-tenant scoping across persisted resources
  • queue-first ingress for operational resilience
  • canonical event envelopes for replay and downstream dispatch
  • optimistic concurrency via resource_version and etag
  • provider abstraction without fully hiding provider-specific concerns

Current Limitations

The repository already includes pieces of the longer-term design, but some are partial:

  • provider reconciliation is present but still early and not fully productionized
  • some operational flows are stubs or simplified implementations
  • the main Worker still combines multiple roles; the separate Cloudflare email Worker does not establish full per-role deployment isolation
  • live billing and the funded outbound SMS/MMS API are implemented but unreleased; sandbox verification does not establish hosted purchase or sending availability
  • examples and external integration guides are still thin

For the design rationale, see the RFCs in docs/rfcs/ in the repository.

On this page