Skip to content

Latest commit

 

History

History
302 lines (214 loc) · 10.3 KB

File metadata and controls

302 lines (214 loc) · 10.3 KB

iii - One Engine, Three Primitives

License Docker npm PyPI Crates.io

What is iii

You start building a backend and immediately need six different tools: an API framework, a task queue, a cron scheduler, pub/sub, a state store, and an observability pipeline. Each has its own config, its own deployment, its own failure modes. A simple "process this, then notify that" workflow touches three services before you write any business logic.

iii replaces all of that with a single engine and two primitives: Function and Trigger.

A Function is anything that does work. A Trigger is what causes it to run - an HTTP request, a cron schedule, a queue message, a state change. You write the function, declare what triggers it, and the engine handles discovery, routing, retries, and observability.

One config file. One process. Everything discoverable. Think of it the way React gave frontend a single model for UI - iii gives your backend a single model for execution.

Three Concepts

Concept What it does
Function A unit of work. It receives input and optionally returns output. It can exist anywhere: locally, in the cloud, on serverless, or as a third-party HTTP endpoint. It can mutate state, invoke other functions, and modify databases.
Trigger What causes a Function to run - explicitly from code, or automatically from an event source. Examples: HTTP route, cron schedule, queue topic, state change, stream event.
Discovery Functions and triggers register and deregister themselves without configuration. Once discovered, they are available across the entire backend.

Quick Start

Install

curl -fsSL https://install.iii.dev/iii/main/install.sh | sh

This installs both the engine and iii-cli.

Override install directory or pin a version
curl -fsSL https://install.iii.dev/iii/main/install.sh | BIN_DIR=$HOME/.local/bin sh
curl -fsSL https://install.iii.dev/iii/main/install.sh | sh -s -- v0.7.0

Verify:

command -v iii && iii --version

Start the engine

iii-cli start

Open the console:

iii-cli console

Your engine is running at ws://localhost:49134 with HTTP API at http://localhost:3111.

Connect a Worker

Node.js

npm install iii-sdk
import { init } from 'iii-sdk';

const iii = init('ws://localhost:49134');

iii.registerFunction({ id: 'math.add' }, async (input) => {
  return { sum: input.a + input.b };
});

iii.registerTrigger({
  type: 'http',
  function_id: 'math.add',
  config: { api_path: 'add', http_method: 'POST' },
});
Python
pip install iii-sdk
import asyncio
from iii import init

async def main():
    iii = init("ws://localhost:49134")

    async def add(data):
        return {"sum": data["a"] + data["b"]}

    iii.register_function("math.add", add)

    iii.register_trigger(
        type="http",
        function_id="math.add",
        config={"api_path": "add", "http_method": "POST"}
    )

asyncio.run(main())
Rust
use iii_sdk::{init, InitOptions};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let iii = init("ws://127.0.0.1:49134", InitOptions::default())?;

    iii.register_function("math.add", |input| async move {
        let a = input.get("a").and_then(|v| v.as_i64()).unwrap_or(0);
        let b = input.get("b").and_then(|v| v.as_i64()).unwrap_or(0);
        Ok(json!({ "sum": a + b }))
    });

    iii.register_trigger("http", "math.add", json!({
        "api_path": "add",
        "http_method": "POST"
    }))?;

    Ok(())
}

Your function is now live at http://localhost:3111/add.

Console

The iii-console is a developer and operations dashboard for inspecting functions, triggers, traces, and real-time state. Launch it with:

iii-cli console

iii console dashboard

Modules

Module Class What it does Default
HTTP API RestApiModule Maps HTTP routes to functions via http triggers with CORS Yes
Queue QueueModule Message queue with pluggable adapters (built-in, Redis, RabbitMQ) Yes
Cron CronModule Scheduled job execution with cron expression triggers Yes
Stream StreamModule Real-time bidirectional streaming over WebSocket Yes
Pub/Sub PubSubModule Topic-based event publishing and subscription Yes
State StateModule Distributed state management with get/set/delete and state triggers Yes
KV Server KvServer Built-in key-value store used by other modules Yes
HTTP Functions HttpFunctionsModule Proxy for invoking external HTTP endpoints as functions No
Observability OtelModule OpenTelemetry traces, metrics, and logs with OTLP export No
Shell ExecModule File watcher that runs shell commands on change No

If config.yaml is missing, the engine loads all default modules. Queue and Stream adapters fall back to the built-in KV store; add Redis for production use.

SDKs

Language Package Install
Node.js iii-sdk npm install iii-sdk
Python iii-sdk pip install iii-sdk
Rust iii-sdk Add to Cargo.toml

Docker

docker pull iiidev/iii:latest

docker run -p 3111:3111 -p 49134:49134 \
  -v ./config.yaml:/app/config.yaml:ro \
  iiidev/iii:latest

Production (hardened)

docker run --read-only --tmpfs /tmp \
  --cap-drop=ALL --cap-add=NET_BIND_SERVICE \
  --security-opt=no-new-privileges:true \
  -v ./config.yaml:/app/config.yaml:ro \
  -p 3111:3111 -p 49134:49134 -p 3112:3112 -p 9464:9464 \
  iiidev/iii:latest

Docker Compose (full stack with Redis + RabbitMQ):

docker compose up -d

Docker Compose with Caddy (TLS reverse proxy):

docker compose -f docker-compose.prod.yml up -d

See the Caddy documentation for TLS and reverse proxy configuration.

Ports

Port Service
49134 WebSocket (worker connections)
3111 HTTP API
3112 Stream API
9464 Prometheus metrics

Configuration

Config files support environment expansion: ${REDIS_URL:redis://localhost:6379}.

Minimal config (no Redis required):

modules:
  - class: modules::api::RestApiModule
    config:
      host: 127.0.0.1
      port: 3111
  - class: modules::observability::OtelModule
    config:
      enabled: false
      level: info
      format: default

Protocol Summary

The engine speaks JSON messages over WebSocket. Key message types: registerfunction, invokefunction, invocationresult, registertrigger, unregistertrigger, triggerregistrationresult, registerservice, functionsavailable, ping, pong.

Invocations can be fire-and-forget by omitting invocation_id.

Repository Layout

  • src/main.rs – CLI entrypoint (iii binary)
  • src/engine/ – Worker management, routing, and invocation lifecycle
  • src/protocol.rs – WebSocket message schema
  • src/modules/ – Core modules (API, queue, cron, stream, observability, shell)
  • config.yaml – Example module configuration
  • examples/custom_queue_adapter.rs – Custom module + adapter example

Development

cargo run                                # start engine
cargo run -- --config config.yaml        # with config
cargo fmt && cargo clippy -- -D warnings # lint
make watch                               # watch mode

Building Docker images locally

docker build -t iii:local .                        # production (distroless)
docker build -f Dockerfile.debug -t iii:debug .    # debug (Debian + shell)

Docker image security: distroless runtime (no shell), non-root execution, Trivy scanning in CI, SBOM attestation, and build provenance.

Examples

See the Quickstart guide for step-by-step tutorials.

Resources

License

Elastic License 2.0 (ELv2)