Repository navigation
Add sandbox-safe SQLite backup and warn against host-side database access - #172
Merged
akemmanuel merged 1 commit intoOct 23, 2026
Merged
Conversation
Under gVisor, SQLite's POSIX locks and -shm index are not shared with
processes on the host machine. A host-side connection (even a read-only
sqlite3 SELECT) believes it is the last connection, checkpoints and
deletes the live -wal on close, and the Host keeps writing to the
unlinked file. All later writes are lost at the next restart.
The docs recommended exactly that for online backups ("use SQLite's
backup API from an operator-controlled process"). Add
scripts/backup-databases.ts, run via docker exec inside the sandbox
like reset-password.ts, and correct the backup guidance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
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.
Problem
On a gVisor-hosted Remote Host, account and Session-access writes from about 16 hours silently disappeared after a routine container restart. Chats stayed in
opengui-harness-v1.sqlite, but theirhost_session_accessrows were gone, so they vanished from the owner's Session list.Cause: SQLite coordinates WAL access through POSIX locks and the shared-memory
-shmindex. gVisor does not share either with processes on the host machine. A host-side connection therefore thinks it is the last connection. When it closes, it checkpoints and deletes the live-wal/-shm. This includes a plainsqlite3 opengui-identity-v1.sqlite "SELECT …". The Host keeps writing to the now-unlinked WAL and still serves its own writes, so nothing looks wrong until the next restart. Then everything since that host-side access is gone.docs/operations.mdanddocs/sandbox-hosting.mdcurrently recommend exactly this pattern for online backups ("use SQLite's backup API orVACUUM INTOfrom an operator-controlled process").Reproduction (runsc)
--runtime=runsccontainer with a bind-mounted data dir. It uses WAL mode withwal_autocheckpoint=0and keeps its connection open.sqlite3 t.sqlite "select count(*) from t". Afterwards-waland-shmare gone from the directory.[A, B].[A].If the same snapshot is taken inside the sandbox, nothing is lost.
Change
scripts/backup-databases.ts: a new operator command that follows thereset-password.tspattern:docker exec opengui node --experimental-strip-types scripts/backup-databases.ts [target-dir]node:sqlite's onlinebackup()from inside the Host's container/sandbox, so SQLite's locking stays coherent.journal_mode = DELETE, no sidecars,quick_checkverified and mode0600. The default target is$OPENGUI_DATA_DIR/backups/<UTC timestamp>/, so host-side tools only ever copy closed files.operations.md,sandbox-hosting.md,docker.md): replace the external-process backup advice with this command. Add an explicit warning never to open the live databases from outside the sandbox, read-only CLI queries included, and explain the failure mode.scripts/backup-databases.test.ts):0600/journal_mode=delete.Verified with
pnpm run check,pnpm vp test scripts/backup-databases.test.tsandpnpm run slop-check, all passing. I also ran the script inside a production gVisor container: both snapshots were written, and the live-walinodes on the host were unchanged.Possible follow-up (not in this PR)
Defense in depth inside the Host: identity writes are small and rare, so they can sit in the WAL for hours before an auto-checkpoint. A
PRAGMA wal_checkpoint(PASSIVE)after identity writes, or a periodic one, would get committed rows into the main file promptly. That would greatly reduce what a stray host-side connection can destroy. Happy to send that separately if you want it.🤖 Generated with Claude Code