Skip to content
This repository was archived by the owner on Nov 7, 2026. It is now read-only.

fix(core): prevent SQLite permit leaks and restrict DB files - #395

Merged
brentyates-swx merged 11 commits into
swxtchfrom
fm/oc-sqlite-permit-387
Nov 4, 2026
Merged

brentyates-swx merged 11 commits into
swxtchfrom
fm/oc-sqlite-permit-387

Conversation

@brentyates-swx

@brentyates-swx brentyates-swx commented Nov 3, 2026 •

Copy link
Copy Markdown

Closes swxtchio/swx-opencode#387

Round 1 corrections

  • Partial port — chore: upgrade effect to 4.1.0 and fix a SQLite lock-up on interrupt anomalyco/opencode#60651 (c6056f39312748c633de00ad2c2576b7e042b97d; downstream 79aafa2f82e7293ba7f2c1222e35b2f58dc5c5ea): the reserve handoff sweep is the cancellation regression for Bun, Core Node, and standalone Node. The queued withTransaction arm is a preservation control: the pinned SqlClient.makeWithTransaction masks acquireConnection and this branch does not change that behavior. The amended donor commit now says so.
  • Partial adapted port — fix(core): restrict database files to their owner anomalyco/opencode#52150 (89d37a0f6de691979f65e52ba35913b5f0b957bf; downstream 5c5df6c8e604452d87c823e4aee5cacb9a8b8f5b; symlink/birth correction 9f7cc80702fee80e82dd811a35f9b8d16e97ae29): canonicalize final-component symlinks for mode preparation, handle dangling targets, create new database files at 0600 before later chmod, and tighten existing database/WAL/SHM files without opening or closing an existing database. Preserve the fork’s Database.layerFromPath, EventWriter registration, auto-vacuum order, path-only behavior, in-memory files, and Windows ACL behavior. The commit and CHANGESET.md both classify this as a partial adapted port.

Focused red/green evidence

Fixture-producing runs used the task GOTMPDIR via TMPDIR.

  • Reserve acquisition handoff: with the original acquirer restored, the bounded later-SQL assertion failed for each adapter; with the fix, each passes (1 pass, 128 expect() calls). Commands from packages/core: bun test test/sqlite-busy.test.ts -t "bun proves reserve handoff and preserves withTransaction scope on interruption", with core-node and standalone-node in place of bun.
  • Waiting cancellation / transaction controls: the separate ... cancels a queued reserve while its permit holder remains active case passes with both original and fixed acquirers, confirms the holder remains active at cancellation, and bounds the waiter and follow-up SQL. The withTransaction arm was also isolated with all acquirers reverted; it stays green as a preservation control under the Effect SqlClient mask.
  • Mode at birth: mutating exclusive creation from 0600 to 0644 while retaining later chmod makes bun test test/database.test.ts -t "creates a fresh SQLite file owner-only before later tightening" fail before the SQLite open. The unmutated test passes.
  • Live aliases and dangling targets: removing final-component resolution makes both live-alias tests observe real WAL/SHM at 0644; the dangling-alias test fails with ENOENT at the symlink instead of opening the target. Restored resolution makes tightens real sidecars reached through a live OPENCODE_DB symlink, tightens real sidecars reached through a symlinked data-directory opencode.db, and opens a dangling OPENCODE_DB symlink with owner-only target files pass.
  • Existing locks and explicit CLI path: restoring open/close of an existing database lets the independent contender acquire BEGIN IMMEDIATE while the same-process holder remains active; the focused lock test fails, and passes with exclusive-create preparation. With preparation removed, the online-prune --database test leaves the selected file at 0644; with the fix, bun test test/cli/db/prune.test.ts -t "online prune apply tightens the explicitly selected --database target" passes and leaves the OPENCODE_DB default unchanged.

Focused preservation controls passed for concurrent initialization, fresh/existing auto-vacuum and migration data, EventWriter registration, transaction commit/rollback/savepoints, held-lock recovery, stale snapshots, retry exhaustion, nonretryable partial writes, permit serialization across retry backoff, database path selection, and the opencode db path no-open smoke.

bun install --frozen-lockfile left bun.lock unchanged. The pre-push bun turbo typecheck gate passed all 30 tasks, and the crew identity gate passed. This repository has no bin/fm-lint.sh. Whole-file changed-test runs, the final suite, and review remain with Firstmate; this PR stays draft.

1. Cherry-pick the two pinned upstream donors with -x, retaining only the assigned behaviors and the fork's Effect beta.83 adapters and database contracts.
2. Make permit acquisition cancellation-safe in the Bun, Core Node, and standalone Node adapters without changing retry or transaction semantics.
3. Prepare POSIX database, WAL, and SHM modes at Database.layerFromPath execution, preserving in-memory operation, existing-file locks, EventWriter registration, auto-vacuum ordering, and db path purity.
4. Add bounded real-client regressions for queued reserve and withTransaction plus producer-driven file-mode and live-lock tests; demonstrate focused red/green results.
5. Record partial-port provenance in CHANGESET.md and hand off the verified branch through the existing draft PR after focused controls, lint, and identity checks.
@brentyates-swx

Copy link
Copy Markdown
Author

Session ID: unknown
Working repo: /home/byates/fm-worktrees/fm-llmrouter/oc-sqlite-permit-387/swx-opencode

kitlangton and others added 6 commits November 3, 2026 23:45
Partial port of anomalyco#60651 (c6056f3) into packages/core/src/database/sqlite.bun.ts, packages/core/src/database/sqlite.node.ts, and packages/effect-sqlite-node/src/index.ts.

Use a Scope-owned child with the fork-pinned Semaphore.withPermits API rather than upgrading Effect or importing Semaphore.takeScoped. Keep the same-connection retry/backoff gate, adapters, and transaction behavior; exclude workerd, generated API changes, and unrelated compatibility edits.

The reserve leg of the adapted sweep in packages/core/test/sqlite-busy.test.ts is the acquisition-interruption red/green proof on Bun, Core Node, and standalone Node. The queued withTransaction leg is a preservation control: the pinned SqlClient.makeWithTransaction masks acquireConnection, so this port does not claim to change its wait-cancellation semantics.

(cherry picked from commit c6056f3)
Partial adapted port of anomalyco#52150 (89d37a0) at the fork's Database.layerFromPath boundary, in packages/core/src/database/file-permissions.ts and packages/core/src/database/database.ts.

Create missing files exclusively with owner-only permissions, tighten existing database/WAL/SHM files before SQLite opens, and never open or close an existing database during preparation. Keep in-memory operation filesystem-free, Windows inherited ACLs, EventWriter registration, fresh-store auto-vacuum ordering, and pure db path selection. Exclude the donor's replaced layer(options) API and its Global path-resolution flow.

Adapt the donor database regression in packages/core/test/database.test.ts to exercise Core opening, SQLite-produced existing sidecars and data, and same-process lock preservation against an independent contender. Cover explicit --database selection through the real online-prune CLI in packages/opencode/test/cli/db/prune.test.ts.

(cherry picked from commit 89d37a0)
Resolve live and dangling final-component database symlinks to the files SQLite uses for database, WAL, and SHM modes. Split exclusive 0600 creation from later chmod so a producer-driven test observes the file's initial mode.

Add Core regressions for OPENCODE_DB and data-directory symlinks, dangling targets, a holder-live cancellable reserve wait, and the transaction-preservation control. Update the PR ledger to distinguish the reserve handoff red/green proof from preservation cases.
@brentyates-swx
brentyates-swx marked this pull request as ready for review November 4, 2026 09:53
@brentyates-swx
brentyates-swx merged commit f36112c into swxtch Nov 4, 2026
2 of 4 checks passed
@brentyates-swx
brentyates-swx deleted the fm/oc-sqlite-permit-387 branch November 4, 2026 09:54
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backport upstream SQLite permit-leak hang fix and owner-only database file modes

3 participants