Skip to content

apomesh-substrate-state

The in-process state implementation plane. This crate holds the non-Postgres adapters behind the substrate’s persistence ports: the in-memory and SQLite StateStore adapters and the in-memory AssetStore. These are the dev, test, and single-binary backends — the same ports the durable Postgres family implements, without a database server. It is a sibling implementation plane: it binds only apomesh-substrate-types (the ports + vocabulary) and the bundled SQLite library, so editing it recompiles this plane and the runtime, never the heavier LLM or tool trees. Reach for it when you run the in-memory or sqlite state-store backend, or when a port grows a method and the in-process adapters must mirror it.

  • Layer: substrate implementation planes — the in-process side of the state-store layer. See The event log for the event-log and checkpoint model these adapters back.
  • Depends on: apomesh-substrate-types (the StateStore / AssetStore ports + the event / snapshot / memory / asset vocabulary), plus rusqlite with the bundled feature (the vendored SQLite C library — no external system dependency).
  • Consumed by: apomesh-substrate — the runtime constructs these adapters and injects them behind the ports, re-exporting them through its own state_store / asset_store facades. The downstream durable adapters (apomesh-state-store-postgres and Redis) reuse this crate’s contract suite transitively through that facade under the test-util feature — they do not depend on it directly.
  • InMemoryStateStore — a HashMap-backed adapter with no IO, for unit tests and dev paths that don’t want the SQLite dependency. Trivially auditable; the full port, exercised by the same contract suite the durable adapters run.
  • SqliteStateStore — a rusqlite adapter in WAL journal mode for single-binary deployments, edge nodes, and cloud-parity runs without a Postgres server. One writer at a time, made explicit through a tokio::sync::Mutex<Connection>.
  • InMemoryAssetStore — a content-addressed (SHA-256), per-tenant-keyed blob adapter that honours the streaming AssetReader port even though it materialises the bytes.
  • The adapter-agnostic contract suitestate_store::contract (behind the test-util feature): the reusable StateStore conformance tests every adapter, in-process or durable, runs against the same behaviour.

It does not own the ports (the StateStore / AssetStore traits live in apomesh-substrate-types), state reconstruction or event folds (the runtime rebuilds sessions — these adapters carry opaque Vec<u8> snapshot bodies they never interpret), or the durable adapters (Postgres in its own crate).

Both in-process StateStore adapters implement the entire port — the tenant-bound event log (append, read, prune, session enumeration; live subscription is the event bus’s concept, layered over the read), CheckpointSource-tagged snapshots, working memory, all three memory tiers (episodic, semantic, procedural), named memory spaces, and the side-effect log. Semantic search is supported, not stubbed: the in-memory adapter blends linear-scan vector cosine with BM25; the SQLite adapter uses FTS5 BM25 with cosine over stored embedding bytes. Both default to the deterministic FakeEmbeddingProvider; a real provider installs via with_embedding_provider. What differs from Postgres is the retrieval mechanism — a brute-force / FTS5 scan versus pgvector’s halfvec + HNSW approximate-nearest-neighbour index — not the capability. Absent here by design (they live in the Postgres crate): the ANN vector index, the Redis HybridStateStore write-through, at-rest sealing, and the sibling registry / catalog / credential store family. The AssetStore in-process plane is in-memory only; the durable PostgresAssetStore lives in the Postgres crate.

Entry pointContract
InMemoryStateStoreThe full in-memory StateStore; new(), with_embedding_provider(provider).
SqliteStateStoreThe WAL SQLite StateStore; open(path), in_memory(), with_embedding_provider(provider).
InMemoryAssetStoreThe content-addressed in-memory AssetStore; new().
state_storeRe-exports the StateStore port + vocabulary from apomesh-substrate-types so adapters and the runtime facade reach crate::state_store::* unchanged.
asset_storeThe AssetStore counterpart re-export.
state_store::contractThe adapter-agnostic conformance suite (feature test-util), reused by downstream adapter crates’ integration tests.

The daemon selects the backend from the [statestore.backend] config section (a closed StateStoreBackend enum). The two in-process backends are in-memory (the default — volatile, dev + tests) and sqlite (durable single-file):

[statestore.backend]
kind = "sqlite"
path = "/var/lib/apomesh/state.db"

The runtime constructs the matching adapter and injects it behind the port:

use std::sync::Arc;
use apomesh_substrate_state::state_store::{SqliteStateStore, StateStore};
// WAL journal mode + schema are applied on open; re-opening is a no-op.
let store: Arc<dyn StateStore> =
Arc::new(SqliteStateStore::open("/var/lib/apomesh/state.db")?);