From c981acc7afe639dc6e2d1c1217e6c2d800740cdc Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Fri, 19 Dec 2025 12:18:58 +0530 Subject: [PATCH 1/5] Add encryption param Config --- packages/libsql-core/src/api.ts | 3 +++ packages/libsql-core/src/config.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/libsql-core/src/api.ts b/packages/libsql-core/src/api.ts index 4e815128..e22ec94c 100644 --- a/packages/libsql-core/src/api.ts +++ b/packages/libsql-core/src/api.ts @@ -15,6 +15,9 @@ export interface Config { /** Encryption key for the database. */ encryptionKey?: string; + /** Encryption key for encryption in Turso Cloud. */ + remoteEncryptionKey?: string; + /** URL of a remote server to synchronize database with. */ syncUrl?: string; diff --git a/packages/libsql-core/src/config.ts b/packages/libsql-core/src/config.ts index 72cc4beb..31d259de 100644 --- a/packages/libsql-core/src/config.ts +++ b/packages/libsql-core/src/config.ts @@ -11,6 +11,7 @@ export interface ExpandedConfig { path: string; authToken: string | undefined; encryptionKey: string | undefined; + remoteEncryptionKey: string | undefined; syncUrl: string | undefined; syncInterval: number | undefined; readYourWrites: boolean | undefined; @@ -181,6 +182,7 @@ export function expandConfig( fetch: config.fetch, authToken: undefined, encryptionKey: undefined, + remoteEncryptionKey: undefined, authority: undefined, }; } @@ -194,6 +196,7 @@ export function expandConfig( intMode, concurrency, encryptionKey: config.encryptionKey, + remoteEncryptionKey: config.remoteEncryptionKey, syncUrl: config.syncUrl, syncInterval: config.syncInterval, readYourWrites: config.readYourWrites, From 80a43d7630f0c62c0fa1de408b02904c4748dae2 Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Fri, 19 Dec 2025 12:25:33 +0530 Subject: [PATCH 2/5] Pass encryption key to libsql lib --- packages/libsql-client/src/sqlite3.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/libsql-client/src/sqlite3.ts b/packages/libsql-client/src/sqlite3.ts index 66d44f4d..a12895f4 100644 --- a/packages/libsql-client/src/sqlite3.ts +++ b/packages/libsql-client/src/sqlite3.ts @@ -81,6 +81,7 @@ export function _createClient(config: ExpandedConfig): Client { const options = { authToken: config.authToken, encryptionKey: config.encryptionKey, + remoteEncryptionKey: config.remoteEncryptionKey, syncUrl: config.syncUrl, syncPeriod: config.syncInterval, readYourWrites: config.readYourWrites, From 1f60feba9a9d330c75b2b5d0e43619ef9fc327b0 Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Fri, 19 Dec 2025 14:33:02 +0530 Subject: [PATCH 3/5] Pass remote encryption key to hrana --- packages/libsql-client/src/http.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/libsql-client/src/http.ts b/packages/libsql-client/src/http.ts index 05769fdf..72aa2c56 100644 --- a/packages/libsql-client/src/http.ts +++ b/packages/libsql-client/src/http.ts @@ -66,6 +66,7 @@ export function _createClient(config: ExpandedConfig): Client { config.intMode, config.fetch, config.concurrency, + config.remoteEncryptionKey, ); } @@ -79,6 +80,7 @@ export class HttpClient implements Client { #customFetch: Function | undefined; #concurrency: number; #authToken: string | undefined; + #remoteEncryptionKey: string | undefined; #promiseLimitFunction: ReturnType>; /** @private */ @@ -88,17 +90,20 @@ export class HttpClient implements Client { intMode: IntMode, customFetch: Function | undefined, concurrency: number, + remoteEncryptionKey: string | undefined, ) { this.#url = url; this.#authToken = authToken; this.#intMode = intMode; this.#customFetch = customFetch; this.#concurrency = concurrency; + this.#remoteEncryptionKey = remoteEncryptionKey; this.#client = hrana.openHttp( this.#url, this.#authToken, this.#customFetch, + remoteEncryptionKey, ); this.#client.intMode = this.#intMode; this.protocol = "http"; @@ -292,6 +297,7 @@ export class HttpClient implements Client { this.#url, this.#authToken, this.#customFetch, + this.#remoteEncryptionKey, ); this.#client.intMode = this.#intMode; } From ec8a6659121b9a50d1329a3a4b8fb08efdc56586 Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Fri, 19 Dec 2025 16:38:24 +0530 Subject: [PATCH 4/5] Add examples of cloud encryption --- examples/cloud-encryption/.gitignore | 1 + examples/cloud-encryption/README.md | 25 +++++++++++++++++++++++++ examples/cloud-encryption/package.json | 10 ++++++++++ examples/cloud-encryption/remote.mjs | 21 +++++++++++++++++++++ examples/cloud-encryption/sync.mjs | 22 ++++++++++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 examples/cloud-encryption/.gitignore create mode 100644 examples/cloud-encryption/README.md create mode 100644 examples/cloud-encryption/package.json create mode 100644 examples/cloud-encryption/remote.mjs create mode 100644 examples/cloud-encryption/sync.mjs diff --git a/examples/cloud-encryption/.gitignore b/examples/cloud-encryption/.gitignore new file mode 100644 index 00000000..e286dd5c --- /dev/null +++ b/examples/cloud-encryption/.gitignore @@ -0,0 +1 @@ +local.db diff --git a/examples/cloud-encryption/README.md b/examples/cloud-encryption/README.md new file mode 100644 index 00000000..dccba22b --- /dev/null +++ b/examples/cloud-encryption/README.md @@ -0,0 +1,25 @@ +# Cloud Encryption + +These examples demonstrates how to use Turso Cloud encryption. + +Visit the documentation here - [Cloud Encryption](https://docs.turso.tech/cloud/encryption) + +## Install Dependencies + +```bash +npm i +``` + +## Running + +Execute the example which operates over remotely encrypted database: + +```bash +node remote.mjs +``` + +Cloud encryption also supports sync: + +```bash +node sync.mjs +``` diff --git a/examples/cloud-encryption/package.json b/examples/cloud-encryption/package.json new file mode 100644 index 00000000..79a2e190 --- /dev/null +++ b/examples/cloud-encryption/package.json @@ -0,0 +1,10 @@ +{ + "name": "batch", + "version": "1.0.0", + "main": "remote.mjs", + "author": "Turso Authors", + "license": "MIT", + "dependencies": { + "@libsql/client": "^0.16.0" + } +} diff --git a/examples/cloud-encryption/remote.mjs b/examples/cloud-encryption/remote.mjs new file mode 100644 index 00000000..223f01f0 --- /dev/null +++ b/examples/cloud-encryption/remote.mjs @@ -0,0 +1,21 @@ +import { createClient } from "@libsql/client"; + +const client = createClient({ + url: process.env.TURSO_DATABASE_URL, + authToken: process.env.TURSO_AUTH_TOKEN, + remoteEncryptionKey: process.env.TURSO_REMOTE_ENCRYPTION_KEY, +}); + +await client.batch( + [ + "CREATE TABLE IF NOT EXISTS users (email TEXT)", + "INSERT INTO users VALUES ('first@example.com')", + "INSERT INTO users VALUES ('second@example.com')", + "INSERT INTO users VALUES ('third@example.com')", + ], + "write", +); + +const result = await client.execute("SELECT * FROM users"); + +console.log("Users:", result.rows); diff --git a/examples/cloud-encryption/sync.mjs b/examples/cloud-encryption/sync.mjs new file mode 100644 index 00000000..4ab90794 --- /dev/null +++ b/examples/cloud-encryption/sync.mjs @@ -0,0 +1,22 @@ +import { createClient } from "@libsql/client"; + +const client = createClient({ + url: "file:local.db", + syncUrl: process.env.TURSO_DATABASE_URL, + authToken: process.env.TURSO_AUTH_TOKEN, + remoteEncryptionKey: process.env.TURSO_REMOTE_ENCRYPTION_KEY, +}); + +await client.batch( + [ + "CREATE TABLE IF NOT EXISTS users (email TEXT)", + "INSERT INTO users VALUES ('first@example.com')", + "INSERT INTO users VALUES ('second@example.com')", + "INSERT INTO users VALUES ('third@example.com')", + ], + "write", +); + +const result = await client.execute("SELECT * FROM users"); + +console.log("Users:", result.rows); From 1301b207bfc53819c66900a234f8ca1b16caab1b Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Wed, 31 Dec 2025 12:19:30 +0530 Subject: [PATCH 5/5] Update hrana-client to 0.9.0 --- package-lock.json | 70 +++++++++++++++++++++++------ packages/libsql-client/package.json | 2 +- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index e7a6eda9..e99ed0c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1075,26 +1075,17 @@ ] }, "node_modules/@libsql/hrana-client": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.7.0.tgz", - "integrity": "sha512-OF8fFQSkbL7vJY9rfuegK1R7sPgQ6kFMkDamiEccNUvieQ+3urzfDFI616oPl8V7T9zRmnTkSjMOImYCAVRVuw==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.9.0.tgz", + "integrity": "sha512-pxQ1986AuWfPX4oXzBvLwBnfgKDE5OMhAdR/5cZmRaB4Ygz5MecQybvwZupnRz341r2CtFmbk/BhSu7k2Lm+Jw==", "license": "MIT", "dependencies": { - "@libsql/isomorphic-fetch": "^0.3.1", "@libsql/isomorphic-ws": "^0.1.5", + "cross-fetch": "^4.0.0", "js-base64": "^3.7.5", "node-fetch": "^3.3.2" } }, - "node_modules/@libsql/isomorphic-fetch": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.3.1.tgz", - "integrity": "sha512-6kK3SUK5Uu56zPq/Las620n5aS9xJq+jMBcNSOmjhNf/MUvdyji4vrMTqD7ptY7/4/CAVEAYDeotUz60LNQHtw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@libsql/isomorphic-ws": { "version": "0.1.5", "license": "MIT", @@ -1929,6 +1920,35 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, @@ -4481,6 +4501,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-jest": { "version": "29.1.1", "dev": true, @@ -4698,6 +4724,22 @@ "node": ">= 8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "dev": true, @@ -4828,7 +4870,7 @@ "license": "MIT", "dependencies": { "@libsql/core": "^0.16.0", - "@libsql/hrana-client": "^0.7.0", + "@libsql/hrana-client": "^0.9.0", "js-base64": "^3.7.5", "libsql": "^0.5.22", "promise-limit": "^2.7.0" diff --git a/packages/libsql-client/package.json b/packages/libsql-client/package.json index f990b3b3..e0479646 100644 --- a/packages/libsql-client/package.json +++ b/packages/libsql-client/package.json @@ -104,7 +104,7 @@ }, "dependencies": { "@libsql/core": "^0.16.0", - "@libsql/hrana-client": "^0.7.0", + "@libsql/hrana-client": "^0.9.0", "js-base64": "^3.7.5", "libsql": "^0.5.22", "promise-limit": "^2.7.0"