
SQLCipher-encrypted SQLDelight drivers for Android and Apple, bundling static SQLCipher for native targets, eager key validation, platform-specific key semantics, and secure delete with crypto-erase.
SQLCipher-encrypted SQLDelight drivers for Kotlin Multiplatform — one dependency, both platforms:
net.zetetic:sqlcipher-android binaries.iosArm64, iosSimulatorArm64, macosArm64, macosX64): the stock
SQLDelight/SQLiter native driver, linked
against a static SQLCipher bundled inside this library's klib (CommonCrypto provider, same
as Zetetic's official iOS builds). Consumers need no SPM package, no vendored xcframework,
and no linker flags.dependencies {
implementation("io.github.yet300:sqlcipher-driver:<version>")
}// Platform side (DI): AndroidSqlCipherDriverFactory(context) or NativeSqlCipherDriverFactory()
val factory: SqlCipherDriverFactory = NativeSqlCipherDriverFactory()
val driver = factory.create(
schema = MyDatabase.Schema,
config = SqlCipherConfig(
name = "app.db",
key = SqlCipherKey { keyStore.fetchPassphraseBytes() }, // fresh copy per call
recovery = SqlCipherRecovery.Fail, // or .Recreate to drop-and-retry on wrong key
),
)Every open is probed eagerly: a wrong key or corrupt file fails inside create (where
SqlCipherRecovery can act), and PRAGMA cipher_version is asserted non-empty so a mislinked
plain-SQLite binary throws MissingSqlCipherException instead of silently writing plaintext.
deleteDatabase(name) deletes the file the same way the platform driver resolves it
(crypto-erase / panic-wipe hook).
SqlCipherKey.passphrase() returns raw key bytes. Android passes them to SQLCipher as a raw
passphrase (the zetetic factory zeroes the array after open); iOS hex-armors them into a
passphrase string for SQLiter's PRAGMA key. The two file formats therefore intentionally
diverge — encrypted databases are per-device and not portable across platforms.
Exactly one sqlite3_* exporter may exist in the final link — the SQLCipher bundled here:
linkSqlite.set(false) so -lsqlite3 (the plain system SQLite) is never
added to your link.sqlite3_* symbols
(e.g. io.github.yet300:tor does this from 0.1.2). With two exporters, which one wins is
per-symbol link order — silently wrong.nm: no T _sqlite3_open from anything but
this library, plus the built-in cipher_version probe at runtime.sqlcipher-driver/native/build-sqlcipher.sh downloads the pinned SQLCipher source, generates
the amalgamation, and produces native/libs/<slice>/libsqlcipher.a for each published target.
The archives are committed so consumers and CI never rebuild them. Keep the script's
SQLCIPHER_VERSION in lockstep with sqlcipher-android in gradle/libs.versions.toml — one
library version means one SQLCipher version on both platforms.
Same flow as ArtiTor: vanniktech maven-publish to the
Central Portal (central.sonatype.com tokens). Signing activates only when a key is configured,
so publishToMavenLocal works without GPG.
Apache-2.0 for this library. The bundled SQLCipher is © Zetetic LLC under its BSD-style license
(see NOTICE); the Android binaries come from Zetetic's own sqlcipher-android artifact.
SQLCipher-encrypted SQLDelight drivers for Kotlin Multiplatform — one dependency, both platforms:
net.zetetic:sqlcipher-android binaries.iosArm64, iosSimulatorArm64, macosArm64, macosX64): the stock
SQLDelight/SQLiter native driver, linked
against a static SQLCipher bundled inside this library's klib (CommonCrypto provider, same
as Zetetic's official iOS builds). Consumers need no SPM package, no vendored xcframework,
and no linker flags.dependencies {
implementation("io.github.yet300:sqlcipher-driver:<version>")
}// Platform side (DI): AndroidSqlCipherDriverFactory(context) or NativeSqlCipherDriverFactory()
val factory: SqlCipherDriverFactory = NativeSqlCipherDriverFactory()
val driver = factory.create(
schema = MyDatabase.Schema,
config = SqlCipherConfig(
name = "app.db",
key = SqlCipherKey { keyStore.fetchPassphraseBytes() }, // fresh copy per call
recovery = SqlCipherRecovery.Fail, // or .Recreate to drop-and-retry on wrong key
),
)Every open is probed eagerly: a wrong key or corrupt file fails inside create (where
SqlCipherRecovery can act), and PRAGMA cipher_version is asserted non-empty so a mislinked
plain-SQLite binary throws MissingSqlCipherException instead of silently writing plaintext.
deleteDatabase(name) deletes the file the same way the platform driver resolves it
(crypto-erase / panic-wipe hook).
SqlCipherKey.passphrase() returns raw key bytes. Android passes them to SQLCipher as a raw
passphrase (the zetetic factory zeroes the array after open); iOS hex-armors them into a
passphrase string for SQLiter's PRAGMA key. The two file formats therefore intentionally
diverge — encrypted databases are per-device and not portable across platforms.
Exactly one sqlite3_* exporter may exist in the final link — the SQLCipher bundled here:
linkSqlite.set(false) so -lsqlite3 (the plain system SQLite) is never
added to your link.sqlite3_* symbols
(e.g. io.github.yet300:tor does this from 0.1.2). With two exporters, which one wins is
per-symbol link order — silently wrong.nm: no T _sqlite3_open from anything but
this library, plus the built-in cipher_version probe at runtime.sqlcipher-driver/native/build-sqlcipher.sh downloads the pinned SQLCipher source, generates
the amalgamation, and produces native/libs/<slice>/libsqlcipher.a for each published target.
The archives are committed so consumers and CI never rebuild them. Keep the script's
SQLCIPHER_VERSION in lockstep with sqlcipher-android in gradle/libs.versions.toml — one
library version means one SQLCipher version on both platforms.
Same flow as ArtiTor: vanniktech maven-publish to the
Central Portal (central.sonatype.com tokens). Signing activates only when a key is configured,
so publishToMavenLocal works without GPG.
Apache-2.0 for this library. The bundled SQLCipher is © Zetetic LLC under its BSD-style license
(see NOTICE); the Android binaries come from Zetetic's own sqlcipher-android artifact.