Skip to content

feat: migrate opencode storage from JSON files to SQLite#168

Merged
gricha merged 4 commits intomainfrom
feat/opencode-sqlite-migration
Feb 14, 2026
Merged

feat: migrate opencode storage from JSON files to SQLite#168
gricha merged 4 commits intomainfrom
feat/opencode-sqlite-migration

Conversation

@gricha
Copy link
Owner

@gricha gricha commented Feb 14, 2026

Summary

  • OpenCode 1.2 replaced flat JSON file storage (storage/{session,message,part}/*.json) with a single SQLite database (opencode.db). Rewrites the worker's opencode-storage module to query SQLite via bun:sqlite instead of walking JSON files on disk.
  • Removes the opencode file watcher from session-index since we query the DB directly now — no filesystem events to watch.
  • All functions become synchronous (bun:sqlite is sync), removing unnecessary awaits from callers.
  • Net -100 lines; the old code walked 3 directory trees with nested try/catch, the new code runs 2-3 SQL queries.

export const AGENT_SESSION_PATHS = {
claudeCode: '.claude/projects',
opencode: '.local/share/opencode/storage',
opencode: '.local/share/opencode',

This comment was marked as outdated.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [B42-QJU] Missing sessionId validation in CLI delete operation (src/index.ts:1289) (high confidence)

sessionId from CLI is passed directly to deleteOpencodeSession without validation or sanitization. Unlike claude-code sessions (line 1416 in router.ts), no input filtering is applied, allowing arbitrary session IDs to be targeted.

🟠 [X6S-LXT] Path traversal via unchecked homeDir parameter (src/sessions/agents/opencode-storage.ts:27) (high confidence)

homeDir parameter is used in path construction without validation, allowing potential access to arbitrary database files via path traversal (e.g., '../../malicious').

🟠 [9A5-YKA] Unsafe type assertion after JSON parse (src/sessions/agents/opencode-storage.ts:153) (high confidence)

safeParse uses 'as T' without validation. Malformed database content could cause runtime errors when code assumes properties exist.

🚨 [E3B-3X3] SQL Injection vulnerability in DELETE operation (src/sessions/agents/opencode-storage.ts:135) (high confidence)

The DELETE query uses string interpolation without parameterization. While sessionId comes from function params, lack of consistent parameterization creates injection risk.

🟠 [LZH-RE9] Silent error handling masks database failures (src/sessions/agents/opencode-storage.ts:71) (high confidence)

Empty catch blocks in listOpencodeSessions and getOpencodeSessionMessages return empty results without logging, making database errors invisible and hard to debug.

🟠 [ZM9-GJK] Silent error handling masks database failures (src/sessions/agents/opencode-storage.ts:124) (high confidence)

Empty catch blocks in listOpencodeSessions and getOpencodeSessionMessages return empty results without logging, making database errors invisible and hard to debug.

🟠 [3WH-V8X] Database connection leak on error (src/sessions/agents/opencode-storage.ts:32) (high confidence)

If withDb callback throws before queries execute, database connection may not close properly. While finally block exists, exceptions during db.close() could mask original error.

🟠 [NTL-86N] Stale hardcoded OpenCode storage paths in parser.ts (src/shared/constants.ts:456) (high confidence)

parser.ts lines 456 and 519 still reference old path '.local/share/opencode/storage' while constants.ts was updated to '.local/share/opencode'. These appear to be unused dead code functions, but if called will fail to find sessions.

🟠 [X97-FNX] DELETE operation does not cascade to child tables (src/worker/session-index.ts:135) (high confidence)

deleteOpencodeSession only deletes from the session table but leaves orphaned records in message and part tables. Without CASCADE constraints or explicit cleanup, deleted sessions leak data.

🟠 [HV3-3RR] Potential orphaned data from incomplete cascade deletion (src/worker/session-index.ts:135) (medium confidence)

DELETE FROM session WHERE id = ? only deletes the session row but doesn't explicitly delete related messages and parts. If DB lacks CASCADE constraints, orphaned data remains.

Identified by Warden via find-bugs

Comment on lines +134 to +136
withDb(homeDir, false, (db) => {
db.query(`DELETE FROM session WHERE id = ?`).run(sessionId);
});

This comment was marked as outdated.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 [SV3-H7D] Command injection in searchHostSessions query parameter (src/agent/router.ts:1289) (high confidence)

The safeQuery sanitization at line 1289 only escapes quotes and backslashes but misses shell metacharacters like backticks, $(), |, &, ;. An attacker could inject commands via the search query.

🟠 [5ZM-7PC] OpenCode sessions no longer searchable after SQLite migration (src/agent/router.ts:1290) (high confidence)

Removed OpenCode storage path from searchPaths but didn't add SQL-based search. OpenCode is still a valid agentType in return signature but cannot be found by search.

⚠️ [AZW-GRW] OpenCode sessions no longer searchable after SQLite migration (src/agent/router.ts:1335) (high confidence)

The search functionality for OpenCode sessions was removed when migrating from JSON files to SQLite. The searchHostSessions function no longer includes the opencode path, and no SQLite-based search was added to replace it. Users cannot search OpenCode session content.

🟠 [B8L-D9H] Potential orphaned database records on session deletion (src/worker/session-index.ts:135) (medium confidence)

DELETE only removes session record, not related message/part records. May leak data unless schema has CASCADE DELETE.

⚠️ [BBX-95T] Parser still uses old JSON file storage for OpenCode (src/worker/session-index.ts:456) (high confidence)

listOpenCodeSessions/getOpenCodeSessionDetail read from .local/share/opencode/storage/* (old path) instead of SQLite. Called by listAllSessions used in mobile app. Will return empty/stale data.

Identified by Warden via find-bugs

OpenCode 1.2 replaced flat JSON file storage with a SQLite database.
Rewrites the worker's opencode-storage module to query the SQLite DB
directly using bun:sqlite instead of walking JSON files on disk.
OpenCode sessions are now in SQLite, not flat files — ripgrep can't
search them. Remove the dead opencode search paths and result parsing
from both the container and host search functions.
The server integration test was creating JSON files for the old storage
format. Uses bun -e inside the container to create a SQLite DB with
test session data instead.
@gricha gricha force-pushed the feat/opencode-sqlite-migration branch from 4fa2ec9 to 525671b Compare February 14, 2026 17:34
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 [GYB-538] Command Injection in searchHostSessions via insufficient input sanitization (src/agent/router.ts:1309) (high confidence)

User-controlled query string is inserted into shell command with inadequate escaping. Only quotes and backslashes are escaped, but shell metacharacters like $, `, ;, |, & remain unescaped, allowing arbitrary command execution via execSync.

Identified by Warden via security-review

SQLite disables foreign key constraints by default. Without PRAGMA
foreign_keys = ON, deleting a session leaves orphaned message and
part rows.

const partRows = db
.query<{ message_id: string; data: string }, [string]>(
`SELECT message_id, data FROM part WHERE session_id = ? ORDER BY message_id, id`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The SQL query in getOpencodeSessionMessages assumes a session_id column exists in the part table, which likely mismatches the actual OpenCode database schema, causing message loading to fail.
Severity: CRITICAL

Suggested Fix

The query for parts needs to be corrected. Instead of filtering the part table by session_id, first query all messages for the given sessionId from the message table. Then, use the collected message_id values to fetch the corresponding parts from the part table. This may require a JOIN or a second query using WHERE message_id IN (...). Additionally, the test suite should be updated to create and seed the part table to ensure this functionality is properly tested.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/sessions/agents/opencode-storage.ts#L90

Potential issue: The function `getOpencodeSessionMessages` queries a `part` table using
a `session_id` column to retrieve message parts. However, evidence from the previous
file-based storage structure and the lack of a `part` table in the test setup strongly
suggest the actual OpenCode 1.2 database schema does not contain a `session_id` column
in the `part` table, or that the table is structured differently. This mismatch will
cause the SQL query to fail with either a "no such table: part" or "no such column:
session_id" error, preventing any OpenCode session messages from being loaded.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is incorrect

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [9S8-676] Command injection in session search via insufficient input sanitization (src/agent/router.ts:1310) (high confidence)

User input is passed to execSync with only quote/backslash escaping. Shell metacharacters like $() ` ; & | can execute arbitrary commands.

Identified by Warden via security-review

@gricha gricha merged commit e020931 into main Feb 14, 2026
15 checks passed
@gricha gricha deleted the feat/opencode-sqlite-migration branch February 14, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant