Time, as a first-class concern
tflo — short for “temporal flow” — is an embeddable temporal engine.
It models experience and business logic as timestamped events and temporal rules: event-time, absence, windows, lateness, replay, and provenance. One pure engine — deterministic, testable, replayable. You decide where it runs.
Where it fits: at the edge, in the browser (WASM), embedded in your Rust service, or alongside the Flink, Esper, or Kafka Streams cluster you already operate. Composition, not substitution.
Time as data
Processing-time vs event-time, lateness, and replay.
Provable temporal logic
Deterministic, testable rules that prove why a pattern fired.
Production superpowers
What-if, roll-back, replay, and audit trails out of the box.
Mental model vs RxJS / XState / Signals
How tflo compares to the tools you already know.
+------------------------------------------------------------+ | | | Connectors: tflo-connect-kafka tflo-connect-mqtt | | Sinks: tflo-sink-influx tflo-arrow | | Policy: tflo-cel tflo-rhai tflo-rego | | │ | | ▼ | | Catalogs: tflo-ops (generic) tflo-fintech (finance) | | │ | | ▼ | | Engine: tflo-core | | graph + windowing + keyed exec | | timers + dedup + metrics | | typed Absent + async contracts | | │ | | ▼ | | State: tflo-state-files tflo-state-s3 | | Browser: tflo-wasm | | | +------------------------------------------------------------+
Where tflo fits
Composition with the engines you already operate — not substitution.
At the edge, mature engine in the center
tflo runs on sensor gateways and in browsers, emitting typed
Signal events. Those signals flow into Flink / Esper / Kafka Streams
for cross-source aggregation.
[sensor gateways] [browser dashboards] [embedded services] │ │ │ └── tflo signal emission (typed Signal events) ──┐ │ [Kafka / NATS / MQTT] │ [Flink / Esper / Kafka Streams]
Inside a host engine, per key
The host owns watermarks, exactly-once, scaling, savepoints. tflo runs inside a
per-key slot (KeyedProcessFunction, DoFn,
Processor) and contributes signal logic and typed Absent.
[Flink job] │ ┌───────┴────────┐ │ KeyedProcess │ ← Flink owns watermarks, │ Function │ exactly-once, scaling │ ┌────────┐ │ │ │ tflo │ │ ← tflo owns signal logic, │ │ graph │ │ typed Absent │ └────────┘ │ └────────────────┘
Same code for batch and streaming
The same detectors that run in production also run against archived events.
Iterator::tflo(...) works identically on
Vec<Event> (backfill) and Stream<Event>
(live) — deterministic output.
// Live let live = stream.tflo(|t| { ... }); // Backfill — same closure let back = vec.tflo(|t| { ... }) .collect();
Where each lane wins
Composition, not competition — honest about where each tool fits.
Rows where tflo shows “—” are deliberate non-goals; for those, host tflo inside Flink / Beam and the host engine covers them.
| Capability | tflo | Flink / Esper / KStreams | Hand-rolled state |
|---|---|---|---|
| Runs in browser / WASM | ✓ | ✗ | case-by-case |
| Runs on edge gateways under 1 GB RAM | ✓ | ✗ (JVM) | case-by-case |
| Embeds in your Rust service, no sidecar | ✓ | ✗ | ✓ |
Typed missing-value reasons (not NaN) | ✓ | ✗ | ✗ |
| Identical code for batch and streaming | ✓ | partial | ✗ |
| Keyed execution, snapshot-safe state | ✓ | ✓ | ✗ |
| Event-time timers, per-key heap | ✓ | ✓ | ✗ |
| Built-in event detectors (cross, glitch, runt, pulse) | ✓ | partial | ✗ |
| No JobManager / no separate state backend to operate | ✓ | ✗ | ✓ |
| Streaming SQL for analyst self-service* | — | ✓ | ✗ |
| Cross-shard / global watermark, multi-shard joins* | — | ✓ | ✗ |
| Exactly-once via two-phase-commit sinks* | — | ✓ | ✗ |
At-least-once + idempotent sinks + Deduplicator | ✓ | ✓ | ✗ |
| Broad connector catalog (Debezium CDC, dozens of cloud sources)* | Kafka / MQTT / Arrow | ✓ | ✗ |
* Rows where tflo shows “—” are deliberately non-goals: tflo doesn't try to replace the host engine on these axes. Host tflo inside Flink / Beam / Kafka Streams and the host covers them — see positioning.
Everything you need
A complete toolkit for turning noisy events into clean lifecycle events.
Streaming-First
Iterator and Stream traits. Every node works
on live data without accumulating the full history in memory.
Keyed Execution
Checkpoint and Restore
tflo-state-files and
tflo-state-s3. Crash recovery and fast restart without
recomputing from the beginning of time.
Lifecycle Events
Event Detectors
Custom Runtime Nodes
Operator trait via
Comp::custom_node — first-class alongside the built-ins.
Policy Filters
tflo-cel,
tflo-rhai, and tflo-rego. Update a policy without recompiling.
Async Native
Stream adapters with Tokio. Integrate directly
into async pipelines with backpressure support.
Kafka Integration
tflo-connect-kafka.
Ingest events and publish results with built-in serde support.
Irregular Data, No Resampling
Operator Catalogs
tflo-core; operators live in
tflo-ops (generic) and tflo-fintech (finance plugin).
Domain catalogs evolve on their own cadence — finance is one among RF,
observability, fraud, IoT.
Stream Composition
Signal Conditioning
Event-Time Timers
(fire_ts, seq) ordering;
snapshot-safe serialization via postcard. The clean way to express
dwell timers, watchdog timeouts, and absence detection.
Idempotent Sinks
Deduplicator primitive: process-local cache plus a durable
per-sink window backed by AsyncStateStore. tflo's answer to
exactly-once is at-least-once + idempotent, not 2PC.
Pluggable Metrics
Metrics trait fires on keyed-step latency, timer fires,
checkpoint commit and failure, late-record drops. No-op default; wire
Prometheus / statsd / OTEL in one place.
Connector Ecosystem
KafkaShardRouter with rebalance fencing), MQTT
(BoundedSet for QoS-2 dedup), Influx line-protocol sink, and
Arrow / Parquet for batch replay and archive.
Event-Pattern Matching
tflo-cep composes typed signals into multi-event domain signals
— abandoned_cart, engaged_with_product,
rage_click. Six-method closure API. Same engine runs in Rust and in
the browser via tflo-cep-wasm.
Browser SDK
Start in 5 lines
A declarative computation graph over any iterator or stream of events.
use tflo_core::prelude::*; use tflo_ops::prelude::*; // A noisy domain event — e.g. a sensor reading or detection confidence #[derive(Clone)] struct Detection { ts: i64, confidence: f64 } // Smooth confidence and flag threshold crossings let events = detections.into_iter() .tflo(|t| { t.timestamp(|d| d.ts); let confidence = t.prop(|d| d.confidence); let smoothed = confidence.sma(5_u64.secs()); let threshold = t.constant(0.8); smoothed.cross(&threshold) }) .collect::<Vec<_>>(); // Same code on a Vec<Detection> (backfill) or a Stream<Detection> (live)
Ready to model your events?
Add tflo-core and tflo-ops to your
Cargo.toml and start building real-time event-processing pipelines today.