SqliteWasmBlazor.Crypto 0.9.1-pre

This is a prerelease version of SqliteWasmBlazor.Crypto.
dotnet add package SqliteWasmBlazor.Crypto --version 0.9.1-pre
                    
NuGet\Install-Package SqliteWasmBlazor.Crypto -Version 0.9.1-pre
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SqliteWasmBlazor.Crypto" Version="0.9.1-pre" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SqliteWasmBlazor.Crypto" Version="0.9.1-pre" />
                    
Directory.Packages.props
<PackageReference Include="SqliteWasmBlazor.Crypto" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SqliteWasmBlazor.Crypto --version 0.9.1-pre
                    
#r "nuget: SqliteWasmBlazor.Crypto, 0.9.1-pre"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SqliteWasmBlazor.Crypto@0.9.1-pre
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SqliteWasmBlazor.Crypto&version=0.9.1-pre&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=SqliteWasmBlazor.Crypto&version=0.9.1-pre&prerelease
                    
Install as a Cake Tool

SqliteWasmBlazor

The first known solution providing true filesystem-backed SQLite database with full EF Core support for Blazor WebAssembly.

License: MIT .NET NuGet GitHub Repo stars

Try the Live Demo - Experience persistent SQLite database in your browser! Can be installed as a Progressive Web App (PWA) for offline use.

SqliteWasmBlazor is part of a family of libraries for building offline-first Blazor applications:

Project Description
RxBlazorV2 Reactive programming framework for Blazor built on R3. Uses Roslyn source generators for observable models with reactive property bindings, command patterns, and automatic component generation.
BlazorPRF Absorbed into this repo as SqliteWasmBlazor.Crypto. Originally a standalone library for PRF-based deterministic encryption in Blazor WebAssembly via the WebAuthn PRF extension; the primitives + key-derivation flow now ship as the SqliteWasmBlazor.Crypto package and back the encryption VFS directly.

Together these enable a complete offline-first stack: persistent local storage with optional at-rest encryption (SqliteWasmBlazor + SqliteWasmBlazor.Crypto) + reactive state management (RxBlazorV2).

Coming soon: CryptoSync — end-to-end encrypted multi-device delta sync, built on top of SqliteWasmBlazor.Crypto. Per-row delta sync with permission enforcement, per-group keys, an admin invitation flow, and a whitelist-authenticated PHP relay that never sees plaintext.

About This Project

This is a non-commercial hobby project maintained in my spare time - no fixed update cycle or roadmap. However, "hobby" refers to time and commitment, not craftsmanship: the project is developed with professional standards including proper test coverage and attention to code quality.

Open source thrives on community involvement. The project grows through bug reports, feature requests, pull requests, and real-world feedback. If you're considering this for production use, I'd encourage you to contribute back - that's how open source stays alive, not through promises from a single maintainer, but through shared ownership.

Stability & Status

The public API surface is intentionally kept minimal to reduce the risk of breaking changes. While the API has been stable in practice, this project is pre-1.0: broader real-world feedback is needed before committing to long-term API guarantees. Contributions and usage reports help move toward that goal.

What's New

Passkey-derived encryption (Plane 2)

Optional at-rest encryption for OPFS-backed SQLite databases. The host opts in by registering a 32-byte key with the worker; without that the same VFS falls through to byte-for-byte vendor SAHPool behavior. With the key registered the entire encryption layer engages:

  • Page-level AEAD — every 4 096-byte SQLite slot is sealed with ChaCha20-Poly1305 before it reaches OPFS. The physical slot is [ciphertext(4096) | nonce(12) | tag(16)]; the nonce is fresh CSPRNG bytes per write.
  • AAD-bound to slot identity — the AEAD's associated data binds (versionTag, dbPath, slotIndex), so a tampered or relocated page fails authentication. Cross-database and cross-slot page swaps are rejected on read; legacy or wrong-version ciphertext is rejected outright.
  • WebAuthn-PRF key derivation — the global key is derived from a passkey via the WebAuthn PRF extension through BlazorPRF. No password, no client-side key file; the authenticator does the unlock.
  • Verified unlock, not silent — a slot-0 AEAD probe gates unlock, and a manifest MAC binds the on-disk state to the credential. Wrong key, wrong credential, or tampered manifest fail loudly before any decryption hits the page cache.
  • Whole-disk envelope export (.eds) — encrypted dumps wrap the slot key under a recipient X25519 pubkey (ECIES). Carries a credentialId hint so the receiver's UI can pick the right passkey automatically.
  • Guided import primitive — a single click on a .eds runs the whole ritual: preflight, wipe, EnterEncrypted, rekey-import, manifest rebind. Works from a Plain or Locked disk state; mistargeted envelopes are rejected before the current disk is touched.
  • Plain-ZIP import on encrypted disks — state-aware: a Locked disk breaks to plain (recovery path), an Unlocked disk re-encrypts on write (passkey binding survives). Preflight validates SQLite shape + page geometry before any wipe.
  • Drop-in host UISqliteWasmBlazor.Crypto.UI ships the Authentication / Encryption / DatabaseErrorAlert / SessionExpired panels, fully RxBlazorV2-based and resx-localized (en + de).
  • Offline test adapterSqliteWasmBlazor.Crypto.BouncyCastle mirrors the in-browser primitives in pure C# for tooling and integration tests that need to run without a browser.
  • Formally verified — 3 Tamarin theories under docs/formal/vfs-tamarin/ cover per-slot AEAD soundness, in-place lifecycle, and key-cache / manifest unlock. 36 lemmas, all verified.

Full reference: docs/crypto-vfs.md. Threat model and assurance summary: docs/security/.

Other recent additions

  • V2 Worker-Side Bulk Import/Export - Worker-side prepared statement loops for 10-50x faster import. Self-describing V2 MessagePack format with column metadata. Memory-safe streaming for large datasets (details)
  • Multi-Part Export - Large databases automatically split into manageable parts with a meta file. Adaptive part sizing based on configurable MB limit
  • Raw Database Import/Export - Export and import complete .db files directly from/to OPFS with schema validation and automatic backup/restore on failure (details)
  • Multi-Database Support - Run multiple independent SQLite databases simultaneously in the same Web Worker (details)
  • Incremental Database Export/Import - File-based delta sync with checkpoint management and conflict resolution (details)
  • Real-World Sample - Check out the Datasync TodoApp for offline-first data synchronization with SqliteWasmBlazor

Breaking Changes

  • v0.9.0-pre — CSP hardening: removed the inline data:text/javascript import that auto-detected <base href>. The worker URL is now built from SqliteWasmOptions.BaseHref (default "/").

    Apps deployed on a sub-path must now opt in explicitly:

    builder.Services.AddSqliteWasm(o => o.BaseHref =
        new Uri(builder.HostEnvironment.BaseAddress).AbsolutePath);
    

    This derives BaseHref from the runtime <base href> exactly as the old auto-probe did, but without the CSP-blocked data: import. Root-path (/) deployments need no change.

    Browser-extension builds (which flatten the _content/ underscore prefix) override the asset root:

    builder.Services.AddSqliteWasm(o => o.AssetRoot = "content/SqliteWasmBlazor/");
    

    Companion packages gained the same options pattern:

    • Components: FileOperationsInterop.InitializeAsync(o => o.AssetRoot = "...")
    • FloatingWindow: services.AddFloatingWindow(o => o.AssetRoot = "...")

    Calling a database operation before InitializeSqliteWasmAsync / InitializeSqliteWasmDatabaseAsync<T> now throws InvalidOperationException with a clear message, instead of silently lazy-initialising with default settings (which 404'd on sub-path / extension builds).

  • v0.7.2-pre - SqliteWasmWorkerBridge is now internal. Use ISqliteWasmDatabaseService via DI instead:

    // Program.cs - add service registration
    builder.Services.AddSqliteWasm();
    
    // Components - inject the interface
    @inject ISqliteWasmDatabaseService DatabaseService
    
    // Replace SqliteWasmWorkerBridge.Instance.DeleteDatabaseAsync(...)
    // with:   DatabaseService.DeleteDatabaseAsync(...)
    

What Makes This Special?

Unlike other Blazor WASM database solutions that use in-memory storage or IndexedDB emulation, SqliteWasmBlazor is the first implementation that combines:

  • True Filesystem Storage - Uses OPFS (Origin Private File System) with synchronous access handles
  • Full EF Core Support - Complete ADO.NET provider with migrations, relationships, and LINQ
  • Real SQLite Engine - Official sqlite-wasm (3.50.4) running in Web Worker
  • Persistent Data - Survives page refreshes, browser restarts, and even browser updates
  • No Server Required - Everything runs client-side in the browser
Solution Storage Persistence EF Core Limitations
InMemory RAM None Full Lost on refresh
IndexedDB IndexedDB Yes Limited No SQL, complex API
SQL.js IndexedDB Yes None Manual serialization
besql Cache API Yes Partial Emulated filesystem
SqliteWasmBlazor OPFS Yes Full None!

Public API

SqliteWasmBlazor exposes a stable public API for database management operations via dependency injection:

ISqliteWasmDatabaseService

The primary interface for database operations outside of EF Core:

public interface ISqliteWasmDatabaseService
{
    // Database management
    Task<bool> ExistsDatabaseAsync(string databaseName, CancellationToken ct = default);
    Task DeleteDatabaseAsync(string databaseName, CancellationToken ct = default);
    Task RenameDatabaseAsync(string oldName, string newName, CancellationToken ct = default);
    Task CloseDatabaseAsync(string databaseName, CancellationToken ct = default);

    // Raw .db file import/export
    Task ImportDatabaseAsync(string databaseName, byte[] data, CancellationToken ct = default);
    Task<byte[]> ExportDatabaseAsync(string databaseName, CancellationToken ct = default);

    // V2 bulk import/export (worker-side prepared statement loops)
    Task<int> BulkImportAsync(string databaseName, byte[] payload,
        ConflictResolutionStrategy conflictStrategy = ConflictResolutionStrategy.None,
        CancellationToken ct = default);
    Task<byte[]> BulkExportAsync(string databaseName, BulkExportMetadata metadata,
        CancellationToken ct = default);
}

Usage in components:

@inject ISqliteWasmDatabaseService DatabaseService

@code {
    private async Task ResetDatabaseAsync()
    {
        // Delete and recreate database
        await DatabaseService.DeleteDatabaseAsync("MyApp.db");

        await using var context = await DbContextFactory.CreateDbContextAsync();
        await context.Database.MigrateAsync();
    }

    private async Task ExportAsync()
    {
        // Export raw .db file (closes DB for consistent snapshot, auto-reopens on next query)
        byte[] data = await DatabaseService.ExportDatabaseAsync("MyApp.db");
    }

    private async Task ImportAsync(byte[] data)
    {
        // Import raw .db file (validates SQLite header)
        await DatabaseService.ImportDatabaseAsync("MyApp.db", data);
    }
}

Other Public Types

Type Purpose
SqliteWasmConnection ADO.NET DbConnection for direct SQL access
SqliteWasmCommand ADO.NET DbCommand for query execution
SqliteWasmDataReader ADO.NET DbDataReader for result iteration
SqliteWasmParameter ADO.NET DbParameter for query parameters
SqliteWasmTransaction ADO.NET DbTransaction for transaction support
IDBInitializationService Tracks database initialization state and errors

All internal implementation details (worker bridge, serialization, etc.) are encapsulated and not part of the public API.

Installation

NuGet Package

dotnet add package SqliteWasmBlazor --prerelease

Or install a specific version:

dotnet add package SqliteWasmBlazor --version 0.6.5-pre

Visit NuGet.org for the latest version.

From Source

git clone https://github.com/bernisoft/SqliteWasmBlazor.git
cd SqliteWasmBlazor
dotnet build

Quick Start

1. Configure Your Project

Program.cs:

using SqliteWasmBlazor;

var builder = WebAssemblyHostBuilder.CreateDefault(args);

// Add your DbContext with SqliteWasm provider
builder.Services.AddDbContextFactory<TodoDbContext>(options =>
{
    var connection = new SqliteWasmConnection("Data Source=TodoDb.db");
    options.UseSqliteWasm(connection);
});

// Register initialization service
builder.Services.AddSingleton<IDBInitializationService, DBInitializationService>();

// Register SqliteWasm database management service (for ISqliteWasmDatabaseService)
builder.Services.AddSqliteWasm();

var host = builder.Build();

// Initialize SqliteWasm database with automatic migration support
await host.Services.InitializeSqliteWasmDatabaseAsync<TodoDbContext>();

await host.RunAsync();

The InitializeSqliteWasmDatabaseAsync extension method automatically:

  • Initializes the Web Worker bridge
  • Applies pending migrations (with automatic migration history recovery)
  • Handles multi-tab conflicts with helpful error messages
  • Tracks initialization status via IDBInitializationService

2. Define Your DbContext

using Microsoft.EntityFrameworkCore;

public class TodoDbContext : DbContext
{
    public TodoDbContext(DbContextOptions<TodoDbContext> options) : base(options) { }

    public DbSet<TodoItem> TodoItems { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<TodoItem>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.Property(e => e.Title).IsRequired().HasMaxLength(200);
        });
    }
}

public class TodoItem
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsCompleted { get; set; }
    public DateTime CreatedAt { get; set; }
}

3. Use in Your Components

@inject IDbContextFactory<TodoDbContext> DbFactory

<h3>Todo List</h3>

@foreach (var todo in todos)
{
    <div>
        <input type="checkbox" @bind="todo.IsCompleted" @bind:after="() => SaveTodo(todo)" />
        <span>@todo.Title</span>
    </div>
}

@code {
    private List<TodoItem> todos = new();

    protected override async Task OnInitializedAsync()
    {
        await using var db = await DbFactory.CreateDbContextAsync();
        todos = await db.TodoItems.OrderBy(t => t.CreatedAt).ToListAsync();
    }

    private async Task SaveTodo(TodoItem todo)
    {
        await using var db = await DbFactory.CreateDbContextAsync();
        db.TodoItems.Update(todo);
        await db.SaveChangesAsync(); // Automatically persists to OPFS!
    }
}

Features

Full EF Core Support

// Migrations
await dbContext.Database.MigrateAsync();

// Complex queries with LINQ
var results = await dbContext.Orders
    .Include(o => o.Customer)
    .Where(o => o.Total > 100)
    .OrderByDescending(o => o.Date)
    .ToListAsync();

// Relationships
public class Order
{
    public int Id { get; set; }
    public Customer Customer { get; set; }
    public List<OrderItem> Items { get; set; }
}

// Decimal arithmetic (via ef_ scalar functions)
var expensive = await dbContext.Products
    .Where(p => p.Price * 1.2m > 100m)
    .ToListAsync();

High Performance

  • Efficient Serialization - JSON for requests (small), MessagePack for responses (optimized for data)
  • Typed Column Information - Worker sends type metadata to reduce .NET marshalling overhead
  • OPFS SAHPool - Near-native filesystem performance with synchronous access
  • Direct Execution - Queries run directly on persistent storage, no copying needed

Enterprise-Ready

  • Type Safety - Full .NET type system with proper decimal support
  • EF Core Functions - All ef_* scalar and aggregate functions implemented
  • JSON Collections - Store List<T> with proper value comparers
  • Logging - Configurable logging levels (Debug/Info/Warning/Error)
  • Error Handling - Proper async error propagation

Documentation

Topic Description
Architecture Worker-based architecture, how it works, technical details
ADO.NET Usage Using SqliteWasmBlazor without EF Core, transactions
Advanced Features Migrations, FTS5 search, JSON collections, logging
Multi-Database Running multiple databases, cross-database references
Bulk Import/Export V2 format, multi-part export, delta sync, type conversions
Encrypted VFS At-rest encryption: ChaCha20-Poly1305, PRF-derived keys, threat model
Security Threat model + assurance summary + Tamarin proofs
Recommended Patterns Multi-view pattern, data initialization best practices
FAQ Common questions and browser support
Changelog Release notes and version history

Browser Support

Browser Version OPFS Support
Chrome 108+ Full SAH support
Edge 108+ Full SAH support
Firefox 111+ Full SAH support
Safari 16.4+ Full SAH support

All modern browsers (2023+) support OPFS with Synchronous Access Handles, including mobile browsers (iOS/iPadOS Safari, Android Chrome).

Roadmap

  • Core ADO.NET provider
  • OPFS SAHPool integration
  • EF Core migrations support
  • MessagePack serialization
  • Custom EF functions (decimals)
  • FTS5 full-text search with highlighting and snippets
  • MudBlazor demo app
  • NuGet package pre-release
  • Database export/import API (MessagePack serialization + raw .db files)
  • Backup/restore utilities (delta sync with checkpoints)
  • Raw database import/export with schema validation
  • Multi-database support
  • V2 worker-side bulk import/export with prepared statement loops
  • Multi-part export for large databases
  • Seed Server API for cloud-based database provisioning
  • Stable NuGet package release
  • Server-side delta generation from SQLite databases

Contributing

Issues, bug reports, and feature discussions are always welcome.

For code contributions we use an issue-first policy: please open an issue and agree on the approach before submitting a pull request. Trivial fixes (typos, obvious one-line bugs) can be sent directly. See CONTRIBUTING.md for details and rationale.

Credits

Author: bernisoft License: MIT

Built with:

License

MIT License - Copyright (c) 2025 bernisoft

See LICENSE file for details.


Built with love for the Blazor community

If you find this useful, please star the repository!

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on SqliteWasmBlazor.Crypto:

Package Downloads
SqliteWasmBlazor.Crypto.UI

Base-plane RxBlazorV2 + MudBlazor panels for SqliteWasmBlazor: WebAuthn-PRF authentication / registration, database boot-error alert, session-expired popover. Hosts wire IPrfAuthenticator / ISessionAuthenticator / IDatabaseResetService seams.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.1-pre 69 5/17/2026