Skip to content

colliery-io/cloacina

Repository files navigation

Cloacina

License Crates.io

Cloacina Logo

Cloacina is a workflow orchestration platform for Rust, built by Colliery Software. It runs in two complementary modes — as a library embedded inside your application for the simplest deployments, or as a standalone server (cloacina-server) that loads packaged workflows uploaded via a CLI and HTTP API. The same engine, the same packaging format, the same multi-tenant model — you pick the deployment shape that matches the team and the workload.

Cloacina exposes two execution primitives:

  • Workflows — Durable, DB-backed DAGs with retries, recovery, and multi-tenancy. Pick this when work needs to survive process restart.
  • Computation graphs — In-process, deterministic, event-driven DAGs that fire on accumulator boundaries via reactors. Pick this when work is event-driven and latency-sensitive.

Both surfaces share the runtime and compose: workflows can subscribe to reactor firings, and workflow tasks can invoke embedded computation graphs.

Cloaca is the Python wheel that ships full parity with the Rust surface for both primitives — first-class, not a feature flag.

New here? Start with When to use Cloacina (and when not), then the Features overview.

Why "Cloacina" and "Cloaca" ? Named after the Roman goddess of sewers and drainage systems, Cloacina reflects the library's purpose: efficiently moving data through processing pipelines, just as ancient Roman infrastructure managed the flow of sewage out of the city. Cloaca is the latin noun for the drain, the Cloaca Maxima is the system Cloacina presided over. (Don't read too much into it, apparently there aren't many deities of "plumbing"!)

Features

  • Two deployment modes — Embedded library inside your app, or cloacina-server loading packaged .cloacina archives over HTTP / WebSocket.
  • Two execution primitives — Durable workflows and in-process computation graphs; pick one or compose both on the same firing.
  • Resilient execution — Automatic retries, failure recovery, atomic task-completion commits, heartbeat-driven stale-claim recovery.
  • Type-safe workflows — Compile-time validation of task dependencies and data flow via the #[task] / workflow! macros.
  • Database-backed — PostgreSQL or SQLite, selected at runtime by connection URL.
  • Multi-tenant — PostgreSQL schema-based isolation; fail-closed search_path enforcement; 4-step decommission orchestration on the server.
  • Packaged workflows — Ship .cloacina packages (Rust compiled to a cdylib on load, Python as a source module tree); scaffold/validate/pack with cloacinactl package; load via HTTP API, signed (optional --require-signatures) or unsigned.
  • First-class Pythoncloaca PyPI wheel exposes the full surface; not a feature flag.
  • Client SDKs — Rust, Python, and TypeScript clients for calling a running server over HTTP/WebSocket.
  • Web UI — Operate and observe a server: workflows, executions (live event stream), triggers, computation-graph health, package upload, and API-key management.
  • Horizontal scaling — A cloacina-compiler build service and a cloacina-agent execution fleet scale the server out; stateless schedulers coordinate through the database.
  • Observability — Prometheus /metrics endpoint with the cloacina_* namespace, plus structured logs.
  • Async-first — Built on tokio for high-performance concurrent execution.
  • Content-versioned — Automatic workflow versioning based on task code and structure.

Installation

Rust library

Add Cloacina to your Cargo.toml:

[dependencies]
cloacina = "0.7.0"

async-trait = "0.1"    # Required for async task definitions
serde_json = "1.0"    # Required for context data serialization

Cloacina supports both PostgreSQL and SQLite backends. The backend is selected automatically at runtime based on your connection URL - no compile-time configuration needed.

Single-Backend Builds (Optional)

For smaller binaries, you can compile with only the backend you need:

# PostgreSQL only
cloacina = { version = "0.7.0", default-features = false, features = ["postgres", "macros"] }

# SQLite only
cloacina = { version = "0.7.0", default-features = false, features = ["sqlite", "macros"] }

Python bindings (cloaca)

pip install cloaca               # default (both backends)
pip install cloaca[sqlite]       # SQLite only
pip install cloaca[postgres]     # PostgreSQL only

See the Python quick start for usage.

cloacinactl CLI

The operator + developer CLI (bundling the daemon as cloacinactl daemon):

curl -fsSL https://get.cloacina.dev/install.sh | bash

See Installing cloacinactl for version pinning, system-wide installs, and supported platforms.

Quick Start

Here's a simple example that demonstrates the basic usage:

use cloacina::*;

// Define a simple task
#[task(
    id = "process_data",
    dependencies = []
)]
async fn process_data(context: &mut Context<serde_json::Value>) -> Result<(), TaskError> {
    // Your business logic here
    context.insert("processed", serde_json::json!(true))?;
    println!("Data processed successfully!");
    Ok(())
}

// Create the workflow
let workflow = workflow! {
    name: "my_workflow",
    description: "A simple workflow",
    tasks: [process_data]
};

// Initialize the runner with a database
let runner = DefaultRunner::new("postgresql://user:pass@localhost/dbname").await?;

// Execute the workflow (await blocks until terminal state or timeout)
let result = runner.execute("my_workflow", Context::new()).await?;

For service-mode usage (running cloacina-server, uploading packaged workflows, executing over the HTTP API), see the platform tutorials.

Multi-Tenancy

Cloacina supports multi-tenant deployments with complete data isolation. PostgreSQL is the supported backend for production multi-tenancy.

Embedded mode — per-tenant runner

When you're embedding Cloacina as a library, construct one DefaultRunner per tenant pinned to a dedicated schema:

// Each tenant gets their own PostgreSQL schema
let tenant_a = DefaultRunner::with_schema(
    "postgresql://user:pass@localhost/cloacina",
    "tenant_a"
).await?;

let tenant_b = DefaultRunner::with_schema(
    "postgresql://user:pass@localhost/cloacina",
    "tenant_b"
).await?;

// Or using the builder pattern
let runner = DefaultRunner::builder()
    .database_url("postgresql://user:pass@localhost/cloacina")
    .schema("my_tenant")
    .build()
    .await?;

Server mode — provisioned tenants over the HTTP API

When you're running cloacina-server, tenants are provisioned and decommissioned via the CLI and HTTP API. The server's TenantRunnerCache keeps a runner per tenant, with fail-closed search_path enforcement at the DAL.

# Create a tenant (schema + admin key)
cloacinactl --profile prod tenant create acme

# Decommission a tenant (4-step teardown:
#   revoke keys → evict runner → evict DB cache → drop schema)
cloacinactl --profile prod tenant delete acme

See Configure a Multi-Tenant Deployment for the operational surface and Decommission a Tenant for the teardown recipe.

SQLite file-based isolation (single-tenant per file)

For non-production setups, SQLite gives you isolation by file path:

let tenant_a = DefaultRunner::new("sqlite://./tenant_a.db").await?;
let tenant_b = DefaultRunner::new("sqlite://./tenant_b.db").await?;

Properties

  • Zero collision risk — Impossible for tenants to access each other's data.
  • No query changes — All existing DAL code works unchanged; multi-tenancy is enforced at the connection level.
  • Performance — No overhead from filtering every query.
  • Clean separation — Each tenant can run different schema versions, and decommissioning a tenant drops the schema cleanly.

Repository Structure

cloacina/
  crates/                          # 11 Rust crates
    cloacina/                      # Core workflow + computation graph engine
    cloacina-macros/               # Procedural macros (#[task], #[workflow], #[reactor], ...)
    cloacina-build/                # Build-time helpers shared across packaged-workflow crates
    cloacina-compiler/             # cloacina-compiler service (compiles .cloacina archives)
    cloacina-computation-graph/    # CG runtime helpers
    cloacina-python/               # PyO3 bindings (PyPI: cloaca)
    cloacina-server/               # cloacina-server HTTP+WS service
    cloacina-testing/              # Shared test fixtures
    cloacina-workflow/             # Workflow plugin trait + types (host-side)
    cloacina-workflow-plugin/      # Workflow plugin trait + 9-method FFI vtable
    cloacinactl/                   # CLI (operator + developer + bundled daemon)
  charts/cloacina-server/          # Helm chart (with embedded local Postgres subchart)
  examples/
    tutorials/                     # Step-by-step learning path
    features/                      # Feature showcases (filtered-reactor, multi-tenant, ...)
    performance/                   # Benchmarks
  tests/python/                    # Python integration tests
  docs/                            # Documentation site
  install.sh                       # One-line installer (served at get.cloacina.dev)

Documentation

Complete Documentation & User Guide

Start here:

Additional resources:

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

About

Embedded workflow orchestration library for Rust and Python. Build resilient task pipelines with automatic retries, state persistence, and dependency resolution — no external services required.

Topics

Resources

License

Apache-2.0, Unknown licenses found

Licenses found

Apache-2.0
LICENSE
Unknown
LICENSE-HEADER.txt

Stars

Watchers

Forks

Contributors