From dfcbda44e651feb849873492e8d2ca87a34d66f3 Mon Sep 17 00:00:00 2001 From: Fabio Canesin Date: Mon, 17 Aug 2026 12:35:29 -0300 Subject: [PATCH 1/3] deps!: require 0xCarbon/go-sqlite3 directly at latest master (SQLCipher 4.17); drop the replace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Import github.com/0xCarbon/go-sqlite3 by its own module path — the fork re-pathed its module (0xCarbon/go-sqlite3#4) — instead of requiring mattn/go-sqlite3 behind a replace directive. Consumers no longer need a mattn replace to consume litestream; they must use the 0xCarbon path for their own sqlite imports too (mixing both paths registers the "sqlite3" driver twice and panics at init). Bumps the driver to fork master d1c4d32: SQLCipher amalgamation 4.17.0 (SQLite 3.53.3), upstream v1.14.49-era merge, _key DSN parameter, hardened keyed-open failure paths, key-handling docs. --- AGENTS.md | 12 ++++++------ _examples/library/basic/main.go | 2 +- _examples/library/library_example_test.go | 2 +- _examples/library/s3/main.go | 2 +- cmd/litestream-test/load.go | 2 +- cmd/litestream-test/populate.go | 2 +- cmd/litestream-test/shrink.go | 2 +- cmd/litestream-test/validate.go | 2 +- cmd/litestream-vfs/main_test.go | 2 +- cmd/litestream-vfs/time_travel_test.go | 2 +- cmd/litestream-vfs/vfs_soak_test.go | 2 +- cmd/litestream/main.go | 2 +- db.go | 2 +- db_internal_test.go | 2 +- docs/PATTERNS.md | 2 +- docs/SQLITE_INTERNALS.md | 2 +- docs/VFS.md | 2 +- encryption_test.go | 2 +- go.mod | 4 +--- go.sum | 6 ++++-- litestream.go | 2 +- litestream_test.go | 2 +- replica.go | 2 +- replica_internal_test.go | 2 +- tests/integration/boundary_test.go | 2 +- tests/integration/concurrent_test.go | 2 +- tests/integration/directory_watcher_helpers.go | 2 +- tests/integration/fixtures.go | 2 +- tests/integration/helpers.go | 2 +- tests/integration/minio_soak_test.go | 2 +- tests/integration/overnight_s3_soak_test.go | 2 +- tests/integration/overnight_test.go | 2 +- tests/integration/profile_test.go | 2 +- tests/integration/quick_test.go | 2 +- tests/integration/s3_restore_connection_drop_test.go | 2 +- tests/integration/scenario_test.go | 2 +- tests/integration/shutdown_retry_test.go | 2 +- tests/integration/soak_replicate_restore_test.go | 2 +- tests/integration/upgrade_test.go | 2 +- 39 files changed, 47 insertions(+), 47 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 901c2e061..dd8f7cd34 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,12 +86,12 @@ This fork tracks upstream `benbjohnson/litestream` and adds: - **VFS**: `VFS.SkipJournalPatch` / `VFSFile.SkipJournalPatch` disable the page-1 journal-mode emulation patch, required for SQLCipher databases (plaintext bytes 18-19/24-28 must not be rewritten on ciphertext pages). -- **Mandatory consumer replace**: Go ignores this module's `replace` - directives when litestream is imported as a dependency. Every consuming - module MUST carry its own replace or the build fails with - `EncryptionKey`/`EncryptionKeyBytes` undefined on the upstream driver: - - replace github.com/mattn/go-sqlite3 => github.com/0xCarbon/go-sqlite3 v1.14.38-0.20260515201505-c8f057756220 +- **Driver dependency**: litestream requires the 0xCarbon SQLCipher fork + directly (`github.com/0xCarbon/go-sqlite3`) — no `replace` directive is + needed or used. Consumers must use the same module path for their own + direct sqlite use: importing both `github.com/mattn/go-sqlite3` and + `github.com/0xCarbon/go-sqlite3` in one binary registers the "sqlite3" + driver twice and panics at init. - **Pin policy**: consumer modules pin this fork via the `pin/` branches (e.g. `pin/7a606e384ee9`); never force-push or delete them. diff --git a/_examples/library/basic/main.go b/_examples/library/basic/main.go index eb456ca1b..51525c94d 100644 --- a/_examples/library/basic/main.go +++ b/_examples/library/basic/main.go @@ -16,7 +16,7 @@ import ( "syscall" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" "github.com/benbjohnson/litestream/file" diff --git a/_examples/library/library_example_test.go b/_examples/library/library_example_test.go index de2c6826a..7cba4430d 100644 --- a/_examples/library/library_example_test.go +++ b/_examples/library/library_example_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" "github.com/benbjohnson/litestream/file" diff --git a/_examples/library/s3/main.go b/_examples/library/s3/main.go index 731daa16f..a6e17abc5 100644 --- a/_examples/library/s3/main.go +++ b/_examples/library/s3/main.go @@ -27,7 +27,7 @@ import ( "syscall" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" "github.com/benbjohnson/litestream/s3" diff --git a/cmd/litestream-test/load.go b/cmd/litestream-test/load.go index 7426d9c44..9847ebda9 100644 --- a/cmd/litestream-test/load.go +++ b/cmd/litestream-test/load.go @@ -15,7 +15,7 @@ import ( "syscall" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) type LoadCommand struct { diff --git a/cmd/litestream-test/populate.go b/cmd/litestream-test/populate.go index 483e52bf9..4ffcde06e 100644 --- a/cmd/litestream-test/populate.go +++ b/cmd/litestream-test/populate.go @@ -11,7 +11,7 @@ import ( "os" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) type PopulateCommand struct { diff --git a/cmd/litestream-test/shrink.go b/cmd/litestream-test/shrink.go index ddd2b224e..f0ab3e763 100644 --- a/cmd/litestream-test/shrink.go +++ b/cmd/litestream-test/shrink.go @@ -9,7 +9,7 @@ import ( "os" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) type ShrinkCommand struct { diff --git a/cmd/litestream-test/validate.go b/cmd/litestream-test/validate.go index da46ca072..cea420ff1 100644 --- a/cmd/litestream-test/validate.go +++ b/cmd/litestream-test/validate.go @@ -13,7 +13,7 @@ import ( "strings" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) type ValidateCommand struct { diff --git a/cmd/litestream-vfs/main_test.go b/cmd/litestream-vfs/main_test.go index 65492f116..21a17d688 100644 --- a/cmd/litestream-vfs/main_test.go +++ b/cmd/litestream-vfs/main_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - sqlite3 "github.com/mattn/go-sqlite3" + sqlite3 "github.com/0xCarbon/go-sqlite3" "github.com/psanford/sqlite3vfs" "github.com/stretchr/testify/require" diff --git a/cmd/litestream-vfs/time_travel_test.go b/cmd/litestream-vfs/time_travel_test.go index ed2dd6c63..971b9b2fe 100644 --- a/cmd/litestream-vfs/time_travel_test.go +++ b/cmd/litestream-vfs/time_travel_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/psanford/sqlite3vfs" "github.com/stretchr/testify/require" diff --git a/cmd/litestream-vfs/vfs_soak_test.go b/cmd/litestream-vfs/vfs_soak_test.go index 79767d461..eb11b9e79 100644 --- a/cmd/litestream-vfs/vfs_soak_test.go +++ b/cmd/litestream-vfs/vfs_soak_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream/file" "github.com/benbjohnson/litestream/internal/testingutil" diff --git a/cmd/litestream/main.go b/cmd/litestream/main.go index 275c26634..0ba09704c 100644 --- a/cmd/litestream/main.go +++ b/cmd/litestream/main.go @@ -18,8 +18,8 @@ import ( "strings" "time" + _ "github.com/0xCarbon/go-sqlite3" "github.com/dustin/go-humanize" - _ "github.com/mattn/go-sqlite3" "github.com/superfly/ltx" _ "golang.org/x/crypto/x509roots/fallback" "gopkg.in/yaml.v2" diff --git a/db.go b/db.go index c449b219c..7fb74b4d5 100644 --- a/db.go +++ b/db.go @@ -19,7 +19,7 @@ import ( "syscall" "time" - "github.com/mattn/go-sqlite3" + "github.com/0xCarbon/go-sqlite3" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/superfly/ltx" diff --git a/db_internal_test.go b/db_internal_test.go index 533367a68..df0536a5e 100644 --- a/db_internal_test.go +++ b/db_internal_test.go @@ -22,7 +22,7 @@ import ( "github.com/superfly/ltx" "golang.org/x/sync/semaphore" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream/internal" ) diff --git a/docs/PATTERNS.md b/docs/PATTERNS.md index 365ab0e9a..c0fa73b3e 100644 --- a/docs/PATTERNS.md +++ b/docs/PATTERNS.md @@ -439,7 +439,7 @@ Lock page numbers by page size: **Responsibilities:** -- Manages SQLite database connection (via `mattn/go-sqlite3`; CGO required, SQLCipher supported) +- Manages SQLite database connection (via `0xCarbon/go-sqlite3`; CGO required, SQLCipher supported) - Monitors WAL for changes - Performs checkpoints - Maintains long-running read transaction diff --git a/docs/SQLITE_INTERNALS.md b/docs/SQLITE_INTERNALS.md index 2e70b7300..2475d0737 100644 --- a/docs/SQLITE_INTERNALS.md +++ b/docs/SQLITE_INTERNALS.md @@ -437,7 +437,7 @@ Litestream uses both database/sql and direct SQLite APIs: // Using database/sql for queries db, err := sql.Open("sqlite3", "database.db") -// Using mattn/go-sqlite3 for low-level access (CGO) +// Using 0xCarbon/go-sqlite3 for low-level access (CGO) conn, err := sqlite3.Open("database.db") // Direct page access (requires special builds) diff --git a/docs/VFS.md b/docs/VFS.md index e85779dde..ddc290c7a 100644 --- a/docs/VFS.md +++ b/docs/VFS.md @@ -166,7 +166,7 @@ import ( "database/sql" "os" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) func main() { diff --git a/encryption_test.go b/encryption_test.go index 2c145aae9..0db3d52d0 100644 --- a/encryption_test.go +++ b/encryption_test.go @@ -10,7 +10,7 @@ import ( "sync/atomic" "testing" - "github.com/mattn/go-sqlite3" + "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" "github.com/benbjohnson/litestream/file" diff --git a/go.mod b/go.mod index 4a63fb9ce..6dd779115 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/mark3labs/mcp-go v0.32.0 github.com/mattn/go-shellwords v1.0.12 - github.com/mattn/go-sqlite3 v1.14.24 github.com/nats-io/nats.go v1.44.0 github.com/pkg/sftp v1.13.6 github.com/prometheus/client_golang v1.17.0 @@ -51,6 +50,7 @@ require ( ) require ( + github.com/0xCarbon/go-sqlite3 v1.14.38-0.20260817152354-d1c4d3294fa8 github.com/lmittmann/tint v1.1.3 github.com/mattn/go-isatty v0.0.20 github.com/pierrec/lz4/v4 v4.1.23 @@ -131,5 +131,3 @@ require ( google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -replace github.com/mattn/go-sqlite3 => github.com/0xCarbon/go-sqlite3 v1.14.38-0.20260515201505-c8f057756220 diff --git a/go.sum b/go.sum index 87df8501d..a4becdfc8 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ cloud.google.com/go/pubsub v1.34.0 h1:ZtPbfwfi5rLaPeSvDC29fFoE20/tQvGrUS6kVJZJvk cloud.google.com/go/pubsub v1.34.0/go.mod h1:alj4l4rBg+N3YTFDDC+/YyFTs6JAjam2QfYsddcAW4c= cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -github.com/0xCarbon/go-sqlite3 v1.14.38-0.20260515201505-c8f057756220 h1:6mAhRjRjEXizhq0G7k3LnXFzyuMp4vR4L62RL95Bt9w= -github.com/0xCarbon/go-sqlite3 v1.14.38-0.20260515201505-c8f057756220/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/0xCarbon/go-sqlite3 v1.14.38-0.20260817152354-d1c4d3294fa8 h1:dSe2JsAQq07u4+dt94EyyDm5tVEmmxD0YeA9rA7mlxo= +github.com/0xCarbon/go-sqlite3 v1.14.38-0.20260817152354-d1c4d3294fa8/go.mod h1:ANMAfeds95Wt2uetNKPPPenXJ6XqHK53ltmgttIvtnQ= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2 h1:Hr5FTipp7SL07o2FvoVOX9HRiRH3CR3Mj8pxqCcdD5A= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 h1:MhRfI58HblXzCtWEZCO0feHs8LweePB3s90r7WaR1KU= @@ -203,6 +203,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= +github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= diff --git a/litestream.go b/litestream.go index 41c31794e..e43e294a6 100644 --- a/litestream.go +++ b/litestream.go @@ -16,7 +16,7 @@ import ( "strconv" "strings" - "github.com/mattn/go-sqlite3" + "github.com/0xCarbon/go-sqlite3" "github.com/superfly/ltx" ) diff --git a/litestream_test.go b/litestream_test.go index 3addaf47b..fa3e736c5 100644 --- a/litestream_test.go +++ b/litestream_test.go @@ -11,7 +11,7 @@ import ( "github.com/superfly/ltx" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" ) diff --git a/replica.go b/replica.go index 6a9d02a84..f1bc9b3b2 100644 --- a/replica.go +++ b/replica.go @@ -14,7 +14,7 @@ import ( "sync/atomic" "time" - "github.com/mattn/go-sqlite3" + "github.com/0xCarbon/go-sqlite3" "github.com/superfly/ltx" "golang.org/x/sync/semaphore" diff --git a/replica_internal_test.go b/replica_internal_test.go index 68735d181..35c0eea5f 100644 --- a/replica_internal_test.go +++ b/replica_internal_test.go @@ -14,7 +14,7 @@ import ( "testing" "time" - "github.com/mattn/go-sqlite3" + "github.com/0xCarbon/go-sqlite3" "github.com/superfly/ltx" ) diff --git a/tests/integration/boundary_test.go b/tests/integration/boundary_test.go index d1725c015..ddf362c2f 100644 --- a/tests/integration/boundary_test.go +++ b/tests/integration/boundary_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) func Test1GBBoundary(t *testing.T) { diff --git a/tests/integration/concurrent_test.go b/tests/integration/concurrent_test.go index d40f5bf9a..ed1818eda 100644 --- a/tests/integration/concurrent_test.go +++ b/tests/integration/concurrent_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) func TestRapidCheckpoints(t *testing.T) { diff --git a/tests/integration/directory_watcher_helpers.go b/tests/integration/directory_watcher_helpers.go index 2b0b8cb61..926223067 100644 --- a/tests/integration/directory_watcher_helpers.go +++ b/tests/integration/directory_watcher_helpers.go @@ -13,7 +13,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) // DirWatchTestDB extends TestDB with directory-specific functionality diff --git a/tests/integration/fixtures.go b/tests/integration/fixtures.go index 4a553e067..16a68bfa1 100644 --- a/tests/integration/fixtures.go +++ b/tests/integration/fixtures.go @@ -8,7 +8,7 @@ import ( "fmt" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) type LoadPattern string diff --git a/tests/integration/helpers.go b/tests/integration/helpers.go index eeebd8718..24f3e6b06 100644 --- a/tests/integration/helpers.go +++ b/tests/integration/helpers.go @@ -17,7 +17,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" ) diff --git a/tests/integration/minio_soak_test.go b/tests/integration/minio_soak_test.go index ba61c7a94..798f1ec25 100644 --- a/tests/integration/minio_soak_test.go +++ b/tests/integration/minio_soak_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) // TestMinIOSoak runs a soak test against local MinIO S3-compatible server using Docker. diff --git a/tests/integration/overnight_s3_soak_test.go b/tests/integration/overnight_s3_soak_test.go index 72cc7fe7b..893f45c61 100644 --- a/tests/integration/overnight_s3_soak_test.go +++ b/tests/integration/overnight_s3_soak_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) // TestOvernightS3Soak runs an 8-hour overnight soak test against real AWS S3. diff --git a/tests/integration/overnight_test.go b/tests/integration/overnight_test.go index fbc576afc..f5339cab2 100644 --- a/tests/integration/overnight_test.go +++ b/tests/integration/overnight_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) func TestOvernightFile(t *testing.T) { diff --git a/tests/integration/profile_test.go b/tests/integration/profile_test.go index 015bd22f3..8d54cae8f 100644 --- a/tests/integration/profile_test.go +++ b/tests/integration/profile_test.go @@ -15,7 +15,7 @@ import ( "strconv" "testing" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" "github.com/benbjohnson/litestream/file" diff --git a/tests/integration/quick_test.go b/tests/integration/quick_test.go index a6b7ea61d..4b57f4307 100644 --- a/tests/integration/quick_test.go +++ b/tests/integration/quick_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) func TestQuickValidation(t *testing.T) { diff --git a/tests/integration/s3_restore_connection_drop_test.go b/tests/integration/s3_restore_connection_drop_test.go index 480afa4b1..61df1e3ab 100644 --- a/tests/integration/s3_restore_connection_drop_test.go +++ b/tests/integration/s3_restore_connection_drop_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) // TestRestore_S3ConnectionDrop verifies restore can recover from dropped S3-compatible connections. diff --git a/tests/integration/scenario_test.go b/tests/integration/scenario_test.go index 42edf7450..11f4ff9bf 100644 --- a/tests/integration/scenario_test.go +++ b/tests/integration/scenario_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) func TestFreshStart(t *testing.T) { diff --git a/tests/integration/shutdown_retry_test.go b/tests/integration/shutdown_retry_test.go index ae9bf027e..1c0236758 100644 --- a/tests/integration/shutdown_retry_test.go +++ b/tests/integration/shutdown_retry_test.go @@ -22,7 +22,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) // TestShutdownSyncRetry_429Errors tests that Litestream retries syncing LTX files diff --git a/tests/integration/soak_replicate_restore_test.go b/tests/integration/soak_replicate_restore_test.go index 4d26f28bf..4a9cd5131 100644 --- a/tests/integration/soak_replicate_restore_test.go +++ b/tests/integration/soak_replicate_restore_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" ) // TestSoakReplicateRestore reproduces issue #1164: intermittent database corruption diff --git a/tests/integration/upgrade_test.go b/tests/integration/upgrade_test.go index d312cfb74..d9b4eaa56 100644 --- a/tests/integration/upgrade_test.go +++ b/tests/integration/upgrade_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - _ "github.com/mattn/go-sqlite3" + _ "github.com/0xCarbon/go-sqlite3" "github.com/benbjohnson/litestream" ) From 5e174c894d1875355a032a8cb471453b4651a7d4 Mon Sep 17 00:00:00 2001 From: Fabio Canesin Date: Mon, 17 Aug 2026 13:11:38 -0300 Subject: [PATCH 2/3] docs+tests: complete the 0xCarbon re-path sweep; document raw-key length contract; pin 4.14-ciphertext compatibility - Replace the remaining mattn/go-sqlite3 prose references (AGENTS.md intro and driver bullet, skills, code comments, goreleaser note) with the 0xCarbon module path; AGENTS.md previously contradicted its own new Driver dependency bullet. - Document that DB.EncryptionKeyBytes must be exactly 32/48/80 bytes (the fork rejects other lengths at the first connection instead of silently KDF-falling-back) and cover it with a fail-fast test. - Add testdata/sqlcipher-v4.14.0/fixture.db (8KB, written by the previous driver pin c8f057756220) with a decrypt-through-current-pin test, pinning kdf/HMAC default compatibility across amalgamation bumps. --- .goreleaser.yml | 2 +- AGENTS.md | 12 ++-- db.go | 2 +- db_internal_test.go | 36 +++++++++++ encryption_test.go | 64 +++++++++++++++++++ litestream.go | 4 +- skills/litestream/SKILL.md | 2 +- skills/litestream/references/ARCHITECTURE.md | 2 +- testdata/sqlcipher-v4.14.0/fixture.db | Bin 0 -> 8192 bytes 9 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 testdata/sqlcipher-v4.14.0/fixture.db diff --git a/.goreleaser.yml b/.goreleaser.yml index 22f702c33..a9d6e42a2 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -10,7 +10,7 @@ builds: - id: litestream main: ./cmd/litestream binary: litestream - # The fork's mattn/go-sqlite3 driver builds SQLCipher (CGO, links + # The 0xCarbon/go-sqlite3 fork builds SQLCipher (CGO, links # libcrypto); pure-Go builds are no longer possible. Windows, macOS, and # 32-bit ARM targets are dropped until cross-CGO toolchains with libcrypto # are wired up; fork consumers are Linux. diff --git a/AGENTS.md b/AGENTS.md index dd8f7cd34..43fb06bc2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md - Litestream AI Agent Guide -Litestream is a disaster recovery tool for SQLite that runs as a background process, monitors the WAL, converts changes to immutable LTX files, and replicates them to cloud storage. This fork uses `mattn/go-sqlite3` (via the 0xCarbon fork with bundled SQLCipher) — CGO is required and libcrypto is linked. +Litestream is a disaster recovery tool for SQLite that runs as a background process, monitors the WAL, converts changes to immutable LTX files, and replicates them to cloud storage. This fork uses `github.com/0xCarbon/go-sqlite3` (the mattn-lineage fork with bundled SQLCipher) — CGO is required and libcrypto is linked. ## Before You Start @@ -76,13 +76,15 @@ Before submitting changes: This fork tracks upstream `benbjohnson/litestream` and adds: -- **Driver**: per-database `mattn/go-sqlite3` driver instances (registered as +- **Driver**: per-database `github.com/0xCarbon/go-sqlite3` driver instances (registered as `litestream-sqlite3-`), each carrying its database's SQLCipher key; see `registerSQLiteDriver` in `litestream.go`. - **Encryption**: `DB.EncryptionKey` (string: bare `x'…'` raw-key blob is auto-quoted; quoted literals `'passphrase'`/`"x'…'"` pass through) and - `DB.EncryptionKeyBytes` (raw bytes; takes precedence). Keys are applied as - the first statement of every pooled connection; set before Open. + `DB.EncryptionKeyBytes` (raw bytes; takes precedence; must be exactly 32, + 48, or 80 bytes — other lengths are rejected at the first connection). + Keys are applied as the first statement of every pooled connection; set + before Open. - **VFS**: `VFS.SkipJournalPatch` / `VFSFile.SkipJournalPatch` disable the page-1 journal-mode emulation patch, required for SQLCipher databases (plaintext bytes 18-19/24-28 must not be rewritten on ciphertext pages). @@ -102,7 +104,7 @@ This fork tracks upstream `benbjohnson/litestream` and adds: stock runners do not provide; fork consumers are Linux. - **Durability**: litestream's own connections run `synchronous=FULL` (DSN `_sync=FULL`), matching upstream's modernc default. Consumer connections - through the mattn fork default to `synchronous=NORMAL` — pass `_sync=FULL` + through the 0xCarbon fork (mattn-lineage) default to `synchronous=NORMAL` — pass `_sync=FULL` in your DSN if you need the stricter behavior. - **CLI**: the `litestream` binary is encryption-blind — there is no key configuration; SQLCipher is a library-only feature via `DB.EncryptionKey` diff --git a/db.go b/db.go index 7fb74b4d5..74097ba5b 100644 --- a/db.go +++ b/db.go @@ -1037,7 +1037,7 @@ func (db *DB) init(ctx context.Context) (err error) { dsn := db.path // _sync=FULL restores the durability litestream had with modernc: the - // mattn driver defaults every connection to synchronous=NORMAL, which + // go-sqlite3 fork defaults every connection to synchronous=NORMAL, which // can lose the newest commits on power loss in WAL mode. dsn += fmt.Sprintf("?_busy_timeout=%d&_sync=FULL", db.BusyTimeout.Milliseconds()) diff --git a/db_internal_test.go b/db_internal_test.go index df0536a5e..8e0c8cb10 100644 --- a/db_internal_test.go +++ b/db_internal_test.go @@ -4689,3 +4689,39 @@ func TestDB_InitSetsSyncFull(t *testing.T) { t.Fatalf("litestream pool connections must run synchronous=FULL (2), got %d", sync) } } + +// TestDB_EncryptionKeyBytesLengthRejected asserts the driver's raw-key +// length contract surfaces through litestream's own pool: SQLCipher honors +// raw keys only at 32/48/80 bytes; the 0xCarbon fork rejects other lengths +// at connection open instead of silently KDF-falling-back. +func TestDB_EncryptionKeyBytesLengthRejected(t *testing.T) { + dbPath := filepath.Join(t.TempDir(), "db") + setup, err := sql.Open("sqlite3", dbPath) + if err != nil { + t.Fatal(err) + } + if _, err := setup.Exec(`CREATE TABLE t (v TEXT);`); err != nil { + t.Fatal(err) + } + setup.Close() + + db := NewDB(dbPath) + db.EncryptionKeyBytes = make([]byte, 16) // invalid: not 32/48/80 + client := &testReplicaClient{dir: t.TempDir()} + r := NewReplicaWithClient(db, client) + r.MonitorEnabled = false + db.Replica = r + if err := db.Open(); err != nil { + t.Fatal(err) + } + defer func() { _ = db.Close(context.Background()) }() + + // db.Open does not connect; the first pooled connection (init) fails fast. + err = db.init(context.Background()) + if err == nil { + t.Fatal("expected invalid raw key length to be rejected") + } + if !strings.Contains(err.Error(), "invalid raw key length") || !strings.Contains(err.Error(), "must be 32, 48, or 80 bytes") { + t.Fatalf("unexpected error: %v", err) + } +} diff --git a/encryption_test.go b/encryption_test.go index 0db3d52d0..8821a209e 100644 --- a/encryption_test.go +++ b/encryption_test.go @@ -1,10 +1,12 @@ package litestream_test import ( + "bytes" "context" "database/sql" "encoding/hex" "fmt" + "os" "path/filepath" "sync" "sync/atomic" @@ -186,6 +188,68 @@ func TestDB_ConcurrentDifferentKeys(t *testing.T) { wg.Wait() } +// sqlcipher414FixtureKey is the raw key of testdata/sqlcipher-v4.14.0/fixture.db. +func sqlcipher414FixtureKey() []byte { + key := make([]byte, 32) + for i := range key { + key[i] = byte(i*7 + 3) + } + return key +} + +// TestSQLCipher414FixtureDecrypts pins the cross-version ciphertext +// contract: databases written by the previous driver pin (0xCarbon fork +// c8f057756220, SQLCipher 4.14.0 / SQLite 3.51.3) must keep opening under +// the current pin (SQLCipher 4.17.0) with unchanged kdf/HMAC defaults. +// Regenerate the fixture only with a deliberate compatibility decision. +func TestSQLCipher414FixtureDecrypts(t *testing.T) { + fixturePath := filepath.Join("testdata", "sqlcipher-v4.14.0", "fixture.db") + fixture, err := os.ReadFile(fixturePath) + if err != nil { + t.Fatalf("read fixture: %v", err) + } + // Guard against an accidentally-plaintext fixture. + if bytes.Equal(fixture[:15], []byte("SQLite format 3")) { + t.Fatal("fixture is plaintext; regenerate with the recorded generator") + } + + dbPath := filepath.Join(t.TempDir(), "db") + if err := os.WriteFile(dbPath, fixture, 0o600); err != nil { + t.Fatal(err) + } + + // Litestream's own path: init reads the database with the key. + db := litestream.NewDB(dbPath) + db.EncryptionKeyBytes = sqlcipher414FixtureKey() + client := file.NewReplicaClient(t.TempDir()) + r := litestream.NewReplicaWithClient(db, client) + r.MonitorEnabled = false + db.Replica = r + if err := db.Open(); err != nil { + t.Fatal(err) + } + if err := db.Sync(context.Background()); err != nil { + t.Fatalf("sync over 4.14-written database: %v", err) + } + if err := db.Close(context.Background()); err != nil { + t.Fatal(err) + } + + // And the data survives intact through the current driver. + verifier, err := sql.Open(registerKeyedSQLite(t, sqlcipher414FixtureKey()), dbPath) + if err != nil { + t.Fatal(err) + } + defer verifier.Close() + var payload string + if err := verifier.QueryRow(`SELECT payload FROM fixture ORDER BY id LIMIT 1`).Scan(&payload); err != nil { + t.Fatalf("read fixture rows: %v", err) + } + if payload != "sqlcipher-4.14-compat" { + t.Fatalf("payload = %q", payload) + } +} + // TestDB_EncryptionKeyStringForms verifies the accepted string key forms // through a full replicate/restore round trip. The driver interpolates the // string into `PRAGMA key = %s;`, so the bare blob form must be auto-quoted diff --git a/litestream.go b/litestream.go index e43e294a6..b1f889b93 100644 --- a/litestream.go +++ b/litestream.go @@ -38,7 +38,7 @@ func normalizeEncryptionKey(key string) string { var blobKeyRe = regexp.MustCompile(`^x'[0-9a-fA-F]+'$`) -// newSQLiteDriver builds a mattn/go-sqlite3 driver bound to one database's +// newSQLiteDriver builds a 0xCarbon/go-sqlite3 (mattn-lineage) driver bound to one database's // SQLCipher key. The key is applied as the first statement of every new // connection; the connect hook applies Litestream's per-connection settings. func newSQLiteDriver(encryptionKey string, encryptionKeyBytes []byte) *sqlite3.SQLiteDriver { @@ -54,7 +54,7 @@ func newSQLiteDriver(encryptionKey string, encryptionKeyBytes []byte) *sqlite3.S } // Litestream owns checkpointing; disable SQLite's automatic WAL // checkpoints (upstream disables this via modernc's _pragma= DSN, - // which mattn/go-sqlite3 does not support). + // which the go-sqlite3 fork does not support). if _, err := conn.Exec("PRAGMA wal_autocheckpoint = 0;", nil); err != nil { return fmt.Errorf("cannot disable wal_autocheckpoint: %w", err) } diff --git a/skills/litestream/SKILL.md b/skills/litestream/SKILL.md index ea06992f1..e71791e40 100644 --- a/skills/litestream/SKILL.md +++ b/skills/litestream/SKILL.md @@ -20,7 +20,7 @@ metadata: Litestream is a standalone disaster recovery tool for SQLite. It runs as a background process, monitors the SQLite WAL (Write-Ahead Log), converts changes to immutable LTX files, and replicates them to cloud storage. It uses -`mattn/go-sqlite3` with bundled SQLCipher (CGO required; links libcrypto). +`github.com/0xCarbon/go-sqlite3` with bundled SQLCipher (CGO required; links libcrypto). ## Quick Start diff --git a/skills/litestream/references/ARCHITECTURE.md b/skills/litestream/references/ARCHITECTURE.md index 26c392911..f645fa0bc 100644 --- a/skills/litestream/references/ARCHITECTURE.md +++ b/skills/litestream/references/ARCHITECTURE.md @@ -26,7 +26,7 @@ The heart of Litestream. Manages a single SQLite database. Key fields: - `path` / `metaPath`: Database and metadata paths -- `db *sql.DB`: SQLite connection (via mattn/go-sqlite3, CGO + SQLCipher) +- `db *sql.DB`: SQLite connection (via 0xCarbon/go-sqlite3, CGO + SQLCipher) - `f *os.File`: Long-running file descriptor - `rtx *sql.Tx`: Long-running read transaction (prevents checkpoint past read point) - `pageSize int`: Database page size (critical for lock page calculation) diff --git a/testdata/sqlcipher-v4.14.0/fixture.db b/testdata/sqlcipher-v4.14.0/fixture.db new file mode 100644 index 0000000000000000000000000000000000000000..d057f49fd0143160b4ee062898894192e64e46d5 GIT binary patch literal 8192 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 literal 0 HcmV?d00001 From 6f41868a72e433e5cbe1dcbcadc82cb2f19f0c19 Mon Sep 17 00:00:00 2001 From: Fabio Canesin Date: Mon, 17 Aug 2026 13:18:05 -0300 Subject: [PATCH 3/3] docs: record raw-key length constraint on EncryptionKeyBytes; fix stale registerSQLiteDriver reference The EncryptionKeyBytes field comment now states the fork's 32/48/80-byte raw-key contract (an earlier edit attempt missed the field's actual text). The Fork Deltas driver bullet still described the removed sql.Register registry; point at the OpenDB connector helpers instead. --- AGENTS.md | 6 +++--- db.go | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 43fb06bc2..d4fceb500 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -76,9 +76,9 @@ Before submitting changes: This fork tracks upstream `benbjohnson/litestream` and adds: -- **Driver**: per-database `github.com/0xCarbon/go-sqlite3` driver instances (registered as - `litestream-sqlite3-`), each carrying its database's SQLCipher key; - see `registerSQLiteDriver` in `litestream.go`. +- **Driver**: per-database `github.com/0xCarbon/go-sqlite3` driver instances + (private `sql.OpenDB` connectors, see `newSQLiteDriver`/`newSQLitePool` in + `litestream.go`), each carrying its database's SQLCipher key. - **Encryption**: `DB.EncryptionKey` (string: bare `x'…'` raw-key blob is auto-quoted; quoted literals `'passphrase'`/`"x'…'"` pass through) and `DB.EncryptionKeyBytes` (raw bytes; takes precedence; must be exactly 32, diff --git a/db.go b/db.go index 74097ba5b..5c174fdae 100644 --- a/db.go +++ b/db.go @@ -198,8 +198,11 @@ type DB struct { EncryptionKey string // EncryptionKeyBytes is the raw SQLCipher key for this database. When - // non-empty it takes precedence over EncryptionKey. Applied to every - // connection of this DB's registered driver. + // non-empty it takes precedence over EncryptionKey. Must be exactly 32, + // 48, or 80 bytes (SQLCipher raw-key forms: key / key+salt / + // key+HMAC+salt); other lengths are rejected at the first connection by + // the 0xCarbon fork. Applied to every connection of this DB's driver; + // set before Open. EncryptionKeyBytes []byte // sqliteDriver is this DB's own driver instance, carrying its SQLCipher