Skip to content

fix(session-manager): writeOutboundDirect opens outbound.db read-only — command-gate denials never deliver#2738

Merged
gavrielc merged 1 commit into
mainfrom
fix/write-outbound-direct-rw
Jun 11, 2026
Merged

fix(session-manager): writeOutboundDirect opens outbound.db read-only — command-gate denials never deliver#2738
gavrielc merged 1 commit into
mainfrom
fix/write-outbound-direct-rw

Conversation

@gavrielc

Copy link
Copy Markdown
Collaborator

Type of Change

  • Feature skill - adds a channel or integration (source code changes + SKILL.md)
  • Utility skill - adds a standalone tool (code files in .claude/skills/<name>/, no source changes)
  • Operational/container skill - adds a workflow or agent skill (SKILL.md only, no source changes)
  • Fix - bug fix or security fix to source code
  • Simplification - reduces or simplifies source code
  • Documentation - docs, README, or CONTRIBUTING changes only

Description

What: writeOutboundDirect (src/session-manager.ts) opens the session's outbound DB via openOutboundDb, which sets readonly: true. The INSERT it then runs throws SQLITE_READONLY on every call.

Why it matters: the only live caller is the command-gate denial path in src/router.ts — when a non-admin sends an admin command, the host writes a "Permission denied" response straight into messages_out. With the readonly open, that write always throws: the sender gets silence instead of a denial, and the throw escapes routeInbound and aborts routing for the whole inbound event. Reproduced in production in #2495 / confirmed in #2496 (stack trace: SqliteError: attempt to write a readonly database at writeOutboundDirect).

How: one-line fix — open through the existing openOutboundDbRw wrapper (same path, write access, DELETE journal + busy_timeout). The host-side write to the container-owned outbound.db is safe: both sides use DELETE journal mode, and the host's even seq numbers stay out of the container's odd-seq space. A short comment in the function doc records that rationale.

This builds on #2496 (same one-line fix, sitting since May) by adding a regression test so the bug can't quietly come back. Closes #2495.

Tested:

  • New guard test src/session-manager.test.ts drives the real writeOutboundDirect against a real session folder: asserts denial rows land in messages_out with even host seqs, and that INSERT OR IGNORE retry semantics hold. Reverting the fix (RW → readonly open) turns both cases red with SQLITE_READONLY.
  • Full suite: 351 host tests pass (vitest), pnpm run build, pnpm run typecheck clean, lint at baseline (114 problems / 13 errors, all pre-existing). Container suite untouched by this diff: 100/101 pass; the one failure is the known upload-trace test that makes a live network call and times out, identical on the unmodified base.

🤖 Generated with Claude Code

writeOutboundDirect opened the session's outbound DB through
openOutboundDb, which sets readonly: true. The INSERT it then runs threw
SQLITE_READONLY on every call, so the command-gate denial path
(router.ts) never delivered its 'Permission denied' response — the
sender just got silence, and the throw aborted routing for that inbound
event.

Switch to the openOutboundDbRw wrapper, which opens the same path with
write access (DELETE journal + busy_timeout). The host-side write to the
container-owned outbound.db is safe: both sides use DELETE journal mode,
and the even host seq stays out of the container's odd-seq space.

Adds a guard test that drives writeOutboundDirect against a real session
folder and asserts the denial rows land in messages_out with even seqs;
it goes red if the open call reverts to the readonly form.

Co-Authored-By: Claude Fable 5 <[email protected]>
@gavrielc gavrielc requested a review from gabi-simons as a code owner June 11, 2026 10:56
@github-actions github-actions Bot added follows-guidelines PR was created using the current contributing template PR: Fix Bug fix labels Jun 11, 2026
@gavrielc gavrielc merged commit 7eddc7d into main Jun 11, 2026
2 checks passed
@gavrielc gavrielc deleted the fix/write-outbound-direct-rw branch June 11, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

follows-guidelines PR was created using the current contributing template PR: Fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

writeOutboundDirect opens outbound DB in read-only mode, silently drops command-gate deny responses

1 participant