Provides a unified API over Turso/libSQL for packages, code artifacts, agent memory, Codex
reactivity, and related tables. Prefer this crate over talking to turso::Connection directly
in application code.
User-global relational data uses [DbConfig::resolve_canonical] / [canonical_store::resolve_canonical_config].
Repo-backed interactive surfaces default to the workspace journey store (.vox/store.db) via
[workspace_journey_store::connect_workspace_journey_optional]; set VOX_WORKSPACE_JOURNEY_STORE=canonical
for legacy user-global / Turso. See [canonical_store] and [workspace_journey_store].
Built-in and app-supplied migrations run through [turso::Connection::execute_batch], which uses
execute and fails on statements that return rows (e.g. bare SELECT, assignment PRAGMA
unless handled with pragma_update). [VoxDb::connect] / [VoxDb::open] apply pragmas via
pragma_update and skip empty migration bodies; see also [VoxDb::apply_migrations].
High-level database facade for the Vox ecosystem (Codex). Owns a single [VoxDb] (one Turso connection). Higher-level helpers (memory, learner, schema sync) delegate to that store; advanced callers use [Self::store] for direct access. Concurrency: one connection per VoxDb handle; Sync/safe across concurrent callers because [GuardedConnection] serializes access to the shared turso::Connection (see its docs for why this is required even though VoxDb/Connection are Clone).
GuardedConnection
Serializes access to a shared turso::Connection to avoid turso::Error::Misuse("concurrent use forbidden"). ## Root cause this fixes turso::Connection::clone() does not create an independent connection: it clones an Arc to the same underlying turso_sdk_kit::rsapi::TursoConnection, which itself guards every step() (the primitive under query/execute/execute_batch/prepare) with an atomic ConcurrentGuard. Two async tasks that call into any clones of the same connection at literally the same instant (e.g. two Tauri GUI commands dispatched close together on a multi-threaded Tokio runtime, or a background poll racing a user action) can have their step() polls genuinely overlap on different OS threads, tripping that guard and returning Err(Misuse("concurrent use forbidden")). Since VoxDb is Clone and is shared as one Arc<VoxDb> across all GUI commands (see vox-gui/src/commands/gui_db_pool.rs), this happened routinely under ordinary concurrent chat usage, and the resulting error surfaced to users as a “Message not saved” toast — the write silently never reached the database. ## Fix Hold a tokio::sync::Mutex<()> around each real network/IO-bearing call (query/execute/execute_batch/pragma_update) before delegating to the wrapped turso::Connection. The lock lives behind an Arc so every clone of a GuardedConnection (and every clone of the owning VoxDb) shares the same lock — matching the fact that they already share the same underlying Turso connection. Implements [std::ops::Deref] to turso::Connection so call sites that need a raw &turso::Connection (e.g. [crate::auto_migrate::AutoMigrator::new], schema migrations run once at connect time) keep compiling unchanged via deref coercion. Method resolution prefers inherent methods on GuardedConnection over ones reached through Deref, so the ~400 existing call sites across this crate (and the many downstream crates that call VoxDb::connection(), e.g. vox-gamify, vox-populi, vox-sql, generated vox-codegen output) that do conn.query(..) / conn.execute(..) / conn.execute_batch(..) get the lock automatically without any call-site changes. ## Known residual gap turso::Connection::last_insert_rowid() is a synchronous, non-blocking read of connection-local state and does not go through ConcurrentGuard at all (confirmed by reading turso-0.6.1’s source), so it is intentionally left un-guarded here — wrapping it would require holding the lock across the execute + last_insert_rowid pair (a call-site change) rather than per-call. Callers that do this pairing (e.g. codex_chat.rs::chat_ensure_workspace_conversation) are not actually safe from this crate’s concurrency bug class: on the multi-threaded Tokio runtime this crate runs under, a different task on a different OS thread can execute its own guarded execute() (acquiring and releasing the lock) between this caller’s execute() and its last_insert_rowid() read, since nothing holds the lock across that gap — no explicit .await yield is required for that interleaving. This does not reproduce the Misuse("concurrent use forbidden") bug this type fixes (the two individual calls are each correctly guarded), but it can silently return the other task’s row id. It is noted here as a narrower, pre-existing (not introduced by this change) correctness edge case for future hardening (e.g. INSERT ... RETURNING).
GuardedRows
Rows fully drained from [GuardedConnection::query] while its mutex guard was held, so nothing about iterating this value can race (or deadlock against) another guarded call on the same [GuardedConnection] — see the guard-lifetime note on query for why a lazy, still-locked cursor isn’t safe here.
Compute a SHA3-512 hash of the given data, returning Base32Hex-encoded string.
in_memory
Connect to an ephemeral in-memory database for testing.
in_memory
Connect to an ephemeral in-memory database for testing.
query
Guarded [turso::Connection::query]. Returns [GuardedRows] rather than a bare turso::Rows. The bare cursor is lazy, so a guard dropped when this function returns (before the caller reads a single row) would let row-streaming race other tasks’ execute/query on the same connection. Holding the guard on the returned value instead (rather than only inside this function) is not an option either: tokio::sync::Mutex isn’t reentrant, and this crate’s callers routinely do let mut rows = conn.query(...).await?; then, later in the same scope, another guarded call on conn (e.g. claim_skill_identity’s SELECT-then-INSERT) — rows would still be lexically alive at that point (Rust drops locals at end of scope, not at last use), so a guard tied to it would self-deadlock. Instead, drain every row into an owned buffer while still holding the guard, then release it before returning — the lock covers the entire real connection access and nothing outlives this function.
execute
Guarded [turso::Connection::execute].
execute_batch
Guarded [turso::Connection::execute_batch].
pragma_update
Guarded [turso::Connection::pragma_update].
next
Mirrors [turso::Rows::next]‘s signature (async fn returning a Result) purely so existing rows.next().await? call sites keep compiling unchanged; the buffer is already fully materialized, so this never actually awaits or errors.
Public product name for the unified database facade (Codex over Arca/Turso). VoxDb remains the stable Rust type name; new documentation should prefer Codex.