BLite Server is a high-performance, self-hosted database server built on top of the BLite embedded engine.
It exposes BLite's full capabilities — schema-less BSON documents and typed strongly-typed collections — over a gRPC / Protocol Buffers transport, hosted on ASP.NET Core / Kestrel.
Status: Active development — core server + auth + observability + transactions complete. Client SDK in progress.
Client (BLite.Client SDK)
├── IBLiteQueryable<T> ──── ExpressionToDescriptorVisitor ──┐
└── RemoteDynamicClient ─── BsonDocument ↔ bytes ────────────┤
│
gRPC (HTTP/2, TLS) — port 2626 │
API-Key header │
▼
BLite.Server (ASP.NET Core + Kestrel)
├── ApiKeyMiddleware → resolve BLiteUser + namespace
├── TelemetryInterceptor → OTel trace + metrics for every RPC
├── DynamicService (schema-less CRUD + streaming Query + Bulk)
├── DocumentService (typed Query / Insert / Update / Delete / BulkInsert)
├── AdminService (user management, permissions)
├── TransactionService (Begin / Commit / Rollback)
└── QueryDescriptorExecutor
└── BLiteEngine / DynamicCollection (BTree, WAL, HNSW, RTree)
| Project | Description |
|---|---|
BLite.Proto |
Shared .proto contracts + QueryDescriptor (MessagePack) |
BLite.Server |
ASP.NET Core gRPC host, service implementations, auth, observability, transactions |
BLite.Client (in progress) |
.NET client SDK with RemoteQueryProvider and IBLiteQueryable<T> |
| Feature | REST/JSON | gRPC/Protobuf |
|---|---|---|
| Transport | HTTP/1.1 | HTTP/2 (multiplexed frames) |
| Payload | JSON (double serialization) | Raw BSON bytes |
| Result streaming | SSE/WebSocket (workaround) | Native server-streaming |
| Expression trees | Custom JSON DSL | QueryDescriptor in bytes |
| Cross-language | ✅ | ✅ (proto3 generates all languages) |
Expression trees cannot be sent over the wire directly.
BLite Server defines a QueryDescriptor — a serializable DTO (MessagePack) that maps 1:1 to BLite's internal QueryModel:
public sealed class QueryDescriptor
{
public string Collection { get; set; }
public FilterNode? Where { get; set; } // predicate tree
public ProjectionSpec? Select { get; set; } // scalar field list
public List<SortSpec> OrderBy { get; set; }
public int? Take { get; set; }
public int? Skip { get; set; }
}Filter nodes support BinaryFilter (field op value) and LogicalFilter (AND/OR/NOT), covering the full range of BLite LINQ predicates.
The server rebuilds a Func<BsonSpanReader, bool> predicate from the descriptor and delegates directly to DynamicCollection.Scan / BsonProjectionCompiler — T is never instantiated on the server for typed queries.
service DynamicService {
rpc Insert (InsertRequest) returns (InsertResponse);
rpc FindById (FindByIdRequest) returns (DocumentResponse);
rpc Update (UpdateRequest) returns (MutationResponse);
rpc Delete (DeleteRequest) returns (MutationResponse);
rpc Query (QueryRequest) returns (stream DocumentResponse);
rpc InsertBulk (BulkInsertRequest) returns (BulkInsertResponse);
rpc UpdateBulk (BulkUpdateRequest) returns (BulkMutationResponse);
rpc DeleteBulk (BulkDeleteRequest) returns (BulkMutationResponse);
rpc ListCollections (Empty) returns (CollectionListResponse);
rpc DropCollection (DropCollectionRequest) returns (MutationResponse);
}
service DocumentService {
rpc Query (QueryRequest) returns (stream TypedDocumentResponse);
rpc Insert (TypedInsertRequest) returns (InsertResponse);
rpc Update (TypedUpdateRequest) returns (MutationResponse);
rpc Delete (DeleteRequest) returns (MutationResponse);
rpc InsertBulk (TypedBulkInsertRequest) returns (BulkInsertResponse);
}
service AdminService {
rpc CreateUser (CreateUserRequest) returns (MutationResponse);
rpc DeleteUser (DeleteUserRequest) returns (MutationResponse);
rpc ListUsers (Empty) returns (UserListResponse);
rpc UpdatePerms (UpdatePermsRequest) returns (MutationResponse);
}
service TransactionService {
rpc Begin (BeginTransactionRequest) returns (BeginTransactionResponse);
rpc Commit (TransactionRequest) returns (MutationResponse);
rpc Rollback (TransactionRequest) returns (MutationResponse);
}All write RPCs (Insert / Update / Delete / Bulk variants) accept an optional transaction_id field.
When set, the operation participates in the named server-side transaction instead of auto-committing.
Every request must carry an x-api-key header.
The server resolves the caller to a BLiteUser with a scoped permission set:
| Permission | Operations |
|---|---|
Query |
read-only (FindById, Query) |
Insert / Update / Delete |
write |
Drop |
DropCollection |
Admin |
full access + user management |
Collections are automatically namespaced by user — user alice accessing orders operates on the isolated logical collection alice::orders.
The root user bypasses namespacing and has full access.
BLite Server supports explicit, token-scoped transactions over gRPC:
1. client calls TransactionService.Begin → receives transaction_id (UUID)
2. client passes transaction_id in any write RPC → writes are not auto-committed
3. client calls Commit or Rollback to finalise
At most one transaction can be active at a time (BLiteEngine constraint).
Sessions that idle longer than Transactions:TimeoutSeconds (default 60 s) are rolled back automatically by a background cleanup service.
BLite Server ships with built-in OpenTelemetry support:
- Traces — every gRPC RPC generates a span via
TelemetryInterceptor - Metrics —
blite.server.rpc.total,blite.server.rpc.duration,blite.server.documents.streamed,blite.server.active_transactions - Exporters — Console (dev) + OTLP/gRPC (Jaeger, Grafana, Datadog, …)
Configure in appsettings.json:
"Telemetry": {
"Enabled": true,
"ServiceName": "blite-server",
"Console": null,
"Otlp": { "Endpoint": "http://localhost:4317" }
}| Step | Scope | Status |
|---|---|---|
| 1 – BLite.Proto | .proto + QueryDescriptor + MessagePack serialization |
✅ Complete |
| 2 – BLite.Server | ASP.NET Core host, DynamicService + DocumentService end-to-end |
✅ Complete |
| 3 – BLite.Client | RemoteQueryProvider, ExpressionToDescriptorVisitor, IBLiteQueryable<T> |
🔄 In progress |
| 4 – Typed path | TypeManifest schema registration, typed query push-down |
🔜 Planned |
| 5 – Auth & multi-tenancy | API Key middleware, AdminService, tenant namespacing |
✅ Complete |
| 6 – Observability | OpenTelemetry traces + metrics, Console + OTLP exporters | ✅ Complete |
| 7 – Transactions | TransactionService, token-scoped sessions, TTL cleanup |
✅ Complete |
# Run the server (binds on all interfaces, port 2626)
dotnet run --project src/BLite.Server
# Or with a custom database path
dotnet run --project src/BLite.Server -- --db /data/mydb.dbThe server listens on https://*:2626 (HTTP/2 only).
// Client usage (BLite.Client SDK — coming soon)
var client = new BLiteRemoteClient("https://myhost:2626", apiKey: "your-key");
var users = client.GetCollection<User>("users");
var result = await users.AsQueryable()
.Where(u => u.Age > 25)
.Select(u => new { u.Name, u.Age })
.ToListAsync();
// Explicit transaction
var txn = await client.BeginTransactionAsync();
await users.InsertAsync(new User { Name = "Alice" }, txn);
await orders.InsertAsync(new Order { UserId = alice.Id }, txn);
await txn.CommitAsync();Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
See LICENSE for the full text.
The AGPL-3.0 requires that any modified version of this software that is made available over a network must also make its source code available. If you need a commercial license without this restriction, please contact the authors.
- BLite — the embedded engine (MIT)
- BLite documentation