Author: ObjectStack Core Team
Created: 2026-02-15
Status: Proposal
Target Version: v3.1 (Q2 2026)
- 1. Executive Summary
- 2. Background & Motivation
- 3. Architecture Impact Analysis
- 4. Turso/libSQL Capabilities Mapping
- 5. Connection Modes
- 6. Embedded Replica & Sync Protocol
- 7. Multi-Tenancy with Database-per-Tenant
- 8. Integration with Existing ObjectStack Services
- 9. Package Structure
- 10. Configuration Schema
- 11. Migration & Deployment Strategy
- 12. Implementation Phases
- 13. Risks & Mitigations
- 14. Decision Log
This document evaluates the architectural impact of developing @objectstack/driver-turso, a data
driver backed by Turso/libSQL — a fork of SQLite designed for edge-first, globally distributed
deployments. The driver brings three transformative capabilities to ObjectStack:
- Edge Deployment — Run ObjectStack data layer at the edge with microsecond read latency
- Embedded Replicas — Local SQLite files that sync with a remote primary (offline-first)
- Database-per-Tenant — Native multi-tenancy via lightweight per-tenant databases
Unlike PostgreSQL or MongoDB drivers (which require persistent server-side infrastructure), the Turso driver is uniquely positioned for serverless, edge, and local-first use cases — making ObjectStack viable for Cloudflare Workers, Vercel Edge Functions, mobile apps, and offline-capable desktop applications.
| Factor | PostgreSQL | MongoDB | Turso/libSQL |
|---|---|---|---|
| Deployment | Server-only | Server-only | Server, Edge, Embedded, Serverless |
| Latency (reads) | 1-10ms (network) | 1-10ms (network) | <1ms (embedded replica) |
| Offline Support | ❌ | ❌ | ✅ (embedded replicas) |
| Multi-Tenancy | Schema/Row isolation | Database per tenant | Native DB-per-tenant (10k+ DBs) |
| Cold Start | Connection pool init | Connection pool init | Near-zero (local file) |
| Edge Runtime | ❌ | ❌ | ✅ (WASM, Cloudflare Workers) |
| Cost Model | Per-instance | Per-instance | Per-query (serverless-friendly) |
| SQLite Compatibility | ❌ | ❌ | ✅ Full SQLite SQL |
- ObjectStack's "Post-SaaS Operating System" vision requires database virtualization across deployment targets (cloud, edge, device). Turso is the first driver that can run in ALL targets.
- Local-first architecture is a growing trend. Embedded replicas enable ObjectStack apps to work offline and sync when connectivity returns.
- Serverless cost optimization — Turso's pay-per-query model eliminates idle connection costs that plague PostgreSQL in serverless environments.
| Component | Impact | Description |
|---|---|---|
@objectstack/spec |
🟢 Minimal | Add TursoConfigSchema + TursoDriverSpec (already done) |
@objectstack/core |
🟢 None | Kernel is driver-agnostic; no changes needed |
@objectstack/objectql |
🟢 None | ObjectQL dispatches via IDataDriver; no changes needed |
@objectstack/runtime |
🟢 None | DriverPlugin wraps any IDataDriver; works as-is |
@objectstack/rest |
🟢 None | REST API is driver-agnostic |
@objectstack/metadata |
🟢 None | Metadata service is storage-agnostic |
@objectstack/cli |
🟡 Minor | Add driver-turso to create-objectstack templates |
| Framework Adapters | 🟢 None | All adapters (Next.js, NestJS, Hono, etc.) are driver-agnostic |
Key Insight: The microkernel architecture means adding a new driver has zero impact on
the server-side stack. The IDataDriver contract completely decouples the data layer.
import { defineStack } from '@objectstack/spec';
import { createTursoDriver } from '@objectstack/driver-turso';
export default defineStack({
datasources: [{
name: 'default',
driver: 'turso',
config: {
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
},
}],
objects: [/* ... */],
});| Component | Impact | Description |
|---|---|---|
@objectstack/client |
🟢 None | Client SDK communicates via REST/GraphQL; driver-agnostic |
@objectstack/client-react |
🟢 None | React hooks use client SDK; no changes |
@objectstack/plugin-msw |
🟢 None | MSW mocks REST endpoints; driver-irrelevant |
New Capability Unlocked: With embedded replicas, a future @objectstack/client-local package
could provide direct libSQL access in the browser (via WASM), enabling:
- Offline-first React/Vue/Svelte apps with local ObjectQL queries
- Optimistic UI updates with background sync
- Zero-latency reads from local embedded replica
This does NOT require changes to existing client packages — it would be a new, optional package.
| Component | Impact | Description |
|---|---|---|
@objectstack/cloud (spec) |
🟡 Minor | Add Turso as a supported datasource in marketplace metadata |
| Deployment Targets | 🟢 Expansion | Enables Cloudflare Workers, Deno Deploy, Vercel Edge |
| Studio IDE | 🟢 None | Object Designer is driver-agnostic |
┌──────────────────────────────────────────────────────────────┐
│ DEPLOYMENT TOPOLOGY A │
│ "Traditional Server + Turso" │
│ │
│ ┌─────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ Browser │ ──REST──▶│ Node.js/Hono │ ────▶│ Turso Cloud │ │
│ │ (SPA) │ │ ObjectStack │ │ (Primary) │ │
│ └─────────┘ └──────────────┘ └─────────────┘ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ DEPLOYMENT TOPOLOGY B │
│ "Edge + Embedded Replica" │
│ │
│ ┌─────────┐ ┌──────────────────┐ ┌─────────────┐ │
│ │ Browser │───▶│ Cloudflare Worker │───▶│ Turso Cloud │ │
│ │ │ │ ObjectStack + │ │ (Primary) │ │
│ │ │ │ Embedded Replica │ │ │ │
│ └─────────┘ │ (local reads) │ │ (writes) │ │
│ └──────────────────┘ └─────────────┘ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ DEPLOYMENT TOPOLOGY C │
│ "Local-First / Offline" │
│ │
│ ┌──────────────────────┐ ┌─────────────┐ │
│ │ Desktop / Mobile │ sync │ Turso Cloud │ │
│ │ ┌─────────────────┐ │ ◀──────▶ │ (Primary) │ │
│ │ │ ObjectStack │ │ └─────────────┘ │
│ │ │ + libSQL local │ │ │
│ │ │ (full offline) │ │ │
│ │ └─────────────────┘ │ │
│ └──────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ DEPLOYMENT TOPOLOGY D │
│ "Multi-Tenant Database-per-Tenant" │
│ │
│ ┌─────────┐ ┌──────────────┐ ┌─────────────────────┐ │
│ │ Tenant A│───▶│ │───▶│ Turso DB: tenant_a │ │
│ └─────────┘ │ ObjectStack │ ├─────────────────────┤ │
│ ┌─────────┐ │ Gateway │───▶│ Turso DB: tenant_b │ │
│ │ Tenant B│───▶│ │ ├─────────────────────┤ │
│ └─────────┘ │ │───▶│ Turso DB: tenant_c │ │
│ ┌─────────┐ └──────────────┘ └─────────────────────┘ │
│ │ Tenant C│───▶ │ │
│ └─────────┘ │ │
│ ┌──────▼──────┐ │
│ │ Tenant DB │ │
│ │ Router │ │
│ └─────────────┘ │
└──────────────────────────────────────────────────────────────┘
| IDataDriver Method | Turso/libSQL Support | Implementation Notes |
|---|---|---|
connect() |
✅ | createClient() from @libsql/client |
disconnect() |
✅ | client.close() |
checkHealth() |
✅ | SELECT 1 probe |
getPoolStats() |
🟡 | Concurrency tracking (no traditional pool) |
execute() |
✅ | client.execute(sql, args) |
find() |
✅ | SQL SELECT with QueryAST→SQL compiler |
findStream() |
🟡 | Cursor-based pagination (no native streaming) |
findOne() |
✅ | SELECT ... LIMIT 1 |
create() |
✅ | INSERT INTO ... RETURNING * |
update() |
✅ | UPDATE ... WHERE id = ? RETURNING * |
upsert() |
✅ | INSERT ... ON CONFLICT DO UPDATE |
delete() |
✅ | DELETE FROM ... WHERE id = ? |
count() |
✅ | SELECT COUNT(*) FROM ... |
bulkCreate() |
✅ | client.batch() with INSERT statements |
bulkUpdate() |
✅ | client.batch() with UPDATE statements |
bulkDelete() |
✅ | client.batch() with DELETE statements |
updateMany() |
✅ | UPDATE ... WHERE <conditions> |
deleteMany() |
✅ | DELETE FROM ... WHERE <conditions> |
beginTransaction() |
✅ | client.transaction() (interactive) |
commit() |
✅ | tx.commit() |
rollback() |
✅ | tx.rollback() |
syncSchema() |
✅ | CREATE TABLE IF NOT EXISTS + ALTER TABLE |
dropTable() |
✅ | DROP TABLE IF EXISTS |
explain() |
✅ | EXPLAIN QUERY PLAN |
| ISchemaDriver Method | Turso/libSQL Support | Implementation Notes |
|---|---|---|
createCollection() |
✅ | CREATE TABLE IF NOT EXISTS |
dropCollection() |
✅ | DROP TABLE IF EXISTS |
addColumn() |
✅ | ALTER TABLE ... ADD COLUMN |
modifyColumn() |
🟡 | SQLite limitation: requires table rebuild |
dropColumn() |
✅ | ALTER TABLE ... DROP COLUMN (SQLite 3.35+) |
createIndex() |
✅ | CREATE INDEX IF NOT EXISTS |
dropIndex() |
✅ | DROP INDEX IF EXISTS |
executeRaw() |
✅ | Direct SQL execution |
| Capability | Memory | PostgreSQL | MongoDB | Turso |
|---|---|---|---|---|
| CRUD | ✅ | ✅ | ✅ | ✅ |
| Bulk Ops | ✅ | ✅ | ✅ | ✅ (batch API) |
| Transactions | ✅ | ✅ | ✅ | ✅ |
| Savepoints | ❌ | ✅ | ❌ | ✅ |
| Query Filters | ✅ | ✅ | ✅ | ✅ |
| Aggregations | ✅ | ✅ | ✅ | ✅ |
| Sorting | ✅ | ✅ | ✅ | ✅ |
| Pagination | ✅ | ✅ | ✅ | ✅ |
| Window Functions | ❌ | ✅ | ❌ | ✅ |
| Subqueries | ❌ | ✅ | ❌ | ✅ |
| CTE (WITH) | ❌ | ✅ | ❌ | ✅ |
| JOINs | ❌ | ✅ | ❌ | ✅ |
| Full-Text Search | ❌ | ✅ | ✅ | ✅ (FTS5) |
| JSON Query | ❌ | ✅ (JSONB) | ✅ | ✅ (JSON1) |
| Vector Search | ❌ | ✅ (pgvector) | ❌ | ✅ (libSQL vectors) |
| Streaming | ✅ | ✅ | ✅ | 🟡 (cursor-based) |
| Schema Sync | ❌ | ✅ | ❌ | ✅ |
| Migrations | ❌ | ✅ | ❌ | ✅ |
| Indexes | ❌ | ✅ | ✅ | ✅ |
| Connection Pooling | N/A | ✅ | ✅ | 🟡 (concurrency limit) |
| Prepared Statements | ❌ | ✅ | ❌ | ✅ |
| Edge Runtime | ✅ | ❌ | ❌ | ✅ |
| Offline Support | ✅ | ❌ | ❌ | ✅ |
| DB-per-Tenant | ❌ | ❌ | ✅ | ✅ (native) |
The Turso driver supports three connection modes, selectable by configuration:
// Connect to Turso cloud or self-hosted libSQL server
const driver = createTursoDriver({
url: 'libsql://my-db-orgname.turso.io',
authToken: process.env.TURSO_AUTH_TOKEN,
});Best for: Standard server deployments, serverless functions.
// Local SQLite file — no network required
const driver = createTursoDriver({
url: 'file:./data/local.db',
});Best for: Desktop apps, CI/CD testing, development environments.
// Local file syncing with remote primary
const driver = createTursoDriver({
url: 'file:./data/replica.db',
syncUrl: 'libsql://my-db-orgname.turso.io',
authToken: process.env.TURSO_AUTH_TOKEN,
sync: {
intervalSeconds: 60,
onConnect: true,
},
});Best for: Edge workers, offline-first apps, low-latency read scenarios.
┌──────────────────┐
│ Turso Primary │
│ (Cloud/Server) │
└────────┬─────────┘
│
┌────────▼─────────┐
│ Sync Layer │
│ (libSQL proto) │
└────────┬─────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───────▼──────┐ ┌────▼─────┐ ┌──────▼──────┐
│ Edge Node 1 │ │ Edge 2 │ │ Edge 3 │
│ ┌────────┐ │ │ ┌──────┐ │ │ ┌────────┐ │
│ │Replica │ │ │ │Replica│ │ │ │Replica │ │
│ │ (.db) │ │ │ │(.db) │ │ │ │ (.db) │ │
│ └────────┘ │ │ └──────┘ │ │ └────────┘ │
└──────────────┘ └──────────┘ └─────────────┘
| Operation | Behavior |
|---|---|
| Reads | Always from local replica (microsecond latency) |
| Writes | Forwarded to primary; acknowledged after primary confirms |
| Read-Your-Writes | Guaranteed for the writer; other replicas see on next sync |
| Sync Trigger | Periodic (configurable) + on-connect + manual driver.sync() |
The driver exposes a sync() method for manual synchronization:
// Trigger manual sync (useful after write operations)
await driver.sync();This integrates with ObjectStack's hook system:
// After data mutation, sync the embedded replica
kernel.hook('data:record:afterCreate', async () => {
await driver.sync();
});Turso natively supports creating thousands of lightweight databases, making it ideal for ObjectStack's multi-tenancy model.
// Multi-tenant configuration
const tenantRouter = createTursoMultiTenantDriver({
// Base URL template — {tenant} is replaced with tenant ID
urlTemplate: 'libsql://{tenant}-orgname.turso.io',
authToken: process.env.TURSO_GROUP_AUTH_TOKEN,
// Tenant lifecycle
onTenantCreate: async (tenantId) => {
// Turso API: create new database in group
await tursoApi.createDatabase(tenantId);
},
onTenantDelete: async (tenantId) => {
await tursoApi.deleteDatabase(tenantId);
},
});| Strategy | PostgreSQL | MongoDB | Turso |
|---|---|---|---|
| Row-Level | ✅ RLS policies | ✅ Query filters | ✅ But not recommended |
| Schema-per-Tenant | ✅ pg schemas | N/A | N/A |
| DB-per-Tenant | 🟡 Heavy (full DB) | ✅ Lightweight | ✅ Native (10k+ DBs) |
| Isolation Level | Medium-High | High | Complete |
| Cost per Tenant | High (connections) | Medium | Low (per-query) |
The driver maps to ObjectStack's tenantId in DriverOptions:
// ObjectStack automatically passes tenantId from security context
const results = await engine.find('accounts', query, {
tenantId: 'tenant_abc', // → Routes to libsql://tenant_abc-org.turso.io
});| Service | Compatibility | Notes |
|---|---|---|
ICacheService |
✅ | Independent service; no driver dependency |
IQueueService |
✅ | Independent service |
IJobService |
✅ | Independent service |
IStorageService |
✅ | Independent service |
IAuthService |
✅ | better-auth supports SQLite/Turso |
IMetadataService |
✅ | Metadata stored in ObjectQL (driver-agnostic) |
ISearchService |
✅ | FTS5 built into libSQL; native full-text search |
IRealtimeService |
✅ | WebSocket layer is driver-independent |
IAIService |
✅ | Vector search supported natively in libSQL |
ObjectStack's plugin-auth uses better-auth which already has official Turso adapter support.
This means authentication tables (users, sessions, accounts) can live in the same Turso database
as application data — eliminating the need for a separate auth database in edge deployments.
libSQL supports native vector search, enabling ObjectStack's IAIService and RAG Pipeline
to store embeddings directly in the same database:
-- Create vector column
ALTER TABLE documents ADD COLUMN embedding F32_BLOB(1536);
-- Similarity search
SELECT * FROM vector_top_k('documents_idx', vector('[0.1, 0.2, ...]'), 10);packages/plugins/driver-turso/
├── package.json
├── tsconfig.json
├── tsup.config.ts
├── vitest.config.ts
├── src/
│ ├── index.ts # Public exports
│ ├── turso-driver.ts # IDataDriver implementation
│ ├── turso-schema-driver.ts # ISchemaDriver implementation
│ ├── turso-driver-plugin.ts # ObjectStack plugin wrapper
│ ├── query-compiler.ts # QueryAST → SQL compiler
│ ├── type-mapper.ts # ObjectStack field types → SQLite types
│ ├── result-mapper.ts # SQLite rows → ObjectStack records
│ ├── multi-tenant.ts # Database-per-tenant router (optional)
│ └── __tests__/
│ ├── turso-driver.test.ts
│ ├── turso-schema-driver.test.ts
│ ├── query-compiler.test.ts
│ ├── type-mapper.test.ts
│ └── multi-tenant.test.ts
{
"name": "@objectstack/driver-turso",
"version": "3.1.0",
"dependencies": {
"@libsql/client": "^0.17.0",
"@objectstack/core": "workspace:*",
"@objectstack/spec": "workspace:*"
},
"devDependencies": {
"vitest": "^4.0.0",
"tsup": "^8.0.0"
}
}The TursoConfigSchema is defined in packages/spec/src/data/driver/turso.zod.ts and supports:
| Property | Type | Default | Description |
|---|---|---|---|
url |
string |
(required) | Database URL (libsql://, https://, file:, :memory:) |
authToken |
string? |
— | JWT auth token for remote databases |
encryptionKey |
string? |
— | AES-256 encryption key for local files |
concurrency |
number |
20 |
Maximum concurrent requests |
syncUrl |
string? |
— | Remote sync URL for embedded replica mode |
localPath |
string? |
— | Local file path for embedded replica |
sync.intervalSeconds |
number |
60 |
Periodic sync interval (0 = manual only) |
sync.onConnect |
boolean |
true |
Sync immediately on connect |
timeout |
number? |
— | Operation timeout in milliseconds |
wasm |
boolean? |
— | Use WASM build for edge/browser environments |
# Scaffold with Turso driver
npx create-objectstack my-app --driver turso
# Set environment variables
export TURSO_DATABASE_URL="libsql://my-db-orgname.turso.io"
export TURSO_AUTH_TOKEN="eyJhbGciOi..."
# Start development
pnpm dev// Before (development with memory driver)
import { createMemoryDriver } from '@objectstack/driver-memory';
// After (production with Turso)
import { createTursoDriver } from '@objectstack/driver-turso';
export default defineStack({
datasources: [{
name: 'default',
driver: 'turso',
config: {
url: process.env.NODE_ENV === 'production'
? process.env.TURSO_DATABASE_URL
: 'file:./dev.db',
authToken: process.env.TURSO_AUTH_TOKEN,
},
}],
});The driver implements ISchemaDriver for automatic DDL operations:
# Generate migration from object definitions
objectstack migrate generate
# Apply migrations to Turso database
objectstack migrate apply --driver turso| Task | Priority | Effort |
|---|---|---|
TursoDriver implementing IDataDriver |
P0 | 2 weeks |
| QueryAST → SQL compiler (SQLite dialect) | P0 | 1 week |
| Type mapper (ObjectStack fields → SQLite types) | P0 | 3 days |
| Transaction support (interactive + batch) | P0 | 3 days |
TursoSchemaDriver implementing ISchemaDriver |
P1 | 1 week |
TursoDriverPlugin (ObjectStack plugin wrapper) |
P0 | 2 days |
| Test suite (unit + integration) | P0 | 1 week |
| Documentation and examples | P1 | 3 days |
Total Estimated Effort: ~5 weeks
| Task | Priority | Effort |
|---|---|---|
| Embedded replica mode with sync | P1 | 1 week |
| WASM build support for Cloudflare/Deno | P1 | 1 week |
| Offline write queue and sync reconciliation | P2 | 2 weeks |
| Edge deployment guides (Cloudflare, Vercel, Deno) | P2 | 3 days |
| Task | Priority | Effort |
|---|---|---|
| Database-per-tenant router | P2 | 1 week |
| Turso Platform API integration (create/delete DB) | P2 | 1 week |
| Tenant migration tools | P3 | 1 week |
| Task | Priority | Effort |
|---|---|---|
Vector search integration with IAIService |
P2 | 1 week |
FTS5 integration with ISearchService |
P2 | 1 week |
better-auth Turso adapter for IAuthService |
P2 | 3 days |
| Performance benchmarks vs. other drivers | P3 | 1 week |
| Risk | Severity | Likelihood | Mitigation |
|---|---|---|---|
SQLite ALTER TABLE limitations (no MODIFY COLUMN) |
Medium | High | Table rebuild strategy in ISchemaDriver |
| Embedded replica sync conflicts | Low | Medium | Read-your-writes consistency; last-write-wins for conflicts |
@libsql/client breaking changes |
Low | Low | Pin version; monitor changelog |
| WASM performance in edge runtimes | Medium | Medium | Benchmark and optimize; fallback to remote mode |
| libSQL/Turso service availability | Low | Low | Embedded replica provides offline fallback |
| Concurrent write limitations | Medium | Low | libSQL fork supports concurrent writes via MVCC |
| Decision | Rationale | Alternatives Considered |
|---|---|---|
Use @libsql/client as underlying client |
Official TypeScript SDK with best feature coverage | better-sqlite3 (no remote), sql.js (WASM only) |
| SQLite dialect in SQL compiler | Turso/libSQL is SQLite-compatible | N/A |
Implement both IDataDriver and ISchemaDriver |
Full driver capability for production use | IDataDriver only (no migrations) |
| Support three connection modes | Maximum flexibility across deployment targets | Remote-only (loses edge/offline value) |
| Database-per-tenant as recommended multi-tenancy | Turso's native strength; complete isolation | Row-level (weaker isolation) |
| WASM support as Phase B | Not needed for initial server-side use | Day 1 (higher initial effort) |
| Document | Description |
|---|---|
ROADMAP.md |
Project roadmap with Turso driver timeline |
ARCHITECTURE.md |
Microkernel design and package structure |
packages/spec/src/data/driver/turso.zod.ts |
Turso configuration schema (Zod) |
packages/spec/src/contracts/data-driver.ts |
IDataDriver interface contract |
packages/spec/src/contracts/schema-driver.ts |
ISchemaDriver interface contract |