Skip to main content

Engine Overview

The iii Engine is a WebSocket-based backend orchestration system that unifies your backend stack with two core primitives: Functions and Triggers.

Architecture

The engine is built in Rust and provides a high-performance, distributed runtime for backend functions. It consists of several key components:

Core Components

Engine Core

The Engine struct (src/engine/mod.rs) is the central orchestrator that manages:
  • Worker Registry: Tracks connected workers (SDK clients)
  • Function Registry: Maintains all registered functions
  • Trigger Registry: Manages trigger types and trigger instances
  • Service Registry: Houses engine modules
  • Invocation Handler: Routes and tracks function invocations
  • Channel Manager: Manages WebSocket channels for real-time communication
pub struct Engine {
    pub worker_registry: Arc<WorkerRegistry>,
    pub functions: Arc<FunctionsRegistry>,
    pub trigger_registry: Arc<TriggerRegistry>,
    pub service_registry: Arc<ServicesRegistry>,
    pub invocations: Arc<InvocationHandler>,
    pub channel_manager: Arc<ChannelManager>,
}

Workers

Workers are SDK clients that connect to the engine via WebSocket. Each worker can:
  • Register functions
  • Register triggers
  • Handle function invocations
  • Receive invocation results
Workers are identified by a unique worker_id and maintain:
  • Active function registrations
  • Active trigger registrations
  • Pending invocations
  • WebSocket channel for bidirectional communication

Functions

Functions are the basic units of work. They can:
  • Exist anywhere: locally, on cloud, serverless, or as HTTP endpoints
  • Receive input and optionally return output
  • Invoke other functions
  • Be triggered by various event sources
Functions are registered with:
  • id: Unique identifier (e.g., "math.add")
  • description: Optional description
  • request_format: Optional JSON schema for input
  • response_format: Optional JSON schema for output
  • metadata: Optional metadata
  • Handler implementation (for engine-native functions)

Triggers

Triggers define how and when functions are invoked:
  • Trigger Types: Categories of triggers (e.g., http, queue, cron)
  • Trigger Instances: Specific trigger configurations
Each trigger connects an event source to a function.

Communication Flow

How It Works

1. Initialization

The engine starts by:
  1. Loading configuration from config.yaml (or using defaults)
  2. Initializing modules (REST API, Queue, Cron, Stream, Observability)
  3. Binding to WebSocket port (default: 49134)
  4. Starting background tasks
iii --config config.yaml

2. Worker Connection

When an SDK connects:
  1. WebSocket handshake establishes connection
  2. Engine creates a Worker instance with unique ID
  3. Engine sends WorkerRegistered message
  4. Worker can now register functions and triggers

3. Function Registration

Workers register functions by sending a RegisterFunction message:
{
  "type": "registerfunction",
  "id": "math.add",
  "description": "Add two numbers",
  "request_format": {"type": "object"},
  "response_format": {"type": "object"}
}
The engine stores the function and associates it with the worker.

4. Trigger Registration

Workers register triggers to connect functions to event sources:
{
  "type": "registertrigger",
  "trigger_type": "http",
  "function_id": "math.add",
  "config": {
    "api_path": "add",
    "http_method": "POST"
  }
}
The engine routes the trigger to the appropriate module (e.g., RestApiModule for HTTP triggers).

5. Function Invocation

When a trigger fires:
  1. Module creates invocation with data payload
  2. Engine looks up function in registry
  3. Engine sends InvokeFunction message to worker
  4. Worker executes handler and returns result
  5. Engine forwards result back to caller
Invocations support:
  • Synchronous: With invocation_id for awaiting results
  • Fire-and-forget: Without invocation_id for async execution
  • Distributed tracing: Via W3C traceparent and baggage headers

6. Distributed Tracing

The engine supports OpenTelemetry for observability:
  • Automatic trace propagation across function calls
  • Span creation for invocations
  • Metrics collection (invocations, errors, latency)
  • Log correlation with trace context

Engine Configuration

The engine is configured via EngineBuilder:
EngineBuilder::new()
    .config_file_or_default("config.yaml")
    .address("0.0.0.0:49134")
    .build()
    .await?
    .serve()
    .await?;
Configuration supports:
  • Environment variable expansion: ${VAR:default}
  • Module configuration
  • Port and host customization
  • Adapter selection per module

Key Features

Hot Reloading

Functions and triggers can be registered/unregistered at runtime without restarting the engine.

Multi-Language Support

SDKs available for:
  • Node.js/TypeScript
  • Python
  • Rust
All SDKs speak the same WebSocket protocol.

Built-in Modules

The engine includes production-ready modules:
  • REST API (HTTP triggers)
  • Queue (message queue with DLQ)
  • Cron (distributed scheduling)
  • Stream (WebSocket pub/sub)
  • State (key-value store with triggers)
  • Observability (OpenTelemetry)

Adapter Pattern

Modules use adapters for pluggable backends:
  • Queue: Redis, RabbitMQ, or in-memory
  • State: Redis, KV store, or in-memory
  • Cron: Redis or KV-based coordination

Service Registry

Modules register as services, enabling:
  • Function-to-module routing
  • Module discovery
  • Cross-module communication

Performance Characteristics

  • Low latency: Sub-millisecond invocation overhead
  • High throughput: Handles thousands of concurrent invocations
  • Scalability: Horizontal scaling via Redis adapters
  • Resource efficient: Rust’s zero-cost abstractions
  • Async runtime: Built on Tokio for efficient I/O

Security

  • WebSocket connections can be secured with TLS
  • HTTP API supports CORS configuration
  • External function invocations support authentication (Bearer, Basic, Custom)
  • URL validation for HTTP invocations
  • Private IP blocking for external calls

Next Steps