Quick Start

Get tflo running in your project in under 5 minutes. The Rust crate powers server-side streaming pipelines; the TypeScript SDK wraps the same engine (compiled to WebAssembly) for browser event analytics. Pick your language.

Use the Rust crates for server-side streaming pipelines, edge gateways, embedded services, or batch / replay over historical data. The same crate compiles to WebAssembly for browser indicators (tflo-wasm) and browser pattern matching (tflo-cep-wasm).

Prerequisites

  • Rust 1.88+ (MSRV). Install with rustup.
  • An existing Rust project, or create one with cargo new my-tflo-app.

Installation

Add the core crate to your Cargo.toml:

[dependencies]
# tflo is pre-1.0 and not yet on crates.io — depend on it via git.
tflo-core = { git = "https://github.com/matt-cochran/tflo" }
tflo-ops  = { git = "https://github.com/matt-cochran/tflo" }

For async stream support, add the async feature:

[dependencies]
tflo-core = { git = "https://github.com/matt-cochran/tflo", features = ["async"] }

Your First Computation

tflo turns any Rust Iterator of domain events into a streaming computation graph. Here’s a simple moving average that smooths a noisy stream:

//! Full runnable example: tflo-examples/examples/docs-quick-start
use tflo_core::prelude::*;
use tflo_ops::prelude::*;

// A noisy domain event — e.g. a sensor reading or detection confidence.
#[derive(Clone)]
struct Reading {
    ts: i64,
    value: f64,
}

fn main() {
    let readings = vec![
        Reading { ts: 1000, value: 100.0 },
        Reading { ts: 2000, value: 101.0 },
        Reading { ts: 3000, value: 99.0 },
        Reading { ts: 4000, value: 102.0 },
        Reading { ts: 5000, value: 103.0 },
    ];

    let smoothed: Vec<f64> = readings.into_iter()
        .tflo(|t| {
            t.timestamp(|x| x.ts);
            let value = t.prop(|x| x.value);
            value.sma(3_u64.secs())
        })
        .collect();

    println!("{:?}", smoothed);
}

What’s happening here:

  1. .tflo(|t| { ... }) builds a computation graph over your iterator.
  2. t.timestamp(|x| x.ts) tells tflo which field is the event timestamp.
  3. t.prop(|x| x.value) extracts the numeric property for computation.
  4. value.sma(3_u64.secs()) computes a 3-second Simple Moving Average.
  5. The output is a Vec<f64> — one value per input event.

Multiple Outputs

Want SMA and EMA simultaneously? Just compute both in the same graph:

let results: Vec<(f64, f64)> = readings.into_iter()
    .tflo(|t| {
        t.timestamp(|x| x.ts);
        let value = t.prop(|x| x.value);
        let sma = value.sma(3_u64.secs());
        let ema = value.ema(3_u64.secs());
        (sma, ema)
    })
    .collect();

Enrichment Pattern

Use .with() instead of .tflo() to keep the original event alongside computed values:

let enriched: Vec<(Reading, f64)> = readings.into_iter()
    .with(|t| {
        t.timestamp(|x| x.ts);
        let value = t.prop(|x| x.value);
        value.sma(3_u64.secs())
    })
    .collect();

Keyed Execution (Per-Source)

When events arrive from many emitters — per sensor, per host, per instrument — use .tflo_keyed() to give each key its own isolated state:

//! Full runnable example: tflo-examples/examples/docs-quick-start
use tflo_core::prelude::*;
use tflo_ops::prelude::*;

// Events from many emitters — per-sensor, per-host, per-instrument.
#[derive(Clone)]
struct Reading {
    source: String,
    ts: i64,
    value: f64,
}

let results: Vec<Result<_, TFloError>> = readings.into_iter()
    .tflo_keyed(
        |t| t.source.clone(),              // key function — partition by source
        OutOfOrderPolicy::Error,            // fail on out-of-order timestamps
        |t| {
            t.timestamp(|x| x.ts);
            let value = t.prop(|x| x.value);
            value.sma(10_u64.secs())
        },
    )
    .collect();

// Each source gets independent window state.
// See tflo-core/src/keyed.rs for the full API.

Next Steps