Warning
This is a preview release. APIs may change before the v5.0.0 GA release.
Current stable version: 4.4.2 — documented in the main README.
📅 Estimated GA: 30 May 2026.
Please test this preview in non-production environments and open a GitHub Issue for any bug, regression, or unexpected behaviour you encounter. Your feedback is essential to make the GA release solid.
BLite 5.0.0-preview.0 builds on the solid foundation of v4.4.2 and introduces three major feature pillars — Encryption at Rest, Audit Trail, and GDPR Compliance Primitives — together with a new multi-process WAL mode and a generalized Retention Policy. BLite Studio has been updated to expose all these features through a dedicated UI.
| Feature | Summary |
|---|---|
| 🔐 Encryption at Rest | Transparent AES-256-GCM page-level encryption for .db, WAL, and all multi-file layouts |
| 🪵 Audit Trail | IBLiteAuditSink callbacks, in-memory BLiteMetrics, and OpenTelemetry ActivitySource |
| 🛡️ GDPR Compliance Primitives | [PersonalData], subject export, database inspection, CDC masking, and GdprMode.Strict |
| 🔄 Multi-Process WAL | .wal-shm sidecar enables N-reader / 1-writer access across OS processes (opt-in) |
| ⏳ Generalized Retention Policy | Per-collection document expiry on any typed collection, not only TimeSeries |
| 🗑️ Secure Erase & VACUUM | Slot-level data zeroing on delete for GDPR Art. 17 compliance |
| 🖥️ BLite Studio | Encryption unlock, GDPR inspection panel, and subject-data export built into the GUI |
dotnet add package BLite --version 5.0.0-preview.0
If you are using
BLite.BsonorBLite.Cachingseparately, update those packages to5.0.0-preview.0as well.
BLite 5 adds transparent page-level encryption using AES-256-GCM — the industry standard for authenticated, tamper-evident encryption. Encryption is applied at the storage layer: pages are encrypted before writing to disk and decrypted after reading. The rest of the engine — LINQ, CDC, ACID transactions, the WAL — works unchanged.
| Threat | Mitigation |
|---|---|
| Database file stolen from disk | AES-256-GCM encryption of every page |
| WAL file stolen | WAL records encrypted with a separate file-role nonce |
| Backup file exfiltrated | Same encryption applies; optional separate backup key |
| Single file stolen in multi-file layout | HKDF per-file subkeys — each file is encrypted with a different key |
| Tampered file opened | GCM authentication tag rejected → CryptographicException |
| Memory scraping | Out of scope (OS-level responsibility) |
The typed API is the highest-level entry point and the preferred way to use BLite in application code. Inherit from DocumentDbContext, declare your collections as properties, and configure encryption in the constructor:
// 1. Define your entity
public class User
{
public ObjectId Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}
// 2. Define your context
public class AppDb : DocumentDbContext
{
public DocumentCollection<ObjectId, User> Users { get; set; } = null!;
public AppDb(string path, CryptoOptions crypto)
: base(path, crypto) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasIndex(x => x.Email);
}
}
// 3. Open the encrypted database — identical to opening a plain one
using var db = new AppDb("users.db", new CryptoOptions("my-secret-passphrase"));
// 4. CRUD operations are unchanged
await db.Users.InsertAsync(new User { Id = ObjectId.NewObjectId(), Name = "Alice", Email = "[email protected]" });
await db.SaveChangesAsync();
var alice = await db.Users.AsQueryable()
.Where(u => u.Email == "[email protected]")
.FirstOrDefaultAsync();No change in application logic is required beyond the constructor. The Source Generator emits the same zero-allocation mapper code; the encryption/decryption layer sits below the mapper at the page-file boundary.
// Mode 1: Passphrase (PBKDF2-SHA256, 600 000 iterations per OWASP 2023)
// ✔ Simple — suited for human-typed secrets and desktop/mobile apps
var opts = new CryptoOptions("my-secret-passphrase");
// Mode 2: Master-key bytes (HKDF-SHA256, multi-file server mode)
// ✔ KMS-friendly — retrieve 32-byte key from Azure Key Vault, AWS KMS, HSM, etc.
// ✔ Required for multi-file layouts where each file gets its own subkey
byte[] masterKey = await myKeyVault.GetKeyAsync("blite-prod");
var opts = CryptoOptions.FromMasterKey(masterKey);
// Pass to either BLiteEngine (dynamic) or DocumentDbContext (typed):
using var engine = new BLiteEngine("data.db", opts);
// —or—
using var db = new AppDb("data.db", opts);In multi-file mode each logical collection and index lives in its own physical file. The EncryptionCoordinator derives a unique 256-bit subkey per file using HKDF-SHA256 so that stealing one file does not expose the others:
// Multi-file layout: collections stored in a separate directory
var config = new PageFileConfig
{
CollectionDataDirectory = "data/collections",
IndexFilePath = "data/indexes",
WalPath = "data/main.wal",
};
byte[] masterKey = await myKeyVault.GetKeyAsync("blite-prod");
using var db = new AppDb(
"data/main.db",
CryptoOptions.FromMasterKey(masterKey),
kvOptions: null,
baseConfig: config);await BLiteEngine.MigrateToEncryptedAsync(
existingPath: "old.db",
newPath: "new-encrypted.db",
keyProvider: myKeyProvider);
await BLiteEngine.MigrateToPlaintextAsync(
existingPath: "old-encrypted.db",
newPath: "new-plain.db",
keyProvider: myKeyProvider);Both migration helpers handle multi-file layouts automatically (all collection files, index files, and WAL are migrated together as an atomic unit).
await engine.RotateEncryptionKeyAsync(
newKeyProvider: myKeyProvider,
options: new KeyRotationOptions { EmitAuditEvent = true },
ct: cancellationToken);The default NullCryptoProvider is a transparent no-op. When encryption is not configured, the JIT eliminates all crypto-related branches through dead-code elimination: zero overhead for existing code that does not opt in.
BLite 5 introduces a formal audit trail layer on top of the existing real-time metrics subsystem. It provides per-operation callbacks with timing, caller identity, and optional OpenTelemetry integration — all with zero overhead when not configured.
public class AppDb : DocumentDbContext
{
public DocumentCollection<ObjectId, Order> Orders { get; set; } = null!;
public AppDb(string path) : base(path) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasIndex(x => x.Status);
}
}
// Configure the audit sink after construction
var db = new AppDb("orders.db");
db.ConfigureAudit(new BLiteAuditOptions
{
Sink = new MyAuditSink(), // your IBLiteAuditSink impl
EnableMetrics = true, // populate db.AuditMetrics
SlowOperationThreshold = TimeSpan.FromMilliseconds(50), // emit SlowOperationEvent
EnableDiagnosticSource = true, // emit Activity spans (OTel)
});public class MyAuditSink : IBLiteAuditSink
{
// Called after every insert
public void OnInsert(InsertAuditEvent e)
{
Console.WriteLine($"[INSERT] {e.CollectionName} — {e.DocumentSizeBytes} B in {e.Elapsed.TotalMilliseconds:F2} ms");
}
// Called after every LINQ / BLQL query
public void OnQuery(QueryAuditEvent e)
{
Console.WriteLine($"[QUERY] {e.CollectionName} via {e.Strategy} — {e.ResultCount} rows in {e.Elapsed.TotalMilliseconds:F2} ms");
}
// Called after every transaction commit
public void OnCommit(CommitAuditEvent e)
{
Console.WriteLine($"[COMMIT] txn {e.TransactionId} — {e.PagesWritten} pages in {e.Elapsed.TotalMilliseconds:F2} ms");
}
// Called when an operation exceeds SlowOperationThreshold
public void OnSlowOperation(SlowOperationEvent e)
{
Console.WriteLine($"[SLOW] {e.OperationType} on {e.CollectionName} took {e.Elapsed.TotalMilliseconds:F2} ms — {e.Detail}");
}
// All methods have default no-op implementations — override only what you need
}All methods have default no-op implementations (C# 8+ default interface methods). Override only the events you care about.
Inject a custom IAuditContextProvider to attach a user identity to every event — useful for per-user access logs in multi-tenant apps:
public class HttpContextAuditProvider : IAuditContextProvider
{
private readonly IHttpContextAccessor _ctx;
public HttpContextAuditProvider(IHttpContextAccessor ctx) => _ctx = ctx;
public string? GetCurrentUserId() => _ctx.HttpContext?.User?.Identity?.Name;
}
db.ConfigureAudit(new BLiteAuditOptions
{
Sink = new MyAuditSink(),
ContextProvider = new HttpContextAuditProvider(httpContextAccessor),
});Enable metrics to get a lightweight, lock-free snapshot of database activity:
db.ConfigureAudit(new BLiteAuditOptions { EnableMetrics = true });
// Anytime later — all reads use Interlocked, ~10-20 ns overhead
BLiteMetrics m = db.AuditMetrics!;
Console.WriteLine($"Inserts: {m.TotalInserts}");
Console.WriteLine($"Queries: {m.TotalQueries} (index: {m.TotalQueriesIndexScan}, full-scan: {m.TotalQueriesFullScan})");
Console.WriteLine($"Commits: {m.TotalCommits}");
Console.WriteLine($"Cache hit rate: {m.CacheHitRate:P1}");
Console.WriteLine($"Avg insert: {m.AvgInsertMs:F2} ms | Avg query: {m.AvgQueryMs:F2} ms");Set EnableDiagnosticSource = true and register a standard OTEL listener. BLite emits Activity spans via BLiteDiagnostics.ActivitySource (source name: BLite.Core). Zero overhead when no listener is registered (ActivitySource.HasListeners() costs ~5 ns).
// ASP.NET Core / OTEL SDK
services.AddOpenTelemetry()
.WithTracing(b => b.AddSource("BLite.Core"));When ConfigureAudit is never called, the _auditOptions field is null and the JIT eliminates all audit branches through dead-code elimination. Zero overhead for existing code.
BLite is an embedded database — the host application is always the data controller. BLite's role is to provide the technical primitives that make compliance possible and auditable, without imposing policies.
Note
BLite ships primitives, not certifications. The DPIA checklist in docs/DPIA_CHECKLIST.md helps your legal/DPO team map BLite features to GDPR obligations.
Mark personal data at compile time using [PersonalData]. The Source Generator picks up the annotation and emits the PersonalDataFields static member on the generated mapper — no reflection at runtime.
using BLite.Core.GDPR;
public class Customer
{
public ObjectId Id { get; set; }
[PersonalData] // Art. 4(1): ordinary personal data
public string Email { get; set; } = "";
[PersonalData] // Art. 4(1)
public string FullName { get; set; } = "";
[PersonalData(Sensitivity = DataSensitivity.Sensitive)] // financial / health data
public string TaxCode { get; set; } = "";
[PersonalData(Sensitivity = DataSensitivity.Special)] // Art. 9 special categories
public string? MedicalNotes { get; set; }
// Timestamp used by the retention policy
[PersonalData(IsTimestamp = true)]
public DateTime CreatedAt { get; set; }
public string Segment { get; set; } = ""; // not personal — never masked
}Or configure via the fluent Fluent API in OnModelCreating:
modelBuilder.Entity<Customer>()
.Property(x => x.Email)
.HasPersonalData(DataSensitivity.Personal);Produce a portable data report for a subject across the entire database in one call:
public class AppDb : DocumentDbContext
{
public DocumentCollection<ObjectId, Customer> Customers { get; set; } = null!;
public DocumentCollection<ObjectId, Order> Orders { get; set; } = null!;
// … other collections
}
using var db = new AppDb("app.db");
// Art. 15 / 20 — "Give me all data for [email protected]"
var query = new SubjectQuery
{
FieldName = "email",
FieldValue = BsonValue.FromString("[email protected]"),
Format = SubjectExportFormat.Json, // or Csv, Bson
};
await using SubjectDataReport report = await db.ExportSubjectDataAsync(query);
// Export to a portable file
await report.WriteToFileAsync("alice-export.json");
// Or stream directly to an HTTP response
await report.ExportAsJsonAsync(httpResponse.BodyWriter.AsStream());
// Inspect in-memory
foreach (var (collection, docs) in report.DataByCollection)
Console.WriteLine($"{collection}: {docs.Count} document(s)");The engine also exposes the same method as an extension on BLiteEngine for the schema-less path.
Produce a record-of-processing snapshot suitable for DPO reporting:
DatabaseInspectionReport report = db.InspectDatabase();
Console.WriteLine($"Path: {report.DatabasePath}");
Console.WriteLine($"Encrypted: {report.IsEncrypted}"); // AES-256-GCM enabled?
Console.WriteLine($"Audit active: {report.IsAuditEnabled}"); // IBLiteAuditSink registered?
Console.WriteLine($"Layout: {(report.IsMultiFileMode ? "Multi-file" : "Single-file")}");
foreach (CollectionInfo col in report.Collections)
{
Console.WriteLine($"\nCollection: {col.Name}");
Console.WriteLine($" Documents: {col.DocumentCount}");
Console.WriteLine($" Storage: {col.StorageSizeBytes / 1024} KB");
Console.WriteLine($" Personal data: {string.Join(", ", col.PersonalDataFields)}");
if (col.RetentionPolicy is { } rp)
Console.WriteLine($" Retention: MaxAge={rp.MaxAge}, Trigger={rp.Triggers}");
}The Change Data Capture channel is GDPR-safe by default: when CapturePayload = true, every field annotated with [PersonalData] is automatically masked before the event is dispatched to subscribers. Consumers must explicitly opt in to receive personal data in clear.
// Default — GDPR-safe: personal-data fields masked with "***"
using var sub = db.Customers.Watch(new WatchOptions { CapturePayload = true })
.Subscribe(e =>
{
// e.Entity.Email → "***"
// e.Entity.FullName → "***"
// e.Entity.Segment → "premium" (not personal — delivered in clear)
ProcessChange(e);
});
// Opt in to receive personal data in clear (e.g. for a privileged audit service)
using var privilegedSub = db.Customers.Watch(new WatchOptions
{
CapturePayload = true,
RevealPersonalData = true, // explicit consent from consuming code
})
.Subscribe(e => FullAuditLog(e));
// Replace the mask value (e.g. drop the field entirely instead of masking)
using var dropSub = db.Customers.Watch(new WatchOptions
{
CapturePayload = true,
PersonalDataMaskValue = BsonValue.Null, // drop rather than mask
})
.Subscribe(e => DownstreamPipeline(e));
// Allowlist: deliver exactly these fields, regardless of personal-data status
using var allowlistSub = db.Customers.Watch(new WatchOptions
{
CapturePayload = true,
IncludeOnlyFields = ["id", "segment", "createdAt"], // wins over all masking rules
})
.Subscribe(e => Analytics(e));
// Blocklist: exclude specific additional fields on top of personal-data masking
using var blockSub = db.Customers.Watch(new WatchOptions
{
CapturePayload = true,
ExcludeFields = ["taxCode", "medicalNotes"],
})
.Subscribe(e => ProcessChange(e));Masking precedence (applied in order on a deep clone of the payload):
- If
IncludeOnlyFieldsis set → allowlist wins; all other rules are skipped. - Else if
RevealPersonalData = false→ replace each[PersonalData]field withPersonalDataMaskValue(or drop ifBsonValue.Null). - Then apply
ExcludeFieldsblocklist.
GdprMode.Strict enforces a privacy-by-design configuration profile at engine open time. Misconfigured databases fail fast rather than silently.
Configure per-entity via the fluent API:
public class AppDb : DocumentDbContext
{
public DocumentCollection<ObjectId, Customer> Customers { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasGdprMode(GdprMode.Strict) // enforce for this collection
.HasRetentionPolicy(
c => c.CreatedAt,
maxAge: TimeSpan.FromDays(3 * 365)); // 3-year retention
}
}Or apply declaratively via the [GdprMode] attribute:
[GdprMode(GdprMode.Strict)]
public class Customer { /* … */ }Or set an engine-wide default:
var db = new AppDb("app.db", new BLiteKvOptions
{
DefaultGdprMode = GdprMode.Strict,
});When GdprMode.Strict is active, the validator runs at engine open and:
| Condition | Behaviour |
|---|---|
| Encryption absent | Throws InvalidOperationException |
| No audit sink registered | Emits a TraceWarning |
[PersonalData] collection has no retention policy |
Emits a TraceWarning |
SecureEraseOnDelete not enforceable |
Emits a TraceWarning |
Strict mode never deletes data, rotates keys, or modifies stored documents on its own.
Full example combining encryption + audit + strict mode:
// Configure a fully GDPR-hardened database
var db = new AppDb(
"app.db",
new CryptoOptions("production-passphrase"),
new BLiteKvOptions { DefaultGdprMode = GdprMode.Strict });
db.ConfigureAudit(new BLiteAuditOptions
{
Sink = new FileAuditSink("audit.jsonl"),
EnableMetrics = true,
});
// Engine open succeeds only if all Strict requirements are met
// → encryption: ✔ audit sink: ✔ retention on personal-data collections: ✔v5 introduces a .wal-shm sidecar file to enable N-reader / 1-writer access from multiple OS processes on the same database file — following the same principles as SQLite's WAL-mode SHM, adapted to BLite's sequential WAL format.
This feature is opt-in — all existing single-process behaviour is preserved unchanged.
var config = new PageFileConfig
{
AllowMultiProcessAccess = true,
};
// Process A (writer)
using var db = new AppDb("shared.db", config);
// Process B (reader — opened concurrently in a separate process)
using var db = new AppDb("shared.db", config);Important
Multi-process WAL requires all cooperating processes to open the database with AllowMultiProcessAccess = true. A process that opens with AllowMultiProcessAccess = false will hold FileShare.None and block other processes.
Retention policies — previously available only on TimeSeries collections — can now be applied to any typed collection:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Typed collection: keep orders for 7 years (statutory requirement)
modelBuilder.Entity<Order>()
.HasRetentionPolicy(
timestampSelector: o => o.PlacedAt,
maxAge: TimeSpan.FromDays(7 * 365),
triggers: RetentionTrigger.OnInsert | RetentionTrigger.Scheduled);
// Typed collection: keep at most 10 000 log entries
modelBuilder.Entity<AuditLogEntry>()
.HasRetentionPolicy(
timestampSelector: e => e.CreatedAt,
maxDocumentCount: 10_000);
}Pruning fires transparently on insert (configurable threshold) or on a scheduled basis — no background threads are created.
When a document is deleted, BLite can now zero the slot on disk before marking it as free. This satisfies GDPR Art. 17 ("Right to Erasure") requirements where physical deletion — not just logical unlinking — is required.
// Enable secure erase per-collection in OnModelCreating
modelBuilder.Entity<Customer>()
.HasSecureErase(true);
// Run VACUUM to reclaim space and compact the file
await engine.VacuumAsync();
// —or typed—
await db.VacuumAsync();BLite Studio (the desktop GUI for database exploration) ships with v5 with three new capabilities:
The connection dialog now exposes an Encryption section. Enter the passphrase before opening and Studio handles the rest transparently:
- The encryption status badge (
AES-256-GCM enabled/Not encrypted) is shown in the title bar. - The database layout (
Single-file/Multi-file) is also displayed for multi-file databases.
A dedicated GDPR sidebar item exposes the Art. 30 inspection report directly inside Studio:
- Encryption status — whether AES-256-GCM is active.
- Audit status — whether a sink is registered.
- Layout — single-file or multi-file.
- Collections table — per-collection document count, storage size, personal-data fields, and retention policy.
The GDPR panel also includes a built-in Subject Export form:
- Enter the field name (e.g.
email) and field value (e.g.[email protected]). - Choose the output format: JSON, CSV, or BSON.
- Click Export and choose a destination file.
Studio calls BLiteEngine.ExportSubjectDataAsync internally and shows the number of exported documents on completion.
v5.0.0-preview.0 is fully backwards-compatible with v4.4.2 databases. No file-format changes are required and no API breaking changes have been introduced for existing code.
| Scenario | Action |
|---|---|
| Open existing unencrypted database | No change required — NullCryptoProvider is the default |
| Enable encryption on an existing database | Use BLiteEngine.MigrateToEncryptedAsync(...) |
| Add audit trail | Call ConfigureAudit(...) after construction |
| Add GDPR annotations | Annotate properties with [PersonalData] and rebuild |
| Enable Strict mode | Add HasGdprMode(GdprMode.Strict) + configure encryption + audit |
| Enable multi-process access | Set PageFileConfig.AllowMultiProcessAccess = true on all processes |
| Type | Description |
|---|---|
ICryptoProvider |
Interface for pluggable encryption providers |
AesGcmCryptoProvider |
Built-in AES-256-GCM implementation |
NullCryptoProvider |
No-op provider (default — zero overhead) |
EncryptionCoordinator |
Per-file HKDF-SHA256 subkey derivation for multi-file mode |
CryptoOptions |
Configuration: passphrase mode or master-key mode |
KdfAlgorithm |
Pbkdf2Sha256 (default, 600 000 iterations) |
| Type | Description |
|---|---|
IBLiteAuditSink |
Interface with OnInsert, OnQuery, OnCommit, OnSlowOperation |
BLiteAuditOptions |
Configuration: sink, metrics, slow threshold, DiagnosticSource |
BLiteMetrics |
In-memory thread-safe counters (TotalInserts, TotalQueries, CacheHitRate, …) |
BLiteDiagnostics |
ActivitySource for OpenTelemetry / Application Insights |
IAuditContextProvider |
Plug-in for injecting caller identity into audit events |
AmbientAuditContext |
Default IAuditContextProvider backed by a static ambient context |
InsertAuditEvent |
Per-insert event record |
QueryAuditEvent |
Per-query event record (CollectionName, Strategy, ResultCount, Elapsed) |
CommitAuditEvent |
Per-commit event record (TransactionId, PagesWritten, WalSizeBytes, Elapsed) |
SlowOperationEvent |
Emitted when an operation exceeds SlowOperationThreshold |
| Type | Description |
|---|---|
PersonalDataAttribute |
Marks a property as personal data; Sensitivity and IsTimestamp |
DataSensitivity |
Personal (Art. 4(1)), Sensitive (financial/health), Special (Art. 9) |
PersonalDataField |
Record struct emitted by source generator on each mapper |
SubjectQuery |
Describes an Art. 15/20 lookup (FieldName, FieldValue, Collections, Format) |
SubjectDataReport |
Result of ExportSubjectDataAsync; supports JSON/CSV/BSON export |
SubjectExportFormat |
Json, Csv, Bson |
DatabaseInspectionReport |
Art. 30 snapshot: encryption, audit, layout, collections |
CollectionInfo |
Per-collection document count, size, personal fields, retention |
IndexInfo |
Per-index name, fields, uniqueness, encryption flag |
RetentionPolicyInfo |
Read-only projection of the active RetentionPolicy |
GdprMode |
None (default), Strict (Art. 25) |
GdprModeAttribute |
Declarative per-entity [GdprMode(GdprMode.Strict)] |
GdprEngineExtensions |
ExportSubjectDataAsync, InspectDatabase on BLiteEngine |
GdprDocumentDbContextExtensions |
Same surface on DocumentDbContext |
| Property | Default | Description |
|---|---|---|
RevealPersonalData |
false |
When false, [PersonalData] fields are masked before dispatch |
PersonalDataMaskValue |
"***" |
Replacement value; set to BsonValue.Null to drop the field |
ExcludeFields |
[] |
Additional fields to remove from the payload |
IncludeOnlyFields |
null |
Allowlist: when set, only these fields are delivered (wins over all masking rules) |
| Member | Description |
|---|---|
ConfigureAudit(BLiteAuditOptions) |
Attaches the audit trail subsystem |
AuditMetrics |
BLiteMetrics? — null until EnableMetrics = true |
new BLiteEngine(path, CryptoOptions, ...) |
Encryption-aware constructor |
new MyDbContext(path, CryptoOptions, ...) |
Encryption-aware constructor (typed) |
VacuumAsync(ct) |
Compact the database and reclaim free space |
RotateEncryptionKeyAsync(IKeyProvider, ...) |
Online key rotation |
BLiteEngine.MigrateToEncryptedAsync(...) |
Offline migration: plaintext → encrypted |
BLiteEngine.MigrateToPlaintextAsync(...) |
Offline migration: encrypted → plaintext |
RotateEncryptionKeyAsyncis implemented but not yet stress-tested under concurrent write load. Use it during a maintenance window.- Multi-process WAL (
AllowMultiProcessAccess) is not yet supported on WASM/Browser targets (tracked in WASM_SUPPORT.md). - Argon2id KDF is reserved for a future release. PBKDF2-SHA256 (600 000 iterations) and HKDF-SHA256 are the only supported KDFs in this preview.
- Source-generator GDPR metadata emission (
PersonalDataFields) requires .NET SDK 9+ (Roslyn 4.x). Fallback reflection path is fully functional on all supported runtimes.
📢 We need your testing!
The GA release is planned for 30 May 2026. Before then, we ask the community to:
- Install
5.0.0-preview.0in a non-production environment. - Run your existing test suites — the migration is backwards-compatible.
- Optionally enable encryption, the audit sink, or GDPR annotations and report your experience.
- Open a GitHub Issue for any bug, performance regression, or unexpected API behaviour you find.
Please include in your issue report:
- BLite version (
5.0.0-preview.0) - .NET runtime and target framework
- Minimal reproducible code or test case
- Expected vs. actual behaviour
Thank you for helping make v5 the best BLite release yet. 🙏
- Main README — full feature documentation for the current stable release (v4.4.2)
- CHANGELOG.md — detailed commit-level change log
- RFC.md — full architectural specification (storage engine, WAL, ACID, query processing)
- roadmap/v5/ENCRYPTION_PLAN.md — encryption architecture details
- roadmap/v5/AUDIT_IMPLEMENTATION.md — audit trail design
- roadmap/v5/GDPR_PLAN.md — GDPR compliance architecture
- docs/DPIA_CHECKLIST.md — DPIA checklist for DPO use
- Official Documentation → blitedb.com/docs