Rust SDK overview

A tour of the CocoIndex Rust SDK — crate layout (core, prelude, connectors, ops, resources), feature flags, common types like StableKey, and its async-only API.

Version
v 1.0.14
Last reviewed
Nov 4, 2026
ExperimentalThe Rust SDK is experimental. It is outside CocoIndex's compatibility promise: any release, including a minor one, may change or remove its API. It is not yet published on crates.io.

The CocoIndex Rust SDK is the cocoindex crate under rust/sdk/cocoindex. Its API is async and runs on Tokio, and your processing functions are async fns.

Python SDK

The Rust SDK calls the same core engine as the Python SDK, and the two write one state database format. See the Python SDK overview.

Until the crate is published, depend on it from GitHub, as Installation shows:

Cargo.toml
[dependencies]
cocoindex = { git = "https://github.com/cocoindex-io/cocoindex", features = [
    "postgres",
    "text",
    "fastembed",
    "fs_live",
] }

Crate organization

PathDescription
cocoindexThe core API at the crate root: App, Environment, Ctx, ContextKey, Error, Result, the #[cocoindex::function], #[app], #[lifespan] and #[main] attributes, the mount!, use_mount! and mount_each! macros, context_key!, and the MemoInput and SchemaFields derives. The resource types (FileEntry, DirTarget, walk_dir, Chunk, IdGenerator, LiveMap, StableKey, …) are re-exported here too
cocoindex::preludeuse cocoindex::prelude::*; imports the core API, the resource types, the macros, and serde::{Serialize, Deserialize}
cocoindex::connectors::*One module per connector, each behind a feature flag of the same name: postgres, sqlite, doris, lancedb, qdrant, turbopuffer, valkey, zvec, neo4j, falkordb, surrealdb, bigquery, snowflake, kafka, iggy, amazon_s3, azure_blob, oci_object_storage, and gdrive (feature google_drive)
cocoindex::ops::*Built-in operations: text (feature text), sentence_transformers and image (feature fastembed), api (feature embed_api), code (feature code_match)
cocoindex::resources::*The resource modules behind the root re-exports: file, fs, chunk, id, live_map, rate_limit, schema, view, embedder
cocoindex::inspectReads an app’s stored state: stable paths, their details, and tracked target states

Import connectors and ops by their module; take the rest from the prelude:

use cocoindex::connectors::postgres;
use cocoindex::ops::sentence_transformers::SentenceTransformerEmbedder;
use cocoindex::ops::text::{RecursiveChunkConfig, RecursiveSplitter};
use cocoindex::prelude::*;

Feature flags

Every connector and op is optional, so a pipeline compiles only the clients it uses. The other features are fs_live (live directory watching through walk_dir(..).live()), chrono and rust_decimal (MemoInput for those leaf types; a connector feature enables the ones it needs), and embed_api (remote embeddings and transcription over an OpenAI-compatible HTTP API). Installation lists them all.

Common types

StableKey

StableKey is the enum of values that identify a processing component in the component path, and a target state within its target. Its variants are Null, Bool, Int, Str, Bytes, Uuid, Symbol, Array (a composite key) and Fingerprint.

APIs that take a key accept any IntoStableKey value: &str, String, bool, the integer types, uuid::Uuid, byte strings (Vec<u8>, &[u8], bytes::Bytes), () (for Null), or a StableKey itself. Common examples include strings (like "setup" or "table"), integers, and UUIDs. Symbol provides predefined names that never conflict with strings (which typically come from runtime data).

Each processing component must be mounted at a unique path. See Processing Component for how the component path tree affects target states and ownership.

Async only

The Rust API is async and runs on Tokio. There are no sync variants of the orchestration APIs: ctx.mount, ctx.scope, ctx.mount_each, ctx.map and the mount macros are all async, and a #[cocoindex::function] body is an async fn. The entry points have _blocking forms for code that owns no runtime: App::open_blocking, app.update_blocking, app.drop_state_blocking, EnvironmentBuilder::build_blocking. They build a runtime of their own and panic inside one, so use the async forms inside #[tokio::main] or #[cocoindex::main]:

use cocoindex::prelude::*;

#[tokio::main]
async fn main() -> Result<()> {
    let app = App::open("my_app", ".cocoindex_db").await?;
    let stats = app
        .run(|ctx| async move {
            // Declare the desired target state using ctx.
            Ok(())
        })
        .await?;

    println!("{stats}");
    Ok(())
}
// Sync entry point, outside a runtime
let app = App::builder("my_app").db_path(".cocoindex_db").build_blocking()?;
app.update_blocking(|_ctx| async move { Ok(()) })?;

Rust has no ambient context: every API takes the component’s Ctx explicitly, and a processing function takes &Ctx as its first argument. See Function and Processing Component.