What Database Does OpenWork Use? SQLite + Drizzle Configuration Guide

OpenWork uses SQLite for runtime state storage and Drizzle ORM for all database access, with MySQL available in the enterprise edition.

OpenWork is an open-source workspace management platform built by Different AI. Understanding its database architecture is essential for contributors, self-hosters, and developers extending the platform. This guide breaks down exactly how OpenWork configures SQLite with Drizzle ORM, where the critical files live, and how the enterprise MySQL layer differs.

Runtime Database: SQLite with Bun and Node Support

OpenWork's runtime state—workspace metadata, key-value stores, and session data—persists to a local SQLite file called runtime.sqlite. The core implementation lives in [dev/apps/server/src/runtime-db.ts](https://github.com/different-ai/openwork/blob/dev/dev/apps/server/src/runtime-db.ts).

This module handles three responsibilities:

  • Path resolution: Determines where runtime.sqlite lives (OpenWork config directory or ~/.config/openwork/runtime.sqlite)
  • Driver selection: Dynamically imports bun:sqlite or node:sqlite based on runtime environment
  • Drizzle wrapping: Returns both raw SQLite handle and type-safe Drizzle ORM instance

Conditional Driver Loading

The runtime database detects the execution environment and loads the appropriate native driver:

// Bun runtime
const { Database } = await import("bun:sqlite");
const { drizzle } = await import("drizzle-orm/bun-sqlite");

// Node runtime (fallback)
const { DatabaseSync } = await import("node:sqlite");
const { drizzle } = await import("drizzle-orm");

Both paths converge on the same Drizzle API surface, ensuring portable code across runtimes.

Database Initialization Pattern

From runtime-db.ts, the initialization flow follows this structure:

const sqlite = new Database(path, { create: true });
const db = drizzle(sqlite);

The returned object exposes:

Property Purpose
sqlite Raw driver handle for prepared statements
db Drizzle ORM instance for type-safe queries
close() Cleanup helper for graceful shutdown

SQLite Schema Definition with Drizzle ORM

OpenWork defines tables using sqliteTable from drizzle-orm/sqlite-core. An example from [dev/apps/server/src/workspace-kv-store.ts](https://github.com/different-ai/openwork/blob/dev/dev/apps/server/src/workspace-kv-store.ts):

import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const kvFactory = (config) => {
  const table = sqliteTable(config.tableName, {
    workspace_id: text("workspace_id").primaryKey(),
    key: text("key"),
    value: text("value"),
  });
  // Prepared statements for performance
  const get = sqlite.prepare(config.selectSql);
  const upsert = sqlite.prepare(config.upsertSql);
  return { table, get, upsert };
};

This pattern combines Drizzle's type-safe schema definitions with raw prepared statements for hot-path operations.

Practical Query Example

import { runtimeDbPath } from "./runtime-db";
import { kvTable } from "./schema";

async function getWorkspaceConfig(workspaceId: string) {
  const { db } = await openRuntimeDb("/my/config");
  
  const result = db
    .select()
    .from(kvTable)
    .where(kvTable.workspace_id.eq(workspaceId))
    .get();
    
  return result?.value;
}

Enterprise Edition: MySQL and PlanetScale via Drizzle

The paid Den-DB enterprise package upgrades to MySQL (or PlanetScale's serverless MySQL) while retaining Drizzle ORM. Entry point: [dev/ee/packages/den-db/src/client.ts](https://github.com/different-ai/openwork/blob/dev/dev/ee/packages/den-db/src/client.ts).

MySQL Connection Setup

import { drizzle } from "drizzle-orm/mysql2";
import { drizzle as drizzlePlanetScale } from "drizzle-orm/planetscale-serverless";

// Standard MySQL
const db = drizzle(mysqlConnection, { schema });

// PlanetScale serverless
const db = drizzlePlanetScale({ url: process.env.DATABASE_URL }, { schema });

Schema and Migrations

Enterprise schemas live in dev/ee/packages/den-db/src/schema/ with files like:

Migration management uses Drizzle Kit, configured in [drizzle.config.ts](https://github.com/different-ai/openwork/blob/dev/dev/ee/packages/den-db/drizzle.config.ts):

import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./src/schema/*",
  out: "./drizzle",
  dialect: "mysql",
});

Complete Configuration Walkthrough

Follow these steps to configure OpenWork's database in your environment:

  1. Locate or create the config directory

    # Default path
    
    mkdir -p ~/.config/openwork
    
    # Or specify custom root
    
    export OPENWORK_ROOT=/var/lib/openwork
  2. Initialize the runtime database

    import { openRuntimeDb } from "@openwork/server/runtime-db";
    
    const { db, sqlite, close } = await openRuntimeDb(process.env.OPENWORK_ROOT);
  3. Execute schema migrations (SQLite tables auto-create on first use)

  4. For enterprise MySQL: Set DATABASE_URL and import from @openwork-ee/den-db

File Reference: Key Database Modules

File Repository Path Purpose
runtime-db.ts dev/apps/server/src/runtime-db.ts SQLite connection factory with runtime detection
runtime-db.test.ts dev/apps/server/src/runtime-db.test.ts Unit tests for path resolution and connectivity
workspace-kv-store.ts dev/apps/server/src/workspace-kv-store.ts SQLite table definitions and KV operations
client.ts dev/ee/packages/den-db/src/client.ts MySQL/PlanetScale Drizzle connector
drizzle.config.ts dev/ee/packages/den-db/drizzle.config.ts Drizzle Kit configuration for migrations
schema/ dev/ee/packages/den-db/src/schema/ Enterprise MySQL table definitions

Summary

  • SQLite powers OpenWork's open-source runtime via runtime.sqlite files
  • Drizzle ORM provides the abstraction layer for both SQLite and MySQL
  • Bun and Node are both supported through conditional driver imports in runtime-db.ts
  • MySQL/PlanetScale replaces SQLite in the enterprise Den-DB package
  • Type-safe schemas are defined with sqliteTable (SQLite) or standard Drizzle columns (MySQL)

Frequently Asked Questions

Which SQLite driver does OpenWork prefer?

OpenWork prefers Bun's native bun:sqlite when running on Bun, falling back to Node's node:sqlite (available in Node 22.5.0+) for standard Node environments. The runtime-db.ts module detects the runtime and imports the appropriate driver dynamically.

Can I use PostgreSQL instead of SQLite or MySQL?

Not without modifications. OpenWork's open-source codebase hardcodes SQLite assumptions in runtime-db.ts, and the enterprise edition hardcodes MySQL in den-db. PostgreSQL support would require implementing a new driver adapter following the same Drizzle ORM patterns.

Where does the SQLite file actually get created?

By default, runtime.sqlite is created in ~/.config/openwork/. If you specify a config directory via the root parameter to openRuntimeDb(), it places the file at {root}/runtime.sqlite. The path resolution logic handles cross-platform differences (macOS, Linux, Windows).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →