Migrate server + desktop state to a local SQLite DB#1999
Draft
src-opn wants to merge 3 commits into
Draft
Conversation
Introduce @openwork/desktop-db (Drizzle + SQLite, runtime-adaptive between better-sqlite3 on Node/Electron and bun:sqlite under the server) as the single source of truth for OpenWork-owned state on the user's machine. Every table uses TypeID ids via a desktop-local registry (separate from the cloud ee/utils one). Migrated to the DB (with one-time import, .pre-db.bak snapshots so source files are preserved for revert, and a migration_state fingerprint guard so we never re-import on every start): - server.json: server config + workspace registry + authorizedRoots - tokens.json: scoped API tokens (hashed) - audit/*.jsonl: audit log - Electron openwork-workspaces.json: workspace list + selection - Electron openwork-server-tokens.json: per-workspace server tokens - Electron openwork-server-state.json: preferred ports - Renderer "real state" localStorage keys: model prefs, drafts, shell-config, onboarding flags, etc. via boot hydration + write-through mirror (web unchanged) Not yet migrated (deferred): opencode.json/jsonc (MCP/plugins/providers), env.json, .opencode/openwork.json sections, Google Workspace OAuth vault. Skills/commands/ agents stay as files (OpenCode + reload watcher depend on them).
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- env.json -> env_var table. EnvService is now DB-backed (same /env API, key + reserved-prefix policy preserved). The desktop shell injects user env vars from the DB into process.env before starting the in-process server and managed OpenCode (buildChildEnv), so the env.json file is no longer read. Stored plaintext for now (parity with the prior 0600 file). - desktop-bootstrap.json -> preference rows. Electron get/set and the renderer bootstrap read it from the DB; the server now surfaces denBaseUrl/denApiBaseUrl in ServerConfig from the DB. Both files are imported once (gated by migration_state) and preserved as .pre-db.bak snapshots. Bootstrap URLs store "" for unset (preference.value is NOT NULL JSON).
Collaborator
Author
Update: env vars + desktop bootstrap config migrated to the DB (commit 3fb510c)Two more file-based stores moved into the SQLite DB:
Both files are imported once (gated by Tests
Still deferred: |
Drop the .pre-db.bak snapshots. They copied/renamed source files, which broke rollback to a pre-DB app version that still reads the originals in place. New model: - Source files are NEVER modified, copied, renamed, or deleted. - migration_state records source path + a non-cryptographic content hash. - A source is imported AT MOST ONCE: once its row is "imported", it's skipped forever, regardless of later content changes (so a rollback->edit->upgrade cycle won't re-import). Shared once-ever gate in import/gate.ts used by both the server (Phase 1) and desktop importers. migration_state schema: replace backup_path with path + hash; migrations squashed to a single unreleased baseline.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces
@openwork/desktop-db(Drizzle + SQLite) as the single source of truth for OpenWork-owned state on the user's machine, and ports the server + desktop shell to use it. The driver is runtime-adaptive:better-sqlite3on Node/Electron,bun:sqliteunder the bun-run server. Every table uses TypeID ids via a desktop-local registry (separate from the cloudee/utilsone).This is the first phase of consolidating the scattered JSON/JSONL/localStorage state documented in the research deep-dive.
What's migrated to the DB
All with: one-time import,
.pre-db.baksnapshots (source files are preserved for revert — nothing is deleted), and amigration_statefingerprint guard so we never re-import on every start.server.jsonserver_config,workspace,authorized_roottokens.jsontokenaudit/*.jsonlauditopenwork-workspaces.jsonworkspace+preferenceopenwork-server-tokens.jsonworkspace_server_tokenopenwork-server-state.jsonworkspace_portlocalStoragepreference(boot hydrate + write-through mirror; web unchanged)The DB lives next to
server.json(<configDir>/openwork.db) so server + Electron + renderer share one file.Not migrated yet (deferred)
opencode.json/opencode.jsonc(MCP servers, plugins, providers, permissions) — needs theOPENCODE_CONFIG_CONTENTvs generated-file strategy since OpenCode reads it at runtime.env.json(user API keys) — blocked on cross-shell read constraint + at-rest encryption decision..opencode/openwork.jsonsections (workspace metadata, blueprint sessions, desktop cloud sync) — tables exist, not wired.Skills / commands / agents stay as files (OpenCode + the reload watcher depend on them).
Notes / caveats
openwork-workspaces.json(DB-only; Tauri rollback would lose that state — accepted).Tests run
@openwork/desktop-db:bun test→ 10 pass / 0 fail (typeid, client, Phase 1 + desktop import once,.pre-db.bak/idempotency, preference mirror). typecheck + tsup build clean;drizzle-kit generateclean.apps/server:bun test src/→ 160 pass / 6 fail. The 6 failures are pre-existing on the base branch (verified by stashing): 4workspace import previewcount assertions + 2seedOpencodeSessionMessages(bun can't loadbetter-sqlite3for OpenCode's own DB).pnpm typecheckclean.apps/desktop:node --testruntime + remote-workspace → 13 pass / 0 fail;check:electron(50 methods),typecheck:electron,node --checkon all.mjs— clean.apps/app:pnpm typecheckclean;pnpm build(Vite) clean.