Architecture
Strata is a set of libraries that run inside your application, plus a command-line tool. This page shows the layers, the crate and package for each one, what depends on what, and what the Rust and .NET engines share.
Strata runs in your process
Section titled “Strata runs in your process”There is no Strata server to deploy. You add the crates or packages to your service, and the engine runs your graph on your process’s async runtime: tokio in Rust, tasks in .NET. When a run stops, the engine saves a checkpoint to a store. A store is a client for a database you already run: a SQLite file, Postgres or Redis.
Everything around the engine stays in your hands:
- When runs start and resume. You call the engine from a web request, a queue consumer or a background job.
- Where runs happen. Strata doesn’t place runs on workers or coordinate a cluster. You partition threads across processes with your own queue (see Known limitations).
- How output reaches users. Strata ships no web server. Streamed output comes back to your code, and you forward it over whatever your service already uses.
The layers
Section titled “The layers”┌─────────────────────── your application process ───────────────────────┐│ Your code state type · nodes · edges · tools · when to run │├─────────────────────────────────────────────────────────────────────────┤│ Hosting settings · background drivers · health checks ││ AI model calls · agents · model routes · embeddings ││ Stores in-memory · SQLite · Postgres · Redis ││ Engine graphs · validation · runs · checkpoints · tools │└──────────────────────────────────┬──────────────────────────────────────┘ │ checkpoints and history, in the │ shared wire format ▼ your database: SQLite · Postgres · Redis ▲ │ reads strata CLI (a separate tool)| Layer | What it does | Rust crate | .NET package |
|---|---|---|---|
| Engine | Graphs, compile-time validation, runs, checkpoints, interrupts, retries, streaming, tools, traces and metrics. Defines the store interfaces. | datalium-strata |
Datalium.Strata |
| Code generation | Builds tool schemas and dispatch from #[strata_tool] and [StrataTool] at compile time. |
datalium-strata-macros, which you get through datalium-strata |
A source generator packed inside Datalium.Strata |
| Stores | Checkpoint and history stores: in-memory, SQLite, Postgres and Redis. Long-term memory stores for SQLite, Postgres and Redis. | datalium-strata-memory |
Datalium.Strata.Storage |
| AI | Model calls, agents, model routes, embeddings and a long-term memory helper. | datalium-strata-ai |
Datalium.Strata.AI |
| Hosting | Settings, background drivers that wake due timers, recover crashed runs and purge old threads, and a store health check. In .NET, also AddStrata() registration and OpenTelemetry wiring. |
datalium-strata-host |
Datalium.Strata.Hosting |
| Cloud client | For the Strata Cloud private beta: a checkpoint store, a decisions client and a decision worker. | datalium-strata-cloud |
Datalium.Strata.Cloud |
| CLI | The strata tool: validates and views graph definitions, lists and inspects threads, and reads checkpoint logs. |
One binary for both languages, built from cli/ |
Every crate, every package and the CLI share one version number. Installation says which ones to pick.
What depends on what
Section titled “What depends on what”The engine is the base. It depends on no other Strata layer, and every other layer depends on it.
- The engine defines the store interfaces.
ICheckpointStoreandIHistoryStorein .NET, theCheckpointStoreandHistoryStoretraits in Rust. The stores and the Cloud client implement them, and so can a store of your own. The engine has no store of its own, so to run a graph you need a stores package or your own store. - Stores, AI and the Cloud client depend only on the engine, plus their own third-party clients.
- Hosting differs by language.
Datalium.Strata.Hostingreferences the other four packages, so one reference brings all of Strata.datalium-strata-hostdepends only on the engine and the stores. In Rust you add the AI or Cloud crate yourself, and run a loop of your own, such as the decision worker, as a task of the host. - The CLI is built on the Rust engine and stores. It reads a database through the same store code the Rust engine writes with, so it has no separate copy of the layout.
The third-party libraries each layer brings:
| Layer | Rust | .NET |
|---|---|---|
| Engine | tokio, serde, and the tracing and metrics facades |
None beyond .NET itself |
| Stores | rusqlite (with SQLite built in), tokio-postgres, redis | EF Core for SQLite, Npgsql, StackExchange.Redis |
| AI | reqwest, with clients for OpenAI-compatible servers and Anthropic | Microsoft.Extensions.AI abstractions, so any IChatClient works |
| Cloud client | reqwest with rustls | None beyond .NET itself |
| Hosting | None beyond the engine and stores | The Generic Host abstractions, health checks and the OpenTelemetry API |
Your code and Strata’s code
Section titled “Your code and Strata’s code”You write the parts that are specific to your application. Strata runs them.
| You write | Strata does |
|---|---|
| The state type: a C# record or a Rust struct | Passes it from node to node, and saves it as JSON in checkpoints (State) |
| Nodes, and the edges between them | Checks the structure when you compile, then runs the nodes and follows the edges (Graphs) |
| Tools: your own methods and functions | Generates their JSON schemas at compile time, and dispatches calls to them |
| Where a person must approve | Pauses the thread there, saves it and resumes it later (Human-in-the-loop) |
| Which store and which model client to use | Saves and loads checkpoints, retries failed nodes, and recovers crashed runs (Durable execution) |
| When to start or resume a thread, and by its ID | Keeps the thread’s checkpoints, so any process can pick it up (Threads and checkpoints) |
What the two engines share
Section titled “What the two engines share”The Rust and .NET engines have the same concepts and the same API shape (see the Introduction). They share no code. Each is native to its language. What joins them is the wire format in schema/: JSON Schemas for the records both engines write, and fixtures that both engines’ tests read.
| Record | Schema | What it holds |
|---|---|---|
| Checkpoint | checkpoint.schema.json |
A saved snapshot of a thread. Either engine can resume it, and the CLI reads it. |
| Graph definition | graph-definition.schema.json |
A graph’s structure, exported with ToDefinition() in C# or to_definition() in Rust. Both engines export the same JSON for the same graph, and strata graph validate checks it, for example in CI. |
| History record | history-record.schema.json |
One message of a conversation history. |
| Memory record | memory-record.schema.json |
One long-term memory. |
The stores share their database layouts too: the same tables in SQLite and Postgres, and the same keys in Redis. In .NET the wire-format types and ToDefinition() are in Datalium.Strata.Storage. In Rust the checkpoint and graph definition types are in datalium-strata, and the history record is in datalium-strata-memory.
Resuming a thread in the other language
Section titled “Resuming a thread in the other language”A thread that one engine paused can be resumed by the other, if both use the same database:
| Store | Checkpoints shared | History shared |
|---|---|---|
| In-memory | No. It lives in one process. | No |
| SQLite | Yes | No. The two engines write history to different tables (HistoryRecords and history_records), and save a role differently ("User" and "user"). |
| Postgres | Yes | Yes |
| Redis | Yes | Yes |
The other engine needs a graph with the same node names, and a state type whose JSON has the same field names. Strata saves your state as JSON, and the two serializers name fields differently by default: System.Text.Json writes the C# property name, such as ApprovedBy, and serde the Rust field name, such as approved_by. To share state, pin the C# names with [JsonPropertyName].
Each record carries its own schema_version. A newer Strata reads everything an older one wrote, and refuses data newer than itself. When services in both languages read one database, upgrade every reader before any writer starts writing something new. The rules are in schema/README.md.
Two things aren’t done yet. The formal checkpoint spec isn’t written (see Known limitations). And no test runs both engines against one database: each engine’s tests check its layout against the shared fixtures instead. The demos below make the hand-off by hand.
See it run
Section titled “See it run”Each demo has a C# and a Rust version.
- 09-cross-language-interop: one engine pauses a thread in a SQLite file and exits, and the other engine resumes it in a new process. C# · Rust
- 12-pluggable-storage: one graph runs against the in-memory, SQLite, Postgres and Redis stores without knowing which one it uses. C# · Rust
- 28-hosting: a service wired with
AddStrata()in .NET, and with settings and aHostin Rust. C# · Rust
- Graphs: nodes, edges and what compiling checks.
- Threads and checkpoints: what a store keeps, and when.