SSQLite + BunEnterprise guide · v1.0
Architecture Implementation Operations Advanced TypeScript

SQLite + Bun

A complete guide to advanced SQL, runtime behavior, search, vectors, agent memory, GraphRAG, OpenLineage, concurrency, limits, security, containers, cloud providers, and migration strategy.

Primary audienceSolutions architects and senior engineers
Operating modelLocal-first through cloud transition
Evidence36 linked sources
Document version1.0.0
No sections matched.

Try a broader term such as “WAL”, “vector”, “cloud”, “memory”, or “security”.

01 · Orientation

What this guide is for

A decision-oriented reference for architects and engineers evaluating SQLite with Bun for local applications, AI memory, search, vector retrieval, containers, and cloud transition.

Central conclusion

SQLite is strongest when one authoritative owner controls each database file.

Bun makes that model unusually productive for TypeScript. It does not remove SQLite’s single-writer serialization, filesystem assumptions, or absence of native distributed availability.

Best default

bun:sqlite + WAL + short transactions + rebuildable search indexes

Use a repository boundary and a deliberate writer coordinator. Add FTS5 first. Add vectors when semantic recall is measured and justified.

Cloud boundary

Do not horizontally scale a writable SQLite file by mounting it into generic stateless replicas.

Scale by ownership: one stateful owner, tenant/file sharding, immutable replicas, single-primary replication, or a managed SQLite-compatible service.

How to use the document

  1. 1
    Start with the decision framework.

    Determine whether the workload fits SQLite before selecting APIs or extensions.

  2. 2
    Choose a Bun surface.

    bun:sqlite, Bun.SQL, and node:sqlite solve different problems.

  3. 3
    Adopt the operational model.

    Writer ownership, WAL lifecycle, backups, and storage topology are architectural requirements.

  4. 4
    Add derived capabilities.

    FTS, vectors, graph projections, and lineage should be reconstructible from canonical records.

02 · Decision

When SQLite is the right database

SQLite is not “small PostgreSQL.” It is an embedded transactional engine optimized for local ownership, low operational overhead, and high read performance.

Strong fit

  • CLI, desktop, mobile, local agent, or developer tool.
  • Single-node API or MCP server with bounded writes.
  • Per-user, per-tenant, per-workspace, or per-repository databases.
  • Read-heavy workloads with one coordinated write stream.
  • Portable knowledge snapshots and embedded catalogs.
  • Local FTS, vector retrieval, graph projections, and memory.
  • Edge or offline-first systems with explicit synchronization.

Poor fit

  • Many independent hosts must write the same logical database.
  • Active-active multi-region writes are mandatory.
  • Stateless replicas expect one shared writable file.
  • Database-native users, roles, network authentication, and centralized policy are required.
  • Transparent HA, managed PITR, and near-zero failover are non-negotiable.
  • Write throughput must scale by adding replicas.

If any answer is “no,” introduce a service boundary, shard ownership, use a distributed SQLite derivative, or move the authoritative store to a client/server database. SQLite’s own guidance identifies high write concurrency and network-separated storage as key client/server boundaries. S02S05

Decision matrix

WorkloadRecommended storeWhyPrimary control
Local AI agent memorySQLite per agent/workspaceFast local reads, portable state, no service dependencyRevisioned memory and rebuildable indexes
Single-node internal APISQLite on local/block storageOperational simplicity and low latencyOne writer coordinator and tested recovery
Many tenant-isolated workloadsDatabase-per-tenantIndependent writer locks and isolationShard catalog and migration fleet tooling
Global read distributionImmutable snapshots or single-primary replicasLocal reads without multi-writer ambiguityVersion routing and freshness contracts
Shared enterprise platformPostgreSQL/distributed SQL; SQLite at edgeMany writers, HA, RBAC, central operationsRepository abstraction and cache invalidation
03 · Foundations

The engine model you must preserve

SQLite is an in-process C library operating on pages and files. Transactions rely on filesystem locking, journals, and storage durability—not a network database daemon.

TypeScript applicationBun runtime, repository, authorization, telemetry
↓ local function calls
SQLite connectionSQL parser, planner, virtual machine, pager
↓ page I/O and locks
Database file + transaction sidecars.sqlite · -wal · -shm or rollback journal
↓ durability contract
Filesystem and block storageLock correctness, atomic writes, fsync semantics, available space
Concurrent readersMany
Concurrent writers per fileOne
Network protocolNone
Built-in replicationNone

Why the ownership model matters

WAL mode allows readers to retain snapshots while one writer appends changes. It does not create independent row-level writers. All writes to unrelated tables and tenants still serialize through one database file. S04S05

Rollback journal and WAL

ModeReader/writer behaviorAdvantagesConstraints
Rollback journalWriter eventually excludes readers while pages are committedSimple file model; works without WAL shared-memory indexMore direct reader/writer blocking
WALReaders hold snapshots while one writer appendsBetter mixed read/write responsivenessSame-host shared memory, checkpoints, sidecars, WAL growth
04 · Bun runtime

Choosing between bun:sqlite, Bun.SQL, and node:sqlite

Bun exposes three useful interfaces. The right choice depends on performance, portability, governance, and how much SQLite-specific behavior the application needs.

Capabilitybun:sqliteBun.SQLnode:sqlite in Bun
Execution modelSynchronousPromise-shaped; SQLite work remains synchronousSynchronous
Primary strengthFast direct SQLite ergonomicsTagged-template API shared with PostgreSQL/MySQLNode/Bun compatibility and governance APIs
Prepared statementsquery() cache and prepare()Automatic prepared statementsprepare(), tag store
TransactionsHelpers, immediate/exclusive variants, nested savepointsUnified transaction APIManual SQL and API primitives
ExtensionsDirect loadExtension()Not the central APISupported with configuration
Authorizer/defensive modeNot documented as a high-level APINoYes
JS SQL functions/aggregatesNot documentedNoYes
Sessions/changesetsNo direct APINoYes
Best fitTrusted application SQLConventional CRUD with possible engine migrationUntrusted SQL, tooling, portable libraries

Use bun:sqlite

For CLIs, local agents, MCP servers, desktop tools, embedded indexes, and single-node services.

Use Bun.SQL

For tagged templates and repository code that may target PostgreSQL/MySQL. Portability stops at API shape.

Use node:sqlite

For Node compatibility, authorizers, defensive mode, runtime limits, custom SQL functions, changesets, and backup APIs.

Bun documents bun:sqlite as synchronous and exposes statement caching, transactions, bigint handling, serialization, extension loading, and file controls. Its unified SQL client supports SQLite, PostgreSQL, and MySQL through a tagged-template API. S14S15S17

05 · Implementation

A production baseline in TypeScript

Start with explicit connection policy, schema migrations, strict data contracts, and a repository boundary before adding search or AI capabilities.

Project layout

Recommended module boundaries
src/
  db/
    connection.ts
    migrations.ts
    schema/
    repositories/
    queries/
  search/
    lexical.ts
    vector.ts
    hybrid.ts
  memory/
    extraction.ts
    consolidation.ts
  lineage/
    emitter.ts
    outbox.ts
  workers/
    read-worker.ts
    write-worker.ts
  app.ts
data/
  .gitkeep
test/
  fixtures/
  retrieval-evaluation/

Connection initialization

connection.ts
import { Database } from "bun:sqlite";

export interface OpenDatabaseOptions {
  filename: string;
  readonly?: boolean;
}

export function openDatabase(options: OpenDatabaseOptions): Database {
  const db = new Database(options.filename, {
    create: !options.readonly,
    readonly: options.readonly ?? false,
    strict: true,
    safeIntegers: true,
  });

  db.exec(`
    PRAGMA foreign_keys = ON;
    PRAGMA busy_timeout = 5000;
    PRAGMA trusted_schema = OFF;
  `);

  if (options.readonly) {
    db.exec(`PRAGMA query_only = ON;`);
  } else {
    const row = db.query<{ journal_mode: string }, []>(
      `PRAGMA journal_mode = WAL`
    ).get();

    if (row?.journal_mode.toLowerCase() !== "wal") {
      db.close();
      throw new Error(`WAL activation failed: ${row?.journal_mode}`);
    }

    db.exec(`PRAGMA synchronous = NORMAL;`);
  }

  return db;
}

Strict parameter mode catches missing named bindings. Safe integers avoid silent precision loss when SQLite 64-bit integers exceed JavaScript’s safe-number range. S14

Migration runner

migrations.ts
import type { Database } from "bun:sqlite";

interface Migration {
  version: number;
  name: string;
  sql: string;
}

const migrations: readonly Migration[] = [
  {
    version: 1,
    name: "canonical-schema",
    sql: `
      CREATE TABLE document (
        id          TEXT PRIMARY KEY,
        tenant_id   TEXT NOT NULL,
        title       TEXT NOT NULL,
        content     TEXT NOT NULL,
        metadata    TEXT NOT NULL DEFAULT '{}'
                    CHECK (json_valid(metadata)),
        created_at  INTEGER NOT NULL,
        updated_at  INTEGER NOT NULL
      ) STRICT;

      CREATE INDEX document_tenant_updated_idx
      ON document(tenant_id, updated_at DESC);
    `,
  },
];

export function migrate(db: Database): void {
  db.exec(`
    CREATE TABLE IF NOT EXISTS schema_migration (
      version     INTEGER PRIMARY KEY,
      name        TEXT NOT NULL,
      applied_at  INTEGER NOT NULL
    ) STRICT;
  `);

  const applied = new Set(
    db.query<{ version: number }, []>(
      `SELECT version FROM schema_migration`
    ).all().map((row) => row.version),
  );

  const apply = db.transaction((migration: Migration) => {
    db.exec(migration.sql);
    db.query(`
      INSERT INTO schema_migration(version, name, applied_at)
      VALUES ($version, $name, $appliedAt)
    `).run({
      version: migration.version,
      name: migration.name,
      appliedAt: Date.now(),
    });
  });

  for (const migration of migrations) {
    if (!applied.has(migration.version)) {
      apply.immediate(migration);
    }
  }
}
06 · Core usage

Prepared statements, transactions, and write coordination

Bun’s direct SQLite API is strongest when SQL is prepared once, reused with bounded parameters, and executed inside short synchronous transactions.

Statement lifecycle

MethodUseGotcha
db.query(sql)Reusable SQL; cached prepared statement by SQL textUnbounded generated SQL can grow the cache
db.prepare(sql)One-off or deliberately managed statementFinalize when lifecycle is explicit
.get()First rowTypeScript generics do not validate runtime schema
.all()Materialize all rowsLarge result sets allocate memory
.iterate()Process rows without one large arrayStill synchronous; long iterators hold read snapshots
.values()Array-of-arrays resultColumn order becomes part of the contract
.run()Mutation metadataUse RETURNING when rows are needed

Atomic queue claim

Atomic read-modify-write
interface ClaimedTask {
  id: string;
  payload: string;
  attempts: bigint;
  leaseUntil: bigint;
}

const claimStatement = db.query<
  ClaimedTask,
  { workerId: string; now: bigint; leaseUntil: bigint }
>(`
  UPDATE task
  SET
    status = 'running',
    lease_owner = $workerId,
    lease_until = $leaseUntil,
    attempts = attempts + 1,
    updated_at = $now
  WHERE id = (
    SELECT id
    FROM task
    WHERE status = 'pending'
       OR (status = 'running' AND lease_until < $now)
    ORDER BY priority DESC, created_at
    LIMIT 1
  )
  RETURNING
    id,
    payload,
    attempts,
    lease_until AS leaseUntil
`);

const claimTask = db.transaction((workerId: string) => {
  const now = BigInt(Date.now());
  return claimStatement.get({
    workerId,
    now,
    leaseUntil: now + 60_000n,
  });
});

const task = claimTask.immediate(crypto.randomUUID());

Savepoint for optional derived work

Nested rollback boundary
const persistDocument = db.transaction((input: DocumentInput) => {
  const documentId = insertDocument(input);

  db.exec("SAVEPOINT derived_indexes");
  try {
    insertFtsProjection(documentId, input);
    insertVectorProjection(documentId, input.embedding);
    db.exec("RELEASE derived_indexes");
  } catch (error) {
    db.exec("ROLLBACK TO derived_indexes");
    db.exec("RELEASE derived_indexes");
    markIndexingPending(documentId, error);
  }

  return documentId;
});
07 · Advanced SQL

Queries and data structures beyond CRUD

SQLite supports sophisticated relational, analytical, JSON, graph, temporal, and virtual-table patterns. Bun passes these capabilities through the loaded SQLite engine.

Recursive CTEs

Trees, graph traversal, dependency expansion, date series, and hierarchical rollups.

Native SQL

Window functions

Rank, running totals, lead/lag, partitioned analytics, and deduplication.

Native SQL

JSON

Flexible metadata with indexed generated projections and table-valued traversal.

Built in

Virtual tables

FTS5, R*Tree, vectors, CSV, geospatial, and extension-defined data sources.

Extensible

Partial/expression indexes

Smaller indexes aligned to active states, sparse attributes, or computed keys.

Planner-aware

WITHOUT ROWID

Efficient composite-key and mapping tables when the natural primary key is the storage key.

Schema choice

Recursive graph traversal

Bounded cycle-safe traversal
WITH RECURSIVE walk(entity_id, depth, path) AS (
  SELECT
    :start_id,
    0,
    ',' || :start_id || ','

  UNION ALL

  SELECT
    relation.target_id,
    walk.depth + 1,
    walk.path || relation.target_id || ','
  FROM walk
  JOIN relation
    ON relation.source_id = walk.entity_id
  WHERE relation.tenant_id = :tenant_id
    AND walk.depth < :max_depth
    AND instr(
      walk.path,
      ',' || relation.target_id || ','
    ) = 0
)
SELECT
  entity_id,
  MIN(depth) AS distance
FROM walk
GROUP BY entity_id
ORDER BY distance
LIMIT :limit;

Recursive CTEs are appropriate for bounded, shallow graph expansion. Deep graph algorithms and corpus-wide community detection should run in an indexing worker or specialized graph engine. S12

Windowed retrieval analysis

SELECT
  query_id,
  memory_id,
  fused_score,
  row_number() OVER (
    PARTITION BY query_id
    ORDER BY fused_score DESC
  ) AS rank_within_query,
  fused_score - lead(fused_score) OVER (
    PARTITION BY query_id
    ORDER BY fused_score DESC
  ) AS margin_to_next
FROM retrieval_candidate;

JSON with generated projections

CREATE TABLE document (
  id        TEXT PRIMARY KEY,
  tenant_id TEXT NOT NULL,
  metadata  TEXT NOT NULL
            CHECK (json_valid(metadata)),

  source_uri TEXT GENERATED ALWAYS AS (
    json_extract(metadata, '$.source.uri')
  ) VIRTUAL,

  media_type TEXT GENERATED ALWAYS AS (
    json_extract(metadata, '$.mediaType')
  ) STORED
) STRICT;

CREATE INDEX document_source_uri_idx
ON document(tenant_id, source_uri)
WHERE source_uri IS NOT NULL;

Use JSON for optional, low-frequency metadata. Promote security, tenant, status, time, and frequently filtered values to relational columns or generated/indexed projections. S10S31S32

Data structure selection

NeedSQLite representationTrade-off
Canonical entitiesSTRICT tableStrong constraints; conventional B-tree indexes
Many-to-many mappingComposite primary key, often WITHOUT ROWIDEfficient natural-key lookup; different rowid semantics
Optional metadataJSON + generated columnsFlexibility without sacrificing common access paths
Full-text lookupFTS5 virtual tableDerived index must remain synchronized
Semantic lookupVector virtual table or BLOB extensionNative packaging and model-version lifecycle
RelationshipsAdjacency-list edge tableGood for bounded traversal; not graph-native analytics
HierarchyClosure table or materialized pathFast reads versus more expensive moves/writes
Temporal/geospatial intervalsR*TreeSpecialized multidimensional range index
08 · Query engineering

Indexes, plans, and performance validation

SQLite’s planner is capable, but the application must align indexes to equality filters, ranges, ordering, and the actual projection.

Index design sequence

Equality filtersRange predicateOrderingCovering columns
Partial covering index
CREATE INDEX memory_active_lookup_idx
ON memory(
  tenant_id,
  kind,
  updated_at DESC,
  confidence,
  importance
)
WHERE status = 'active';
Plan inspection
EXPLAIN QUERY PLAN
SELECT
  id,
  content,
  confidence,
  importance,
  updated_at
FROM memory
WHERE tenant_id = :tenant_id
  AND status = 'active'
  AND kind = :kind
ORDER BY updated_at DESC
LIMIT 25;

Plan smells

  • SCAN on a table expected to grow.
  • USE TEMP B-TREE FOR ORDER BY on a latency-sensitive query.
  • An index used for filtering but not sorting.
  • Functions applied to indexed columns without a matching expression index.
  • JSON predicates that differ syntactically from the expression index.
  • Automatic indexes appearing repeatedly—often evidence of a missing deliberate index.

Bun statement cache discipline

db.query(sql) caches the compiled statement by SQL text. Reuse stable statements. Use prepare() for unique generated statements, and avoid embedding values in SQL strings. S14

11 · AI knowledge

Agent memory, graphs, and GraphRAG

A reliable memory system separates evidence, derived assertions, revisions, retrieval indexes, and feedback. Embeddings alone are not memory governance.

Memory classes

ClassMeaningLifecycle
EpisodicA specific event or interactionTime-bound; may compact into summaries
SemanticDurable concept or factRevisioned and evidence-backed
ProfileStable preference or operating constraintExplicitly superseded or retracted
ProceduralHow to perform a taskVersioned with tools and environment
WorkingShort-lived active-session stateExpires aggressively
SummaryCompacted historical contextRetains source references
TaskGoal, status, and next actionState-machine lifecycle

Canonical memory schema

CREATE TABLE source_event (
  id           TEXT PRIMARY KEY,
  tenant_id    TEXT NOT NULL,
  source_type  TEXT NOT NULL,
  payload      TEXT NOT NULL CHECK (json_valid(payload)),
  content_hash TEXT NOT NULL,
  observed_at  INTEGER NOT NULL,
  ingested_at  INTEGER NOT NULL,
  UNIQUE (tenant_id, content_hash)
) STRICT;

CREATE TABLE memory (
  id             TEXT PRIMARY KEY,
  tenant_id      TEXT NOT NULL,
  kind           TEXT NOT NULL,
  content        TEXT NOT NULL,
  metadata       TEXT NOT NULL DEFAULT '{}'
                 CHECK (json_valid(metadata)),
  confidence     REAL NOT NULL CHECK (confidence BETWEEN 0 AND 1),
  importance     REAL NOT NULL CHECK (importance BETWEEN 0 AND 1),
  status         TEXT NOT NULL CHECK (
    status IN ('candidate','active','superseded','retracted','expired')
  ),
  observed_at    INTEGER NOT NULL,
  valid_from     INTEGER,
  valid_until    INTEGER,
  expires_at     INTEGER,
  supersedes_id  TEXT REFERENCES memory(id),
  source_event_id TEXT REFERENCES source_event(id),
  created_at     INTEGER NOT NULL,
  updated_at     INTEGER NOT NULL
) STRICT;

CREATE TABLE memory_revision (
  memory_id       TEXT NOT NULL REFERENCES memory(id),
  revision        INTEGER NOT NULL,
  content         TEXT NOT NULL,
  metadata        TEXT NOT NULL CHECK (json_valid(metadata)),
  confidence      REAL NOT NULL,
  source_event_id TEXT REFERENCES source_event(id),
  created_at      INTEGER NOT NULL,
  PRIMARY KEY (memory_id, revision)
) WITHOUT ROWID, STRICT;

Retrieval sequence

Authorization + tenant scopeExact lookupFTSVector candidatesEntity expansionEvidence rerankContext budget

Graph model

CREATE TABLE entity (
  id             TEXT PRIMARY KEY,
  tenant_id      TEXT NOT NULL,
  entity_type    TEXT NOT NULL,
  canonical_name TEXT NOT NULL,
  description    TEXT,
  metadata       TEXT NOT NULL DEFAULT '{}'
                 CHECK (json_valid(metadata)),
  UNIQUE (tenant_id, entity_type, canonical_name)
) STRICT;

CREATE TABLE relation (
  id            TEXT PRIMARY KEY,
  tenant_id     TEXT NOT NULL,
  source_id     TEXT NOT NULL REFERENCES entity(id),
  target_id     TEXT NOT NULL REFERENCES entity(id),
  relation_type TEXT NOT NULL,
  weight        REAL NOT NULL DEFAULT 1,
  confidence    REAL NOT NULL DEFAULT 1,
  valid_from    INTEGER,
  valid_until   INTEGER,
  evidence_id   TEXT REFERENCES source_event(id)
) STRICT;

CREATE INDEX relation_outgoing_idx
ON relation(tenant_id, source_id, relation_type, target_id);

CREATE INDEX relation_incoming_idx
ON relation(tenant_id, target_id, relation_type, source_id);

GraphRAG boundary

GraphRAG adds entity extraction, relationship extraction, entity resolution, community detection, hierarchical summaries, and local/global query routing. SQLite is well suited to store the canonical chunks, graph edges, community assignments, summaries, and retrieval indexes. Corpus-scale clustering should generally run outside online SQL.

Question typeRetrieval mode
Exact identifier or routeB-tree and FTS
“What is X?”Hybrid chunk retrieval
“How is X related to Y?”Entity resolution + bounded graph expansion
“What are the major themes?”Community/global summaries
“What changed over time?”Temporal filters + revisions + graph evidence
12 · Provenance

SQLite with OpenLineage and OpenTelemetry

SQLite can participate in OpenLineage even without a native connector. Instrument the TypeScript job that reads and writes SQLite datasets.

OpenLineage

Explains durable provenance: which job transformed which datasets, under which model and configuration.

  • Index builds
  • Embedding migration
  • Memory consolidation
  • Graph extraction
  • Schema and dataset versions

OpenTelemetry

Explains runtime execution: latency, candidate counts, errors, cache use, and per-request decisions.

  • Retrieval spans
  • Lock wait
  • Query plans
  • Embedding calls
  • Answer trace correlation

OpenLineage models datasets, jobs, runs, and events abstractly. A SQLite table or derived index can be represented as a dataset when the emitting application supplies a stable namespace and name. S20S21

Naming convention

namespace = sqlite://<platform>/<logical-database-identity>
name      = <attached-schema>.<table-or-logical-index>

sqlite://core-platform-ai/knowledge-prod :: main.document
sqlite://core-platform-ai/knowledge-prod :: main.chunk
sqlite://core-platform-ai/knowledge-prod :: main.chunk_vec

Minimal TypeScript emitter

interface OpenLineageEvent {
  eventType: "START" | "COMPLETE" | "FAIL";
  eventTime: string;
  run: { runId: string; facets?: Record<string, unknown> };
  job: { namespace: string; name: string };
  inputs?: Array<{ namespace: string; name: string }>;
  outputs?: Array<{ namespace: string; name: string }>;
  producer: string;
  schemaURL: string;
}

export async function emitLineage(
  baseUrl: string,
  event: OpenLineageEvent,
  signal?: AbortSignal,
): Promise<void> {
  const response = await fetch(
    new URL("/api/v1/lineage", baseUrl),
    {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(event),
      signal,
    },
  );

  if (!response.ok) {
    throw new Error(
      `Lineage emission failed: ${response.status}`
    );
  }
}

Transactional outbox

CREATE TABLE lineage_outbox (
  id              TEXT PRIMARY KEY,
  event_json      TEXT NOT NULL CHECK (json_valid(event_json)),
  status          TEXT NOT NULL DEFAULT 'pending'
                  CHECK (status IN ('pending','sending','sent','failed')),
  attempts        INTEGER NOT NULL DEFAULT 0,
  next_attempt_at INTEGER NOT NULL,
  created_at      INTEGER NOT NULL,
  sent_at         INTEGER
) STRICT;

CREATE INDEX lineage_outbox_pending_idx
ON lineage_outbox(status, next_attempt_at)
WHERE status IN ('pending', 'failed');

Commit the output records and an OpenLineage event into the outbox in one SQLite transaction. Publish outside the transaction, retry transient failures, and mark delivery state. This prevents a metadata-backend outage from rolling back the business transformation.

Vector-index facet

{
  "_producer": "https://example.com/core-platform-ai/sqlite-lineage",
  "_schemaURL": "https://schemas.example.com/openlineage/vector-index/1-0-0.json",
  "embeddingModel": "embedding-v3",
  "embeddingModelRevision": "2026-07-01",
  "dimensions": 768,
  "distanceMetric": "cosine",
  "normalization": "l2",
  "vectorEngine": "sqlite-vec",
  "indexType": "flat-exact",
  "chunkingStrategy": "semantic-section",
  "generationId": "embedding-generation-2026-07"
}
13 · Limits

Hard ceilings and practical limits

SQLite’s compiled limits are generally high. Operational limits—writer contention, recovery time, maintenance space, storage topology, and latency—usually arrive first.

ResourceTypical defaultUpper bound or constraintPractical meaning
String/BLOB or encoded row1,000,000,000 bytesAbout 2.147 GB implementation ceilingUse object storage for large media and archives
Database filePage-size dependent~281 TB at 64 KiB pagesRestore, backup, and maintenance windows dominate first
Columns2,000Compile-time up to 32,767Very wide schemas create planning/code-generation cost
Tables in a join64Hard planner limitGenerated mega-joins need staging or redesign
SQL statement length1,000,000,000 bytesCompile configurableBind data and batch work instead
Bound parameters32,766 in modern SQLiteCompile configurableProvider limits can be dramatically lower
Compound SELECT terms500Compile configurableStage large generated unions
Attached databases10125 compile-time maximumATTACH is not a distributed sharding layer

SQLite documents a maximum file size of approximately 281 TB with the largest page size, but the filesystem, disk capacity, backup process, and operational blast radius are the real constraints. S03

Capacity model

Provisioned capacity must include:

main database
+ WAL or rollback journal
+ temporary sort and index files
+ table-rebuild migration copy
+ backup or snapshot staging
+ vector/FTS rebuild space
+ filesystem safety reserve

Provider limits are not vanilla limits

Cloudflare D1, for example, currently documents 10 GB per paid database, 2 MB maximum row/string/BLOB, 100 columns, 100 bound parameters, 100 KB SQL statements, and one-at-a-time query processing per individual database. These are provider contracts—not SQLite core limits. S22

14 · Concurrency

Writer serialization, locks, snapshots, and WAL checkpoints

Most production SQLite incidents are caused by transaction duration, filesystem assumptions, or unmonitored WAL lifecycle—not incorrect SQL syntax.

The concurrency equation

Contention risk ≈ write arrival rate × write transaction duration × competing writers

Rules

  • One application-level writer queue. Multiple pooled connections do not create write throughput.
  • No remote I/O in transactions. Compute before BEGIN IMMEDIATE.
  • Bound every batch. Limit rows, bytes, and elapsed transaction time.
  • Treat SQLITE_BUSY as expected backpressure. Retry only idempotent work with jitter and a deadline.
  • Close readers promptly. Long snapshots delay checkpoints.
  • Monitor WAL size and checkpoint progress. Do not rely on defaults without visibility.

Checkpoint starvation

A checkpoint cannot advance past pages needed by active readers. Continuous overlapping readers can prevent a full reset, grow the WAL, consume disk, and increase recovery work. S04

-- Inspect checkpoint progress without aggressively blocking.
PRAGMA wal_checkpoint(PASSIVE);

-- Controlled maintenance only.
PRAGMA wal_checkpoint(RESTART);
PRAGMA wal_checkpoint(TRUNCATE);

-- Automatic threshold in WAL pages.
PRAGMA wal_autocheckpoint = 1000;

Synchronous durability

SettingUseTrade-off
FULLCommit durability through power/OS failure is a strong requirementMore synchronization cost
NORMAL in WALCommon throughput/durability balanceRecent committed transactions may be lost in severe power/OS failure while database consistency remains protected
OFFDisposable data onlyDurability and corruption exposure; generally inappropriate for authoritative state

Sidecar files

application.sqlite
application.sqlite-wal
application.sqlite-shm
application.sqlite-journal   # rollback mode / recovery scenarios
15 · Runtime scaling

Workers, event-loop isolation, and sharding

Bun reduces driver overhead. It does not make synchronous SQLite queries non-blocking or turn one file into a multi-writer system.

Read-worker pattern

Bun HTTP/MCP processRequest routing, auth, rate limits
Read workersOne read-only connection each
SQLite WALMany readers, one writer
Writer workerSerialized bounded transactions
// workers/read-worker.ts
import { Database } from "bun:sqlite";

const db = new Database("./data/application.sqlite", {
  readonly: true,
  strict: true,
  safeIntegers: true,
});

db.exec(`
  PRAGMA query_only = ON;
  PRAGMA busy_timeout = 5000;
`);

self.onmessage = (event: MessageEvent<SearchRequest>) => {
  try {
    const rows = executeBoundedSearch(db, event.data);
    self.postMessage({
      requestId: event.data.requestId,
      ok: true,
      rows,
    });
  } catch (error) {
    self.postMessage({
      requestId: event.data.requestId,
      ok: false,
      error: error instanceof Error ? error.message : String(error),
    });
  }
};

Workers improve event-loop responsiveness and parallel read CPU use. They do not create parallel writers. Each worker should own its connection and close readers promptly.

Horizontal write scaling

tenant-a.sqliteWriter A
tenant-b.sqliteWriter B
tenant-c.sqliteWriter C

Separate files have independent writer locks. Sharding is credible when transactions remain inside a tenant, workspace, repository, device, region, or agent boundary. It introduces fleet migrations, backup inventory, routing, and cross-shard query complexity.

16 · Containers

Container and Kubernetes deployment patterns

Container orchestration assumes replaceable replicas. Writable SQLite assumes an authoritative storage owner. The deployment must make that ownership explicit.

Anti-patterns

Database in image layer

State disappears when the container is replaced.

One copy per replica

Replicas silently diverge.

Shared NFS/RWX WAL

Cross-host lock and shared-memory assumptions fail.

Horizontal autoscaling of writers

Creates contention or divergent files—not write scale.

Concurrent rolling-update writers

Migration and ownership races.

Raw file copy during writes

Main database may not include committed WAL state.

Pattern A: one stateful pod

Illustrative StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: knowledge-api
spec:
  serviceName: knowledge-api
  replicas: 1
  selector:
    matchLabels:
      app: knowledge-api
  template:
    metadata:
      labels:
        app: knowledge-api
    spec:
      containers:
        - name: app
          image: example/knowledge-api:1.0.0
          volumeMounts:
            - name: database
              mountPath: /var/lib/knowledge
        - name: litestream
          image: litestream/litestream:latest
          args: ["replicate"]
          volumeMounts:
            - name: database
              mountPath: /var/lib/knowledge
  volumeClaimTemplates:
    - metadata:
        name: database
      spec:
        accessModes: ["ReadWriteOncePod"]
        resources:
          requests:
            storage: 20Gi

ReadWriteOncePod constrains a supported CSI volume to one pod and is preferable when single-pod ownership is critical. It does not provide failover, backups, or replication by itself. S29

Pattern B: database-owner service

Stateless replicasScale horizontally
SQLite owner APIAuth + one writer
Local/block volumeSQLite + WAL

This is the safest generic cloud pattern. Upstream clients use a network API while SQLite remains local to the owner. The service boundary also makes a later PostgreSQL migration easier.

Pattern C: immutable snapshot replicas

  1. 1
    Build

    Create a new database generation offline.

  2. 2
    Validate

    Integrity check, schema version, logical hash, retrieval evaluation.

  3. 3
    Publish

    Distribute the immutable file.

  4. 4
    Switch

    Open read-only and atomically route to the new generation.

Litestream and LiteFS

ToolPurposeDoes not provide
LitestreamContinuous SQLite-aware replication and restore, often to object storageParallel writers or a general multi-primary database
LiteFSSingle-primary replicated filesystem with local read replicasActive-active writes; synchronous replication by default

LiteFS routes writes to one primary and asynchronously distributes changes. Litestream’s container guidance recommends local storage and warns about incompatible cross-OS or network lock models. S26S27

17 · Cloud

Cloud transition and provider models

“Cloud SQLite” can mean hosted SQL, synchronized embedded replicas, actor-owned databases, single-primary replication, or a Raft service. Compatibility and consistency vary materially.

OptionCategoryWrite modelGood fitKey constraint
VM + block diskVanilla SQLiteOne active owner per fileMaximum compatibility and controlYou operate HA, patching, backup, and failover
Cloudflare D1Managed serverless SQLite-based SQLEach database processes queries one at a timeCloudflare-native per-tenant/entity databases10 GB paid DB cap and lower SQL limits
Durable Objects SQLiteActor/object-owned databaseOne object coordinates its statePer-user, room, workflow, or agent stateCross-object analytics/transactions require design
TursoSQLite-compatible embedded/sync/cloud productsProduct-specific: remote primary, local sync, or concurrent modesLocal-first and many-database architecturesValidate current SDK and semantic model
SQLite CloudManaged distributed service built on SQLiteProvider-managed clusterManaged operations and SQLite-oriented APIsPlan limits and vendor compatibility surface
LiteFSReplicated SQLite filesystemOne lease-elected primaryLocal reads across replicasWrite routing, lag, and failover operations
rqliteRaft database service built on SQLiteLeader/consensus serializes writesModerate control-plane workloads requiring HAHTTP service semantics and lower write throughput
PostgreSQLClient/server relational DBMany concurrent writersShared enterprise authoritative storeMore infrastructure and migration work

Current provider observations

  • D1: current paid limits include 10 GB per database, 100 columns, 2 MB row/string/BLOB, 100 parameters, and 100 KB SQL. Cloudflare explicitly positions horizontal scale around many smaller databases. S22
  • Durable Objects: each SQLite-backed object has an ownership boundary well aligned to per-tenant or per-agent state, with current per-object storage limits documented by Cloudflare. S23
  • Turso: embedded replicas keep local reads and delegate writes to a cloud primary; newer synchronization products introduce explicit local-write and sync semantics. Verify the current product and SDK rather than assuming legacy libSQL behavior. S24
  • SQLite Cloud: offers managed tiers with published storage and connection limits. Validate extension support and distributed transaction semantics. S25
  • rqlite: provides Raft-backed availability, but consensus serializes writes and adds network/log overhead relative to standalone SQLite. S28

Serverless rule

Ephemeral function/container filesystems are suitable for request-local computation, downloaded read-only snapshots, or caches. They are not a shared authoritative SQLite store because each instance owns a separate disposable file.

18 · Security

Threat model and controls

SQLite security is primarily application and filesystem security. Vanilla SQLite does not provide database users, network authentication, or GRANT/REVOKE.

SQL injection

Bind values. Do not interpolate query text. Constrain identifiers through allowlists or tagged-template helpers.

Malicious database file

Open read-only in an isolated worker, disable trusted schema, enable defensive controls, reduce limits, and inspect integrity.

Native extension

Treat as arbitrary executable code. Pin, checksum, sign, scan, and load only from an allowlisted path.

File theft

Use encrypted disks, SQLCipher/SEE where appropriate, restricted service users, protected backups, and managed keys.

Cross-tenant access

Apply tenant authorization before search. Duplicate scope filters in canonical joins and vector partitions.

Resource exhaustion

Bound SQL length, expression depth, output rows, recursion, vector candidates, query time, and memory.

Security-sensitive connection with node:sqlite

import { DatabaseSync, constants } from "node:sqlite";

const db = new DatabaseSync("./data/import.sqlite", {
  readOnly: true,
});

db.enableDefensive(true);
db.exec(`
  PRAGMA trusted_schema = OFF;
  PRAGMA query_only = ON;
  PRAGMA cell_size_check = ON;
  PRAGMA mmap_size = 0;
`);

const allowedTables = new Set([
  "document",
  "document_chunk",
  "document_chunk_fts",
]);

db.setAuthorizer((action, arg1) => {
  if (
    action === constants.SQLITE_ATTACH ||
    action === constants.SQLITE_DETACH ||
    action === constants.SQLITE_PRAGMA ||
    action === constants.SQLITE_INSERT ||
    action === constants.SQLITE_UPDATE ||
    action === constants.SQLITE_DELETE
  ) {
    return constants.SQLITE_DENY;
  }

  if (
    action === constants.SQLITE_READ &&
    arg1 !== null &&
    !allowedTables.has(arg1)
  ) {
    return constants.SQLITE_DENY;
  }

  return constants.SQLITE_OK;
});

SQLite’s security guidance recommends trusted-schema controls, defensive mode, authorizers, and reduced runtime limits for hostile SQL or files. Bun’s node:sqlite compatibility is the stronger high-level surface for these controls. S07S17

Extension supply chain

  • No runtime downloads.
  • Pin extension and SQLite versions together.
  • Build/test per OS and CPU architecture.
  • Verify checksums and signatures before startup.
  • Include native artifacts in the SBOM.
  • Disable or avoid extension loading after initialization.
  • Use a separate process for untrusted extensions.

Logging gotcha

A statement’s expanded SQL may contain bound values. Avoid logging expanded SQL where secrets, personal data, tokens, or document content can appear. Log statement identifiers, plan hashes, normalized query shapes, and redacted parameters instead.

19 · Portability

Runtime, dialect, data, and operational portability

Portability is multi-dimensional. A common API can reduce rewrites while still leaving SQL syntax, extension behavior, data representation, and availability semantics coupled to SQLite.

LayerPortable?Control
Repository contractHighDomain operations rather than generic query strings
Bun.SQL call-siteModerateShared tagged templates and result API
SQL dialectLow for advanced featuresAdapter-specific queries and tests
FTS/vector extensionsLowSearch interfaces and reconstructible indexes
Data typesModerateExplicit timestamp, decimal, JSON, and bigint conventions
Transactions/concurrencyLowDesign for the target engine’s semantics
Backup/HALowProvider-specific operational adapter and runbooks
Portable domain boundary
export interface KnowledgeRepository {
  transact<T>(operation: () => T): T;

  writeMemory(input: NewMemory): void;

  lexicalSearch(
    input: LexicalSearchRequest,
  ): readonly RetrievalCandidate[];

  vectorSearch(
    input: VectorSearchRequest,
  ): readonly RetrievalCandidate[];

  traverseEntities(
    input: GraphSearchRequest,
  ): readonly GraphCandidate[];

  health(): RepositoryHealth;
  close(): void;
}

Adapter set

BunSqliteKnowledgeRepository
NodeSqliteKnowledgeRepository
TursoKnowledgeRepository
PostgresKnowledgeRepository
InMemoryKnowledgeRepository  # tests only

Use Bun.SQL for conventional CRUD when migration is likely, but keep FTS, vector, PRAGMA, JSON, and graph queries inside engine-specific modules. A unified API does not make the dialect or operational model universal. S15

20 · Extensions

What plugins can add—and what they cost

SQLite’s virtual-table and extension mechanisms can add new indexes, functions, tokenizers, data sources, encryption, and geospatial behavior inside Bun’s process.

CapabilityExtension routeOperational cost
Vector retrievalsqlite-vec, Vec1, SQLite-VectorNative binaries, index lifecycle, model versioning
GeospatialR*Tree, SpatiaLiteComplex native packaging and spatial semantics
EncryptionSQLCipher, SQLite SEECustom SQLite build, key management, backup compatibility
Regular expressionsSqlean/PCRE/custom functionCPU bounds and extension trust
Fuzzy/IP/UUID/statisticsSqlean modules or custom UDFsMaintenance and version pinning
Change trackingSQLite session extension / node:sqliteConflict/application semantics
CSV/filesVirtual tablesFilesystem authorization and untrusted input

Loading an extension in Bun

import { Database } from "bun:sqlite";

if (process.platform === "darwin") {
  Database.setCustomSQLite(
    process.env.SQLITE_LIBRARY_PATH ??
      "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib"
  );
}

const db = new Database("./data/application.sqlite");
db.loadExtension("./native/approved-extension");

Bun documents that Apple’s default macOS SQLite build does not support loadable extensions. A custom SQLite dynamic library must be selected before opening any connection. S14

JavaScript functions before native code

import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync("./data/application.sqlite");

db.function(
  "normalize_identifier",
  { deterministic: true, directOnly: true },
  (value: unknown): string | null => {
    if (typeof value !== "string") return null;
    return value
      .normalize("NFKC")
      .trim()
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, "-")
      .replace(/^-|-$/g, "");
  },
);

Use JavaScript-defined functions for lightweight deterministic logic. Heavy per-row callbacks block the SQLite statement and the JavaScript thread; implement CPU-intensive virtual tables or tokenizers in native code or a separate worker.

21 · Operations

Backups, recovery, maintenance, and observability

A production SQLite system needs a tested recovery objective, WAL monitoring, integrity checks, planner maintenance, and explicit storage capacity controls.

Safe backup mechanisms

MethodUseNotes
Online backup APIConsistent live copySource remains usable; available through node:sqlite
VACUUM INTOCompact consistent snapshotRequires destination space and substantial I/O
LitestreamContinuous replication and point-in-time recovery workflowDoes not create multi-writer HA
Block/filesystem snapshotInfrastructure backupMust capture main and transactional sidecars consistently
-- Fast routine validation.
PRAGMA quick_check;
PRAGMA foreign_key_check;

-- More expensive full validation.
PRAGMA integrity_check;

-- Planner statistics maintenance.
PRAGMA optimize;

-- Controlled compact snapshot.
VACUUM INTO 'knowledge-snapshot.sqlite';

Operational metrics

SQLITE_BUSYCount, wait duration, retries exhausted
Write queueDepth, oldest age, batch duration
WALBytes/pages, checkpoint progress, oldest reader
QueriesP50/P95/P99, rows examined, rows returned
StorageDB size, free space, freelist, temporary peak
RecoveryBackup lag, restore duration, integrity result
IndexesFTS/vector generation, rebuild duration, drift
RuntimeSQLite version, compile options, extension versions

Startup health probe

SELECT sqlite_version();
PRAGMA compile_options;
PRAGMA journal_mode;
PRAGMA synchronous;
PRAGMA foreign_keys;
PRAGMA trusted_schema;
PRAGMA busy_timeout;
PRAGMA page_size;
PRAGMA page_count;
PRAGMA freelist_count;
PRAGMA database_list;
22 · Command reference

Advanced SQL, PRAGMA, and CLI commands

The command layer matters: SQL and PRAGMA statements run through Bun; dot-commands run only in the sqlite3 command-line shell.

Transactions

BEGIN IMMEDIATESAVEPOINTROLLBACK TORELEASECOMMIT

Mutation

UPSERTRETURNINGON CONFLICT DO NOTHINGUPDATE … RETURNING

Query

WITH RECURSIVEWINDOWjson_tree()EXPLAIN QUERY PLAN

Maintenance

PRAGMA optimizewal_checkpointVACUUM INTOREINDEX

Integrity

quick_checkintegrity_checkforeign_key_checktable_xinfo

CLI only

.expert.eqp full.scanstats.backup.recover.sha3sum

Recommended connection PRAGMAs

PRAGMA foreign_keys = ON;
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA busy_timeout = 5000;
PRAGMA trusted_schema = OFF;

Plan and integrity workflow

EXPLAIN QUERY PLAN
SELECT ...;

PRAGMA table_xinfo('memory');
PRAGMA index_list('memory');
PRAGMA index_xinfo('memory_active_lookup_idx');
PRAGMA foreign_key_check;
PRAGMA quick_check;
PRAGMA optimize;

CLI diagnostics

.timer on
.stats on
.eqp full
.scanstats on
.expert --sample 100

Backup and recovery CLI

sqlite3 knowledge.sqlite ".backup main knowledge-backup.sqlite"

sqlite3 corrupt.sqlite ".recover" > recovered.sql
sqlite3 recovered.sqlite < recovered.sql

The SQLite shell includes diagnostics, backup, recovery, import/export, hashing, and experimental index recommendations. These dot-commands are not SQL and cannot be sent through db.exec(). S36

23 · Gaps

Known Bun and SQLite gotchas

These are the issues most likely to produce subtle production failures or misleading portability assumptions.

GotchaFailureControl
SQLite calls are synchronousHTTP/MCP event-loop stallsRead workers, bounded queries, separate maintenance process
Bun.SQL returns PromisesTeam assumes SQLite is non-blockingDocument synchronous execution and test tail latency
Default unsafe integer conversionSilent precision losssafeIntegers: true and bigint boundary normalization
Non-strict parameter bindingMissing parameter may become NULLstrict: true
Statement cache by SQL textDynamic SQL grows cacheStable templates; prepare() for one-offs
Long iterate()Old snapshot and WAL growthBound iteration and release statements promptly
.as(Class) skips constructorsUninitialized invariants/private fieldsUse for presentation behavior only
macOS system SQLiteVersion/feature drift and no extension loadingInspect runtime; configure custom SQLite before connections
Embedded DB in compiled executableWrites disappear on exitUse as seed/snapshot; copy to persistent data location
Relative database pathDifferent DB based on working directoryResolve an explicit absolute application-data path
:memory: per connectionWorkers see independent databasesUse a file or explicit serialize/deserialize transfer
Native extension mismatchLoad failure, crash, or duplicate SQLite runtimePin ABI, OS, architecture, SQLite, and extension together
ORM abstractionFalse portability for FTS/vector/PRAGMAEngine-specific query modules and repository contracts

Bun supports SQLite in standalone executables, but imported file databases resolve relative to the process working directory by default, while an embedded database is an in-memory writable asset whose changes do not persist after process exit. S16

24 · Evolution

Scaling and migration triggers

Move components only when a measured requirement crosses SQLite’s ownership or operational boundary. SQLite can remain a local cache or agent store after the central authority moves.

Phased capability plan

  1. 1
    Relational foundation

    STRICT tables, migrations, constraints, indexes, provenance, WAL, backups.

  2. 2
    Lexical retrieval

    FTS5, exact lookup, evaluation set, query telemetry.

  3. 3
    Semantic retrieval

    Versioned embeddings, vector adapter, hybrid fusion, rebuild tooling.

  4. 4
    Durable memory

    Evidence, revisions, conflicts, validity, expiration, usefulness feedback.

  5. 5
    Graph retrieval

    Entities, relationships, bounded traversal, source evidence.

  6. 6
    Cloud scale

    Shard ownership, single-primary replication, managed provider, or authoritative server DB.

Migration triggers

Multiple active writer hosts

The defining SQLite boundary.

Write lock wait dominates P95

After transaction and batching fixes.

Failover exceeds SLO

Restore or volume attachment is too slow.

Central RBAC/PITR required

Database-service governance becomes mandatory.

One tenant monopolizes writer

Shard or migrate central authority.

Index rebuild exceeds window

Split search/index infrastructure.

Deep graph/ANN dominates

Scale specialized retrieval independently.

Active-active regions

Choose distributed storage with explicit semantics.

Target hybrid architecture

Authoritative shared records  → PostgreSQL / distributed SQL
Vector retrieval              → specialized vector service if required
Graph analytics               → graph engine or offline job
Agent-local memory            → SQLite
Edge/read cache               → SQLite snapshot or embedded replica
Portable export               → SQLite
25 · Blueprints

Reference deployment profiles

Use these profiles as starting architectures. Each preserves a clear owner, a recovery model, and a future migration boundary.

Profile A Local AI agent or developer tool

Architecture

  • One database per workspace or repository.
  • bun:sqlite in-process.
  • FTS5 + exact vector search.
  • Revisioned memory and provenance.
  • Periodic VACUUM INTO or file backup while quiescent.

Controls

  • Absolute application-data path.
  • Safe integers and strict bindings.
  • Derived indexes rebuildable.
  • Schema version on startup.
  • Export/delete tooling.
Profile B Single-node Bun cloud service

Architecture

  • One Bun owner service.
  • Local or block-backed persistent volume.
  • Read workers and one writer coordinator.
  • Litestream or online-backup workflow.
  • Stateless frontends may scale separately.

Controls

  • WAL and lock metrics.
  • ReadWriteOncePod if Kubernetes.
  • Restore drill and explicit RTO/RPO.
  • No shared NFS/RWX live database.
  • Maintenance process for index rebuilds.
Profile C Per-tenant cloud databases

Architecture

  • Shard catalog maps tenant to database owner.
  • Independent files or provider databases.
  • Tenant-scoped search and memory.
  • Asynchronous central analytics export.

Controls

  • Fleet migration orchestration.
  • Per-shard size and write quotas.
  • Backup inventory and tenant relocation.
  • No cross-tenant transactions.
Profile D Enterprise shared platform

Architecture

  • PostgreSQL or distributed SQL is authoritative.
  • SQLite remains local cache, agent memory, snapshot, or offline store.
  • OpenLineage tracks derived indexes and migrations.
  • Repository adapters separate stores.

Controls

  • Cache invalidation and generation IDs.
  • Central RBAC and audit.
  • Independent retrieval SLOs.
  • Explicit consistency contract.
26 · Validation

Red-team and production-readiness checklist

The implementation is credible only when concurrency, recovery, security, portability, and retrieval quality have been tested under realistic failure and scale conditions.

Correctness

Concurrency

Recovery

Security

Scale

AI retrieval

27 · Evidence

Sources, provenance, and methodology

Primary documentation was favored for runtime behavior, engine semantics, provider limits, and standards. The guide distinguishes fact, architectural analysis, and recommendation.

Method

Synthesized the complete conversation research, reconciled duplicated findings, separated engine/runtime/provider layers, and revalidated time-sensitive claims on July 20, 2026.

Evidence hierarchy

Official SQLite and Bun documentation first; official provider and standards documentation second; extension project documentation for extension-specific behavior.

Uncertainty

Cloud prices, quotas, product maturity, bundled SQLite versions, and extension releases can change. Version-sensitive claims are dated and linked.

Interpretation

Recommendations are architectural analysis. They are not claims that a provider or engine prohibits every alternative implementation.

S01

SQLite overview

SQLite · Core definition and project documentation.

https://sqlite.org/
S02

Appropriate uses

SQLite · Official guidance on embedded use, concurrency, and client/server boundaries.

https://sqlite.org/whentouse.html
S03

Implementation limits

SQLite · Hard and configurable limits for rows, values, pages, columns, joins, and SQL.

https://sqlite.org/limits.html
S04

Write-ahead logging

SQLite · WAL behavior, checkpointing, same-host requirement, sidecar files, and caveats.

https://sqlite.org/wal.html
S05

Isolation

SQLite · Serializable isolation, readers, snapshots, and the single-writer model.

https://sqlite.org/isolation.html
S07

Security guidance

SQLite · Defensive mode, trusted schema, authorizers, limits, and untrusted input.

https://sqlite.org/security.html
S08

PRAGMA reference

SQLite · Connection configuration, inspection, maintenance, and WAL controls.

https://sqlite.org/pragma.html
S09

FTS5

SQLite · Full-text indexing, MATCH syntax, ranking, tokenizers, snippets, and maintenance.

https://sqlite.org/fts5.html
S10

JSON functions

SQLite · JSON and JSONB functions, operators, and table-valued traversal.

https://sqlite.org/json1.html
S11

STRICT tables

SQLite · Stricter type enforcement and integrity behavior.

https://sqlite.org/stricttables.html
S12

WITH and recursive CTEs

SQLite · Recursive traversal, materialization hints, and query patterns.

https://sqlite.org/lang_with.html
S13

Window functions

SQLite · Ranking, running aggregates, lead/lag, and partitioned analytics.

https://sqlite.org/windowfunctions.html
S14

Bun SQLite API

Bun · bun:sqlite database, statements, transactions, serialization, extension loading, and file controls.

https://bun.com/docs/runtime/sqlite
S15

Bun SQL API

Bun · Unified tagged-template interface for SQLite, PostgreSQL, and MySQL.

https://bun.com/docs/runtime/sql
S16

Bun compiled executables

Bun · SQLite behavior in compiled executables and embedded database assets.

https://bun.com/docs/bundler/executables
S17

Node SQLite API

Node.js · DatabaseSync, functions, aggregates, authorizers, limits, sessions, and backup APIs.

https://nodejs.org/api/sqlite.html
S18

Vec1 vector extension

SQLite · Official ANN extension, IVFADC/OPQ, distance metrics, and maturity caveats.

https://sqlite.org/vec1
S19

sqlite-vec

sqlite-vec · Portable SQLite vector extension, vec0 tables, KNN syntax, metadata, and JavaScript integration.

https://alexgarcia.xyz/sqlite-vec/
S20

OpenLineage object model

OpenLineage · Jobs, runs, datasets, events, and extensible facets.

https://openlineage.io/docs/next/spec/object-model/
S21

OpenLineage facets

OpenLineage · Standard and custom facets, schema URLs, and metadata extension.

https://openlineage.io/docs/spec/facets/
S22

Cloudflare D1 limits

Cloudflare · Database size, query, row, parameter, throughput, and sharding constraints.

https://developers.cloudflare.com/d1/platform/limits/
S23

Durable Objects limits

Cloudflare · SQLite-backed object storage limits and ownership model.

https://developers.cloudflare.com/durable-objects/platform/limits/
S24

Turso embedded replicas

Turso · Local reads, remote-primary writes, synchronization, and product evolution.

https://docs.turso.tech/features/embedded-replicas/introduction
S25

SQLite Cloud plans

SQLite AI · Published managed-service storage and connection limits.

https://www.sqlite.ai/pricing
S26

LiteFS architecture

Fly.io · Single-primary replication, leases, replicas, lag, and failure behavior.

https://fly.io/docs/litefs/how-it-works/
S27

Litestream containers

Litestream · SQLite-aware replication in containers and storage compatibility guidance.

https://litestream.io/guides/docker/
S28

rqlite features

rqlite · Raft-backed distributed service built on SQLite and its operational trade-offs.

https://rqlite.io/docs/features/
S29

ReadWriteOncePod

Kubernetes · Single-pod volume ownership for stateful workloads.

https://kubernetes.io/docs/tasks/administer-cluster/change-pv-access-mode-readwriteoncepod/
S30

Drizzle with Bun

Drizzle · Typed query-building and migration support for bun:sqlite.

https://orm.drizzle.team/docs/connect-bun-sqlite
S31

Generated columns

SQLite · Virtual and stored generated columns and indexing.

https://sqlite.org/gencol.html
S32

Partial indexes

SQLite · Predicate-limited indexes and write/storage trade-offs.

https://sqlite.org/partialindex.html
S33

WITHOUT ROWID

SQLite · Alternative table representation for natural and composite primary keys.

https://sqlite.org/withoutrowid.html
S34

R*Tree

SQLite · Multidimensional range indexes for geospatial and temporal intervals.

https://sqlite.org/rtree.html
S35

Backup API

SQLite · Consistent online backups while a source remains in use.

https://sqlite.org/backup.html
S36

SQLite CLI

SQLite · Dot-commands, diagnostics, backup, recovery, expert mode, and output formats.

https://sqlite.org/cli.html