Turso vs SQLite Encryption at Rest: Architecture, Cipher Suites, and Implementation Differences

Turso implements native encryption-at-rest using modern AEGIS ciphers directly in its core storage layer, while SQLite relies on third-party extensions like SQLCipher that wrap the page-cache I/O with AES-256-CBC.

Turso is a modern, SQLite-compatible database engine that diverges significantly from standard SQLite when it comes to protecting data at rest. Unlike SQLite, which requires external extensions for encryption, Turso builds encryption directly into its storage architecture. This article examines the technical differences between Turso's native encryption and SQLite's extension-based approaches, referencing specific implementation details from the tursodatabase/turso repository.

Architectural Differences: Native Integration vs Extension Layer

Encryption in Turso is not an add-on feature but a fundamental component of the storage engine.

Turso's Core Storage Layer Encryption

In Turso, encryption logic resides in core/storage/encryption.rs, where the CipherMode enum defines supported algorithms including AEGIS-256, AEGIS-256X2/X4, AEGIS-128L, and AEGIS-128X2/X4. The encryption is applied per-page before writing to the WAL or database file, as implemented in core/storage/pager.rs. This tight integration means the pager wraps every read and write operation with EncryptionContext::encrypt_page and decrypt_page methods.

The EncryptionKey enum in core/storage/encryption.rs represents keys as either Key256 or Key128 variants, derived from hex strings via EncryptionKey::from_hex_string. When opening a database, core/connection.rs validates the file header magic bytes against the supplied key before allowing access.

SQLite's Extension-Based Approach

Standard SQLite does not ship with built-in encryption. Third-party solutions like SQLCipher or the commercial SQLite Encryption Extension (SEE) implement encryption by intercepting page-cache I/O operations. These extensions act as Virtual File System (VFS) wrappers or page-cache shims that encrypt/decrypt pages using AES-256-CBC. The key is typically supplied via PRAGMA key = 'hexstring' after connection establishment, and the cipher is usually fixed at compile time rather than selectable at runtime.

Cipher Suites and Performance Characteristics

Turso leverages modern authenticated encryption designed for high-throughput scenarios.

Turso's AEGIS implementation utilizes SIMD-accelerated Rust crates (aegis) optimized for low-latency per-page encryption. The CipherMode enum allows selection of different AEGIS variants per database, with the chosen cipher encoded in the file header and validated during EncryptionContext::new in core/storage/pager.rs.

SQLCipher's AES-256-CBC relies on OpenSSL or custom implementations that vary by platform. While secure, AES-CBC lacks the authentication tags and performance optimizations of AEGIS, and typically offers only a single cipher choice per database build.

Key Management and Configuration

The two systems handle key derivation and storage differently.

Turso requires keys to be configured before database initialization using DatabaseOpts::new().with_encryption(true). The key is stored in the Database struct as encryption_key: Option<EncryptionKey> (defined in core/lib.rs) and passed to the pager via EncryptionContext. Runtime validation occurs in tests/integration/query_processing/encryption.rs, which confirms that cached databases reject incorrect keys even when the file is already in memory.

SQLite with SQLCipher accepts keys via PRAGMA key after opening the connection. As noted in the source analysis, Turso explicitly rejects late configuration attempts: the test suite verifies that calling PRAGMA journal_mode='mvcc' after encryption setup produces the error "configure encryption before PRAGMA journal_mode='mvcc'".

MVCC Integration and Consistent Snapshotting

Turso's encryption design accounts for its Multi-Version Concurrency Control (MVCC) engine.

When a page is copied to create a new version, the same EncryptionContext and EncryptionKey are applied to ensure consistency across snapshots. The core/mvcc/persistent_storage/logical_log.rs file reuses the encryption key for each logical log entry, guaranteeing that historical snapshots remain readable with the original key.

Standard SQLite lacks native MVCC, so SQLCipher and similar extensions do not need to coordinate with versioned pages or maintain encryption consistency across snapshot isolation levels.

VACUUM Behavior and Data Migration

The two systems handle encrypted exports differently.

Turso's VACUUM INTO creates an unencrypted destination file by default. The implementation in core/vdbe/vacuum.rs explicitly copies pages while stripping encryption metadata, as verified in tests/integration/query_processing/encryption.rs (test case test_vacuum_into_unencrypts).

SQLCipher's VACUUM maintains encryption on the destination unless the user explicitly disables it, preserving the security model but requiring additional steps to create unencrypted backups.

Implementation Examples

Opening an Encrypted Database in Turso (Rust)

use turso_core::{Database, DatabaseOpts, CipherMode};

let db = Database::open(
    "/path/to/db.sqlite",
    DatabaseOpts::new()
        .with_encryption(true)
        .with_cipher(CipherMode::Aegis256)
        .with_key_hex("0123abcd...deadbeef")?
)?;

Opening an Encrypted Database in Turso (Go)

import "github.com/tursodatabase/turso-go"

opts := turso.NewDatabaseOpts().
    WithEncryption(true).
    WithCipher(turso.Aegis256).
    WithKeyHex("0123abcd...deadbeef")
db, err := turso.Open("/path/to/db.sqlite", opts)

Opening an Encrypted Database in Turso (JavaScript)

import { openDatabase } from "@tursodatabase/turso";

const db = await openDatabase("/path/to/db.sqlite", {
  encryption: {
    cipher: "aegis256",
    hexkey: "0123abcd...deadbeef"
  }
});

SQLite with SQLCipher (C API)

sqlite3* db;
sqlite3_open("/path/to/db.sqlite", &db);
sqlite3_key(db, "0123abcd...deadbeef", 32);
// Subsequent queries use encrypted pages

Summary

  • Turso implements encryption natively in core/storage/encryption.rs using AEGIS ciphers, while SQLite relies on external extensions like SQLCipher using AES-256-CBC.
  • Turso supports multiple cipher selections via CipherMode and requires configuration through DatabaseOpts before opening, whereas SQLCipher uses PRAGMA key after connection.
  • Turso's encryption integrates with its MVCC engine in core/mvcc/persistent_storage/logical_log.rs, ensuring consistent snapshot encryption.
  • VACUUM INTO in Turso automatically produces unencrypted output (core/vdbe/vacuum.rs), while SQLCipher preserves encryption by default.
  • Keys in Turso are handled as EncryptionKey enums derived from hex strings and stored in Database structs, with validation occurring in core/connection.rs.

Frequently Asked Questions

Can I use SQLCipher databases with Turso?

No, Turso uses AEGIS-based encryption stored in core/storage/encryption.rs and is not compatible with SQLCipher's AES-256-CBC format. The file headers and encryption schemes differ fundamentally. To migrate data, you must export from SQLCipher to SQL and import into a new Turso encrypted database.

What happens if I provide the wrong encryption key?

Turso validates the key against file header magic bytes during Database::open in core/connection.rs. If the key is incorrect, the open operation fails immediately. The test suite in tests/integration/query_processing/encryption.rs verifies that even cached database handles reject wrong keys, preventing silent data corruption.

Does Turso encryption support multiple ciphers simultaneously?

Yes, Turso supports multiple AEGIS variants (AEGIS-256, AEGIS-128L, etc.) selectable per database via the CipherMode enum. The chosen cipher is encoded in the database file header and validated by the EncryptionContext in core/storage/pager.rs. However, once a database is created with a specific cipher, that cipher is fixed for that file.

How does encryption affect Turso's performance?

Turso's AEGIS implementation leverages SIMD-accelerated Rust crates designed for high-throughput, low-latency per-page encryption. Unlike AES-256-CBC implementations that may vary by platform, AEGIS provides consistent performance across supported architectures while maintaining authentication tags that prevent tampering.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →