The AI-native database. One database for AI agents.
Documents, typed tables, graphs, vectors/search, blobs, queues, events and time series in one replicated .NET cluster.
Everything links to everything else, and agents reach all of it through SQL, a built-in MCP server and a typed .NET SDK.
Website · Quick start · Capabilities · Why .NET, Orleans and ZoneTree · Docs · Status
Agents shouldn't need a dozen databases. Yet an agent that needs memory and the ability to act can easily end up wired to seven: Postgres for records, a vector database for embeddings, Redis or RabbitMQ for tasks, S3 for files, Kafka for events, a graph database for relationships and a time-series database for metrics. Every one of them brings its own client, credentials, permission model, backups and failure modes. The agent, and your team, spend their time keeping seven copies of the truth in sync.
flowchart TB
subgraph Today["Today: one agent, seven systems"]
direction TB
A1["AI agent"] --> PG[("Postgres<br/>records")]
A1 --> VDB[("Vector DB<br/>embeddings")]
A1 --> MQ[("Redis / RabbitMQ<br/>tasks")]
A1 --> S3[("S3<br/>files")]
A1 --> KF[("Kafka<br/>events")]
A1 --> GDB[("Graph DB<br/>relationships")]
A1 --> TSDB[("Time-series DB<br/>metrics")]
end
subgraph One["With KeyLoad: one database"]
direction TB
A2["AI agent"] --> KL[("KeyLoad<br/>every model, one reference,<br/>one permission check")]
end
Today ~~~ One
KeyLoad is an AI-native database: it's designed around how agents work, not bolted onto a database built for something else.
- One reference for everything. All models share canonical entity references. A queue message can point to a document, that document can be a node in a knowledge graph, and its files, embeddings and history stay attached to it.
- One language. SQL is the familiar shared language for querying and combining the models. Agents get the same operations as MCP tools, and applications get them through a typed .NET SDK.
- One permission model. API keys and access policies live in the database itself. Every call is checked, down to rows and fields, and neither clients nor prompts can grant themselves extra roles.
- One atomic write. A document, its event, a graph edge and the follow-up task in the same partition commit together, or not at all.
- Limits on every operation. Full scans must be requested explicitly, and every request has caps on work and memory, so a runaway request is rejected at its limit instead of running unbounded.
- Replicated from day one. The default server is a three-node cluster that keeps three copies of your data (RF3), on Orleans with ZoneTree storage on each node.
Agents don't think in tables, queues or buckets. Picture a support agent handling a refund: it takes the ticket off a queue, loads the customer and the order, finds similar past cases with vector search, links the case into a knowledge graph, attaches the receipt and schedules a follow-up. Spread across separate systems, that one step turns into a distributed saga held together by glue code and stale copies, and the agent needs a key to every one of them.
When everything lives in one database, that step should become one authorized operation. Today its writes within one partition already commit atomically, while leased receives, blob uploads and cross-partition work are still separate steps. Our goal is that building context becomes one query instead of a join written in prompt code, and that access is one policy you can read. That's why we're building KeyLoad: so that nobody has to stand up and run a stack of databases just to let agents get work done.
KeyLoad is a development preview today: the status section lists what works and what's still in progress. If you'd rather run one database than seven, star the repo to follow along, try the quick start and open an issue describing the agent workload you want it to handle.
| You want to… | KeyLoad gives you |
|---|---|
| Give an agent long-term memory and retrieval (RAG) | Documents, files and embeddings in one place, with vector, full-text and hybrid search |
| Build a knowledge graph | Relationships between any entities, including typed rows, and graph traversal |
| Run reliable agent workflows | Queues with scheduling, leases, retries and dead letters, stored next to the data they change |
| Make event-driven apps | Ordered event history, durable subscriptions, change feeds and projections |
| Analyze time series | Timestamped samples, range reads, aggregates, time windows and retention |
| Store files for agents | Chunked uploads and partial reads of large blobs |
| Model | In one atomic commit | Read and query | SQL | MCP tools |
|---|---|---|---|---|
| 📄 Documents | ✅ Put, patch, delete with revision checks | Get by key, indexed queries, live queries | SELECT |
keyload_documents_*, keyload_query_* |
| 🧾 Typed tables | ✅ Rows with a schema, primary keys and unique constraints | Indexed queries | SELECT |
keyload_documents_commit, keyload_sql_execute |
| 🕸️ Graphs | ✅ Add and remove edges | Graph traversal | CALL |
keyload_graph_traverse |
| 🧭 Vectors and search | ✅ Store a vector with its document | Exact vector, full-text and hybrid search | CALL |
keyload_search_execute |
| 📬 Queues and topics | ✅ Enqueue, publish | Receive with leases, ACK, retries, dead letters, peek | SELECT … FROM QUEUE_MESSAGES(…) |
keyload_messages_*, keyload_subscriptions_* for topics |
| 📜 Events | ✅ Append with an expected revision | Read, replay, durable subscriptions | SELECT … FROM EVENTS(…) |
keyload_streams_*, keyload_subscriptions_* |
| 📈 Time series | ✅ Append samples | Ranges, latest value, aggregates, windows, retention | CALL |
keyload_series_* |
| 📦 Files and blobs | Separate upload and publish steps | Chunked uploads, byte-range reads | CALL |
keyload_blobs_* |
| 🔁 Change feeds | Document changes recorded in the same commit | Resumable change feed, projections | CALL |
keyload_changes_read, keyload_projections_* |
SELECT reads data. CALL runs any operation from the same catalog the MCP server exposes, so every model is reachable from SQL.
These aren't separate silos. Collections, tables and queues live in the same database and reference each other, so one request can work across models:
- Queue to knowledge graph (
QueueToGraph). Reads ready messages from a queue, resolves their linked entities and writes the new knowledge-graph relationships. - Graph to queued actions (
GraphToQueueMutation). Follows graph relationships and can enqueue actions for each entity it finds.
flowchart LR
Q[["Queue: inbox"]] -->|"QueueToGraph"| E["Linked entities<br/>documents and rows"]
E --> G(("Knowledge graph"))
G -->|"GraphToQueueMutation"| A[["Queue: actions"]]
What the first version does. The current composition API runs through .NET CommitAsync, SQL CALL keyload_documents_commit(@arguments) or the official MCP server:
- All data in one request must live in the same atomic partition and transaction domain, which is the
PartitionRefyou pass in. The whole batch succeeds or rolls back together. - Reading a queue into the graph does not lease or ACK the messages, so they stay in the queue for their regular consumers.
- Blobs remain part of the same database, but use their separate upload and publication operations.
- KeyLoad is a development preview. Full declarative SQL, the native SQL-client protocol and cross-partition composition are still in development, and production guarantees remain under qualification.
The composition guide and its transaction contract cover the details.
flowchart LR
subgraph Callers
SDK[".NET SDK"]
MCP["AI agent over MCP"]
SQL["SQL over HTTP"]
CLI["CLI and admin console"]
end
subgraph Cluster["KeyLoad cluster (3 nodes)"]
REQ["Request grain<br/>one per request"]
PART["Partition grains"]
N1[("Node 1<br/>ZoneTree")]
N2[("Node 2<br/>ZoneTree")]
N3[("Node 3<br/>ZoneTree")]
end
SDK --> REQ
MCP --> REQ
SQL --> REQ
CLI --> REQ
REQ --> PART
PART --> N1
PART --> N2
PART --> N3
- A caller sends an operation to any node, through the SDK, MCP, SQL or plain HTTP.
- KeyLoad gives that request its own Orleans grain, which calls only the database grains it needs.
- Database grains route the work to the storage owner on each node. Storage stays on its node, even when Orleans moves grains around the cluster.
- A write is acknowledged only after a majority of the three replicas has persisted it. If a node fails, the other two keep the acknowledged data.
Here's one agent tool call, end to end:
sequenceDiagram
participant Agent as AI agent
participant Node as Any KeyLoad node
participant Req as Request grain
participant Part as Partition grain
participant Reps as Three ZoneTree replicas
Agent->>Node: MCP tool call with an API key
Note over Node,Req: Key and permissions are checked against the database
Node->>Req: A new grain for this request
Req->>Part: One atomic command
Part->>Reps: Replicate
Reps-->>Part: A majority persisted it
Part-->>Agent: One result, all or nothing
You'll find the full picture in the architecture map.
We picked a stack where the database, its cluster and its storage all run in one runtime, inside one process per node, with no glue in between.
.NET 10: a fast, memory-efficient runtime.
Span<T>, pooled buffers and hardware intrinsics let hot paths avoid allocations and use SIMD. Vectorized paths are a priority workstream, and we'll publish measurements before claiming any speedup.- One language end to end. The server, SDK, CLI, analyzers and tests are all C#.
- Aspire orchestrates the whole three-node cluster, locally and in CI, from one AppHost.
- Open source, cross-platform and at home in Linux containers.
Orleans: a cluster runtime that's proven in production.
- Virtual actors (grains) that the cluster places, activates and moves for you. Orleans came out of Microsoft Research and has powered Halo's cloud services, among others.
- KeyLoad gives each request its own grain, so isolation, cancellation and backpressure work per request.
- Membership and failure detection are built in. KeyLoad also turns on Orleans' experimental distributed grain directory and activation repartitioning, which we're still qualifying.
- Fast generated binary serialization between nodes.
ZoneTree: storage that lives inside the node.
- An embedded, persistent, ordered key-value engine written in C#. Data lives in each node's own process, with no extra network hop and no native interop.
- A write-ahead log on each node, and ordered keys for range scans and indexes.
- One storage engine under every model: documents, rows, graphs, vectors, queues, events, time series and blobs.
- ZoneTree.FullTextSearch adds the full-text index on the same foundation.
Together: Orleans moves the routing, and ZoneTree keeps the data in place. Storage never travels with a grain, so the cluster can rebalance work without copying files between nodes.
You need: the .NET SDK version pinned in global.json, and Docker.
1. Build the solution
dotnet restore KeyLoad.slnxdotnet build KeyLoad.slnx --no-restore --configuration Release2. Build the server image
The cluster runs the server image pinned by its digest, so build it from source and push it to a local registry:
docker run -d --name keyload-registry -p 5050:5000 registry:3docker build -t localhost:5050/keyload/server:dev . && docker push localhost:5050/keyload/server:devexport KeyLoad__ContainerImages__Server="localhost:5050/keyload/server:dev@$(docker inspect --format '{{index .RepoDigests 0}}' localhost:5050/keyload/server:dev | cut -d@ -f2)"3. Start the three-node cluster
dotnet run --project src/KeyLoad.AppHost --configuration Release --no-buildAspire starts three nodes at http://localhost:5101, :5102 and :5103. Node data and your local development credentials are stored in data/cluster/, which git ignores. Keep that folder between restarts.
4. Look around
- Open http://localhost:5101/admin for the admin console.
- Or check the cluster from the CLI:
dotnet run --project src/KeyLoad.Cli --configuration Release --no-build -- status http://localhost:5101 data/cluster/local-profile.jsonYour local admin API key is the AdminKey value in data/cluster/local-profile.json. Export it as KEYLOAD_API_KEY for the examples below.
The client SDK gives you typed database operations. This creates a collection, writes an order and reads it back:
using KeyLoad;
using KeyLoad.Client;
var apiKey = Environment.GetEnvironmentVariable("KEYLOAD_API_KEY")
?? throw new InvalidOperationException("Set KEYLOAD_API_KEY.");
using var http = new HttpClient { BaseAddress = new Uri("http://localhost:5101") };
var client = new KeyLoadClient(http, apiKey);
// tenant, database, transaction domain, partition key
var partition = new PartitionRef("acme", "shop", "orders", "customer-42");
var collection = await client.ConfigureResourceAsync(Guid.NewGuid(), new("acme", "shop",
new ResourceDefinition("orders", ResourceKind.Collection, "orders")));
collection.ThrowIfFail();
var commandId = Guid.NewGuid(); // Reuse this ID if you retry the same write.
var result = await client.CommitAsync(new CommandRequest(commandId, partition,
[
new PutDocument("orders", "order-1", "{\"status\":\"new\"}", ExpectedRevision: 0)
]));
result.ThrowIfFail();
var order = await client.GetAsync(new EntityRef(partition, "orders", "order-1"));
order.ThrowIfFail();Use SQL to read data and to call any database operation:
using System.Text.Json;
var rows = await client.ExecuteSqlAsync(new SqlOperationRequest(partition,
"SELECT * FROM orders WHERE status = @status LIMIT 20",
new() { ["status"] = JsonSerializer.SerializeToElement("new") },
AllowFullScan: true));
rows.ThrowIfFail();
Console.WriteLine(rows.Value);Here's what works today:
SELECT * FROM orders WHERE number BETWEEN 1 AND 9 ORDER BY id -- filter and sort documents
SELECT * FROM QUEUE_MESSAGES('jobs') -- peek at a queue without consuming it
SELECT e.eventType FROM EVENTS('events', 'stream-a', 3) AS e -- read event history
EXPLAIN SELECT * FROM orders -- see the query plan
CALL keyload_documents_commit(@arguments) -- run any database operationModel views (QUEUE_MESSAGES, EVENTS) and unindexed filters require AllowFullScan: true, and model views don't support cursors. Full SQL, including joins, is still in progress. The query guide lists the supported syntax, and the compatibility inventory tracks the rest.
Every node has a built-in MCP server at /mcp. Database operations are available as tools, such as keyload_sql_execute, keyload_documents_commit, keyload_search_execute, keyload_graph_traverse, keyload_messages_receive and keyload_blobs_read_range. Add it to any MCP client. In Claude Code's .mcp.json, for example:
{
"mcpServers": {
"keyload": {
"type": "http",
"url": "http://localhost:5101/mcp",
"headers": { "Authorization": "Bearer ${KEYLOAD_API_KEY}" }
}
}
}The agent sees exactly what its API key allows. Permissions are stored in the database, so a prompt can't escalate them. The MCP and API guide describes the full operation catalog, along with the plain HTTP routes under /v1/.
KeyLoad is a development preview. Try it, build with it and tell us what breaks, but don't trust it with production data yet.
| Ready to try (in source, covered by tests) | Still in progress |
|---|---|
| Documents, typed rows, graphs, queues, events, time series, blobs and search behind one permission model | Full SQL (joins, foreign keys) and a native SQL client protocol |
SQL SELECT, model views and CALL, plus the .NET SDK, MCP server and HTTP API |
Combining data across partitions in one request |
| Queue ↔ graph composition within one partition | Approximate vector search (ANN); vector search is exact for now |
| Three-node Orleans cluster, crash recovery, local backup and restore | Production, endurance and power-loss qualification |
| Admin console and CLI | Storage upgrades from older versions, which still have an open cold-cluster failure |
| The full performance comparison with other databases (current runs are on the website) |
Full-text search comes from ZoneTree.FullTextSearch. KeyLoad then ranks the results it finds and checks permissions on each one.
For detailed status, see the implementation tracker and the qualification records. We publish performance numbers only from real GitHub Actions runs, on the website.
A database designed around how AI agents work: every kind of agent data in one place, linked by shared references, reachable through MCP and SQL, with permissions an agent can't talk its way around. That's what KeyLoad is built to be.
It includes vector search, but vectors don't sit in a separate store. They live next to the documents, graph, queues and files they belong to, and one request can use all of them. Vector search is exact today, and approximate (ANN) search is in progress.
That's the main use case. Documents, embeddings, files, a knowledge graph and event history live together, so long-term memory, retrieval (RAG) and the task queue share one store and one permission model.
For agent workloads, that's the goal: one database instead of a stack of them. KeyLoad is still a development preview, so check the project status before you move anything.
Every node runs a built-in MCP server at /mcp. Point any MCP client at it with an API key, and the agent can use exactly the operations that key allows.
Orleans already handles cluster membership, failure detection and request routing for .NET. KeyLoad gives every request its own grain, while ZoneTree keeps each node's data on that node. See Why .NET, Orleans and ZoneTree.
Yes, through MCP or the HTTP API under /v1/. The typed SDK is .NET for now.
Yes. KeyLoad is MIT-licensed and developed in the open on GitHub.
Not yet. Endurance, fault and power-loss qualification are still in progress.
| Path | What's inside |
|---|---|
src/KeyLoad.Server |
The database server: HTTP API, MCP server and admin console |
src/KeyLoad.Client |
The .NET SDK |
src/KeyLoad.Cli |
Command-line tool for operators and workers |
src/KeyLoad.AppHost |
Aspire host for the local cluster and every test suite |
src/KeyLoad.Abstractions |
Shared public contracts |
src/KeyLoad.Core, Query, Orleans, Replication, Security, Storage.* |
Database engine, SQL, cluster, replication, permissions and storage |
tests/ |
Unit, crash-recovery, three-node and website tests (TUnit) |
benchmarks/ |
Comparisons with other databases, plus microbenchmarks |
site/ |
Source for keyload.cloud |
docs/ |
Architecture, feature specifications and design decisions |
All test suites run through the Aspire AppHost. After building, pick a suite: unit, unit-scalar, recovery, rf3, analyzers, comparison or site.
dotnet run --project src/KeyLoad.AppHost --no-build --no-restore --configuration Release -- --KeyLoadTests:Suite=unitdotnet format KeyLoad.slnx --verify-no-changes --no-restoreThe rf3 suite starts a real three-node cluster in Docker and tests it through the .NET SDK and the official MCP client. Before your first change, read the architecture map and the feature spec in docs/Features/. If you work with AI coding agents, AGENTS.md holds the repository rules.
KeyLoad is developed by Managed Code and stands on the shoulders of these projects. Thank you to their authors and contributors.
| Project | How KeyLoad uses it |
|---|---|
| .NET and ASP.NET Core | Server, client SDK and HTTP hosting |
| Orleans | Distributed request execution, cluster routing and binary serialization |
| ZoneTree | Persistent ordered storage for every database model |
| ZoneTree.FullTextSearch | Full-text search index |
| MCP C# SDK | Built-in MCP server and real MCP clients in tests |
| ManagedCode.Communication | Typed operation results and ASP.NET Core/Orleans integration |
| ManagedCode.Storage | File-system storage and backup transfer support |
| ManagedCode.TimeSeries | Time-series aggregation |
| ManagedCode.Orleans.Graph | Grain call relationship policies |
| Cartograph | Segmented backup archives and catalogs |
| OpenTelemetry .NET | Logs, metrics and traces |
Development and presentation also use Aspire for Docker orchestration, TUnit for tests, Roslyn for the repository's own code analyzers, BenchmarkDotNet for microbenchmarks, and Three.js for the website's cluster illustration. The benchmark suite compares KeyLoad with the free community editions of PostgreSQL with pgvector, TimescaleDB, MongoDB, Redis, RabbitMQ, KurrentDB, Neo4j, Qdrant and OpenSearch, connecting through the official clients Npgsql, MongoDB .NET Driver, StackExchange.Redis, RabbitMQ .NET Client and KurrentDB .NET Client.
KeyLoad is licensed under MIT.
Developed by Managed Code