Build Rust applications around durable business cells.
Give each account, cart, room or workflow its own SQLite state. Write the business logic in Rust; Cellar handles ownership, routing, replication and recovery.
Quickstart · Write code · Architecture · Examples · Documentation
A cell brings a business object's behavior and state together:
- A cart keeps its items and checkout state in one place.
- An account owns its balance, settings and transaction history.
- A room coordinates participants and live connections.
Choose boundaries around data that changes together. Transactions stay within a cell; cells communicate through calls or durable events.
You need Rust 1.94.1 or later, Cargo, native C/C++ build tools, and curl. Run these commands from a checkout of this repository. See build guidance for local target-directory setup.
Start the counter application:
cargo run -p cellar --bin cellar --locked -- \
dev examples/native-counter/project.jsonOnce the server is ready, open another terminal:
# Increment alice's counter.
curl -sS -X POST http://127.0.0.1:9876/counters/alice
# {"count":1}
# Read the same cell.
curl -sS http://127.0.0.1:9876/counters/alice
# {"count":1}
# A different name has separate state.
curl -sS http://127.0.0.1:9876/counters/bob
# {"count":0}These values assume fresh state. Stop with Ctrl-C, restart with the same
command, and read alice again: its count remains. dev watches the project
and keeps local state beside its configuration under .cellar/.
Open the counter source or follow its walkthrough for routing, assets and cleanup.
Inside a cell handler, use its SDK context to access storage. This helper increments a counter in one transaction:
use cellar::sdk::Context;
fn increment(context: &Context) -> anyhow::Result<u64> {
let storage = context.storage()?;
storage.transaction_sync(|tx| {
let count: u64 = match tx.get_json("count")? {
Some(value) => serde_json::from_value(value)?,
None => 0,
};
let next = count
.checked_add(1)
.ok_or_else(|| anyhow::anyhow!("counter overflow"))?;
tx.put_json("count", &serde_json::json!(next))?;
Ok(next)
})
}The storage belongs to the current cell. Cellar checks ownership and durability before releasing a successful handler response. See storage and transactions.
In a gateway handler, resolve a declared namespace and forward the request:
use cellar::sdk::{Context, Invocation, Output, Registry};
let application = Registry::new(|context: Context, invocation| {
Box::pin(async move {
let Invocation::Fetch(request) = invocation else {
anyhow::bail!("expected an HTTP request");
};
let counter = context.namespace("COUNTERS")?.get_by_name("alice");
Ok(Output::Http(counter.fetch(&context, request).await?))
})
});COUNTERS is a namespace binding declared in your
project configuration.
The same name finds the same cell within that namespace, even when its owner
changes. The complete counter also registers
its cell factory; this gateway snippet shows only request forwarding.
cellar-axum lets you use Axum handlers, middleware and optional OpenAPI. Run the same counter in an Axum-owned HTTP server:
# Stop the earlier server before reusing port 9876.
cargo run -p cellar-axum-counter --bin axum-host --locked -- \
127.0.0.1:9876 examples/.cellar/axum-counterPOST to /counters/alice, or open /docs for Swagger UI. The
two-host example includes both hosting modes
and a generated Rust client.
To own listeners, executors and node lifecycle directly, start with the embedded Node example.
| Concept | Meaning |
|---|---|
| Cell | Business behavior with its own durable SQLite state. |
| Namespace | A binding used to find and call cells by name or ID. |
| Node | A process running compiled applications and hosting cells. |
| Fleet | Nodes sharing an object store for ownership and durable data. |
Applications use cellar::sdk. Inside the host, a serial actor owns the pure
decision core; adapters execute effects and return completion events. The
execution domain owns tasks, services and shutdown.
Arrows show component relationships. For the request lifecycle and component boundaries, see architecture and ownership.
Each cell has one authoritative owner. A request can arrive at another node; Cellar routes it to the owner. Before a replacement owner serves the cell, Cellar recovers the required state and log history.
SQLite writes are captured as LTX, the replication format. A single bucket-backed node waits for upload proof. A fleet can prove durability through peer logs before uploading to the bucket.
The output gate checks the required proof before releasing success. If a reply is lost, the caller may still have an ambiguous outcome; use an explicit idempotency and retry contract.
Pick a working example for the part you need:
| Build | Start here |
|---|---|
| Stored data and transactions | SQL, KV, Blob |
| Calls between cells | RPC |
| Background jobs | Alarms, queues, workflows |
| Event delivery | Transactional outbox, durable streams |
| Web applications | Static assets, WebSockets |
| Fleet management | Fleet console, FleetCare lab |
See the full example catalog for more recipes. Event-delivery work and its remaining qualification gates are tracked in the implementation status.
Build a package containing your compiled application:
cargo run -p cellar --bin cellar --locked -- \
build examples/native-counter/project.json --output counter-packageThe CLI can publish a package with deploy and download a verified executable
with install. Run that application executable on your nodes; the generic CLI
does not contain your application's factories.
Follow packaging and deployment for credentials, shared storage, installation and rollout. The distributed fleet example covers local recovery and handoff drills.
Start with cellar to build an application. The other crates separate HTTP
integration, fleet administration and runtime internals:
| Crate | Purpose |
|---|---|
| cellar | Application SDK, production host and CLI |
| cellar-axum | Axum handlers, external ingress and optional OpenAPI |
| cellar-admin | Authenticated fleet management API and dashboard service |
| cellar-logic | Pure runtime decisions and state transitions |
| cellar-native | Rust values, codecs and web primitives |
| cellar-ltx | SQLite write capture, LTX replication and restore |
| Learn about | Guide |
|---|---|
| Modeling cells and choosing boundaries | Cell modeling |
| Application contexts and resource lifetimes | SDK and lifetimes |
| Owning the runtime in your application | Embedding |
| Running and recovering a fleet | Operations, recovery |
| Evidence and remaining release gates | Production readiness |
The documentation library contains the full guides. Open the visual explorer locally for crate maps, hosting and lifetime diagrams, plus interactive request, write, recovery and shutdown flows.
Read AGENTS.md for repository boundaries and Rust conventions.
Use cargo format to format code and cargo check-layout to check module
placement. The SDK CI workflow defines the
full validation commands.
Cellar executes native Rust applications without a JavaScript engine. Current Cellar development uses fresh configuration and state; legacy Celld aliases and persisted-state migration are unsupported.