The AI-native database. One database for AI agents.
Your agent's context, relationships and next action. Together.
SQL · Native MCP · .NET SDK
Website · Quick start · Capabilities · Docs · Status
At Managed Code, we've spent years building agents. Different projects. Same story: the agent always needs more than the database we started with.
You start with vectors. Great, the agent can find similar things. But it also needs real records. Add SQL. Then hybrid search. Still missing relationships? Add a graph. Now it needs to remember what happened: events. Read a PDF? Files. Do something later? Queues. Track what changes over time? Time series.
Every requirement makes sense. Put them together, and suddenly you're running a stack of databases just to build one agent. Different clients, permissions, backups and sync jobs. A lot of plumbing before the interesting work even starts.
Agents shouldn't need a dozen databases.
We came to a simple conclusion: we need an AI-native database that keeps the agent's context, relationships and work together. With MCP built in, so the agent can actually use it.
Meet KeyLoad. One database for AI agents.
Documents, typed tables, graphs, vectors/search, blobs, queues, events and time series belong to the same database. They connect through canonical entity references, so a row can be part of a graph, an event can point to a document, and a queued task can carry the context it needs.
SQL is the familiar shared language. The built-in MCP server and typed .NET SDK reach the same authorized operations. The full product is still being built; the status below shows what's accepted and what remains.
| The idea | Why it matters |
|---|---|
| Keep context together | Search, relationships, files and history connect to the same entities |
| Keep work next to the data | A document, event, graph edge and queued task in one partition commit together |
| Give agents a native way in | MCP discovery finds the relevant tools; SQL and the SDK use the same operations |
| Keep permissions in the database | Persisted keys and row/field policies apply across every caller |
A support agent doesn't ask for a vector database. It needs the ticket, the customer, similar cases, the attached file and a way to schedule the next action. Those things belong together.
That's what we're building: one place for context, one place for the work that follows. Same-partition writes already commit atomically. Leased receives, blob uploads and cross-partition work are separate steps today. Try the preview or bring us your agent workload.
| 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 |
Development preview. Production qualification is still open.
| Original tasks | Accepted | In progress |
|---|---|---|
| 104 | 17 | 87 |
flowchart LR
Build["Implement<br/>in progress"] --> Check["Qualify<br/>in progress"] --> Release["Release<br/>gated"]
classDef active fill:#f7eef3,stroke:#97718d,color:#171717
classDef gated fill:#f2f2f2,stroke:#777,color:#171717
class Build,Check active
class Release gated
Accepted = original task criteria passed for a recorded source. In source = implemented, with qualification open. Later changes require fresh checks.
| Workstream | What it does and why | State | Remaining work |
|---|---|---|---|
| Records & indexes | CRUD and revision checks keep records consistent | ✓ Accepted | Recovery and fault gates |
| Backup & restore | Recover a saved local database | ✓ Local scope | RF3 cluster restore drills |
| Atomic batches | Commit related writes together; retry without duplicates | ✓ Accepted | Cross-partition composition |
| Graphs | Store relationships and traverse agent knowledge | ✓ Accepted | Cross-partition graphs |
| Time series | Samples, ranges and aggregates track history | ✓ Accepted | Chunk and scale checks |
| Search | Exact vectors, hybrid ranking and Explain retrieve context | ✓ Accepted | ANN, rebuilds and freshness |
| SQL & tables | SELECT, CALL and bounded INNER JOIN share one language |
◐ In source | Full SQL, foreign keys and client protocol |
| Queues & events | Leases, history and queue ↔ graph flows drive agent work | ◐ In source | Failure, security and recovery checks |
| SDK & MCP | SDK, tool discovery, CLI and admin connect callers | ◐ In source | Gateway and official MCP RF3 checks |
| Authorization | Persisted keys and row/field policies protect context | ◐ In source | Adversarial and revocation checks |
| RF3 & Orleans | Three replicas, follower reads and partition movement | ◐ In source | Fault, restart and runtime checks |
| RAM & SIMD | Bounded caches and CPU vectorization target lower memory/latency | ◐ In progress | Comparable Linux measurements |
| Production | Qualified database and operator guidance | ○ Gated | Full unit/scalar, recovery, RF3, coverage, performance, endurance and power-loss gates |
Product functional coverage is unmeasured. Public performance figures require original GitHub measurements; process-kill tests do not prove power-loss durability.
Task tracker · SQL conformance · Coverage · Benchmark methodology · Published benchmarks
| Model | Operations in source | Atomic write scope |
|---|---|---|
| Documents | CRUD, revision checks, indexed and live queries | Put, patch, delete |
| Typed tables | Schemas, primary keys, unique constraints and indexed reads | Row mutations |
| Graphs | Edges and bounded traversal | Add/remove edges |
| Vectors & search | Exact vector, full-text and hybrid retrieval | Store vectors with documents |
| Queues & topics | Scheduling, leases, ACK, retries, dead letters and peek | Enqueue/publish |
| Events | Append, replay and durable subscriptions | Append with expected revision |
| Time series | Ranges, aggregates, windows and retention | Append samples |
| Files & blobs | Chunked uploads and byte-range reads | Separate upload/publication |
| Change feeds | Resumable feeds and projections | Record document changes |
Atomic batches share one partition. SQL SELECT reads documents, typed rows and model views; CALL reaches the shared operation catalog. MCP discovers those operations on demand. SQL syntax · API and tool catalog.
| Flow | What one request does |
|---|---|
Queue to knowledge graph (QueueToGraph) |
Reads ready messages, resolves linked entities and writes knowledge-graph relationships |
Graph to queued actions (GraphToQueueMutation) |
Follows relationships and can enqueue actions for linked entities |
flowchart LR
Q[["Queue: inbox"]] -->|"QueueToGraph"| E["Linked entities<br/>documents and rows"]
E --> G(("Knowledge graph"))
G -->|"GraphToQueueMutation"| A[["Queue: actions"]]
The current composition API uses .NET CommitAsync, SQL CALL keyload_documents_commit(@arguments) or the official MCP server.
| Boundary | Current contract |
|---|---|
| Atomicity | The same atomic partition and transaction domain (PartitionRef); the batch succeeds or rolls back together |
| Queue readsтобін | Composition does not lease or ACK messages |
| Files | Blobs remain part of the same database, with separate upload and publication operations |
| Preview limits | Full declarative SQL, the native SQL-client protocol and cross-partition composition are still in development; production guarantees remain under qualification |
Details: composition guide · transaction contract.
flowchart LR
Callers["SDK · MCP · SQL · HTTP"] --> Request["Short-lived Orleans<br/>request grain"]
Request --> Partition["Partition grains"]
Partition --> N1[("Node 1<br/>ZoneTree")]
Partition --> N2[("Node 2<br/>ZoneTree")]
Partition --> N3[("Node 3<br/>ZoneTree")]
Each authenticated call gets its own request grain, with bounded call-local data and no persisted request state. After the operation and its stream cleanup settle, it requests deactivation immediately instead of waiting for ordinary idle collection. Completed requests are not kept as a history of active grains. Actual activation and memory recovery under load still require qualification.
Persistent connections will have a separate, bounded connection/session grain; that connection layer is still in development. Partition grains route to node-local storage owners; moving a grain does not move its storage handles. Writes require a persisted majority of the three replicas. See the architecture map and grain lifetime contract.
| Foundation | Why we use it |
|---|---|
| .NET 10 | C# end to end, pooled buffers, Span<T> and native SIMD intrinsics; speedups need measurements |
| Orleans | Request isolation, membership, routing and generated binary serialization; experimental directory/repartitioning remain under qualification |
| ZoneTree | Embedded ordered storage and a write-ahead log for every model; data stays on its node |
| ZoneTree.FullTextSearch | Native full-text indexing on the same storage foundation |
| Aspire | Owns Docker cluster startup, readiness and cleanup |
Orleans moves the routing; ZoneTree keeps the data in place.
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 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
SELECT * FROM QUEUE_MESSAGES('jobs')
SELECT e.eventType FROM EVENTS('events', 'stream-a', 3) AS e
EXPLAIN SELECT * FROM orders
CALL keyload_documents_commit(@arguments)Model views (QUEUE_MESSAGES, EVENTS) and unindexed filters require AllowFullScan: true, and model views don't support cursors. Bounded same-partition INNER JOIN is in source. Full SQL, foreign keys and the native SQL-client protocol remain in progress. The query guide lists the supported syntax, and the compatibility inventory tracks the rest.
Every node serves MCP at /mcp. Its compact catalog offers gateway_tools_search, gateway_tools_route and gateway_tool_invoke; tools are discovered on demand. Connect with an API key, for example in Claude Code's .mcp.json:
{
"mcpServers": {
"keyload": {
"type": "http",
"url": "http://localhost:5101/mcp",
"headers": { "Authorization": "Bearer ${KEYLOAD_API_KEY}" }
}
}
}| Step | Agent workflow |
|---|---|
| Learn | Read keyload://guides/agent-quickstart or request the no-argument keyload_agent_quickstart prompt |
| Discover | Call gateway_tools_search with { "query": "keyload_query_capabilities", "maxResults": 1 } |
| Inspect | Use the returned exact schema and effect hints |
| Invoke | Call gateway_tool_invoke with { "toolId": "keyload_query_capabilities", "arguments": {} } |
Every invocation checks current persisted permissions. Retry uncertain writes with the same command identity and payload. Details: API guide · discovery and qualification contract.
| Question | Answer |
|---|---|
| Is this a vector database? | Vectors share a database with documents, graphs, queues and files. Exact search is available; ANN is in progress. |
| Can it hold agent memory? | Yes: linked documents, embeddings, files, graphs and history are the main use case. |
| Does it replace a stack of databases? | That's the goal for agent workloads. Check the preview limits before moving data. |
| How do agents connect? | MCP at /mcp, with an API key and on-demand tool discovery. |
| What about Python or TypeScript? | Use MCP or /v1/ HTTP. The typed SDK is .NET. |
| Can I use it commercially? | Elastic License 2.0 permits application use. Offering a substantial set of KeyLoad features as a hosted or managed service requires ManagedCode authorization. KeyLoad is source available. |
| Production ready? | Not yet: endurance, fault and power-loss qualification remain open. |
Source map and development commands
Feature code uses Features/<SliceName>/<Responsibility>/ across the solution.
| 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 |
Start with the architecture map, the feature specs and repository rules. After building, run a functional suite (unit, unit-scalar, recovery, rf3, analyzers or site):
node scripts/Features/TestInfrastructure/run-tests.mjs --KeyLoadTests:Suite=unitdotnet format KeyLoad.slnx --verify-no-changes --no-restoreNative TUnit fixtures own Aspire startup and cleanup; rf3 uses real .NET and official MCP clients against three Docker nodes. Benchmark builds, checks and measurements run exclusively in GitHub's Benchmarks workflow.
KeyLoad is licensed under the Elastic License 2.0. Use it in your own applications; contact ManagedCode for authorization to offer it as a hosted or managed database service to third parties. Dependency licenses remain with their respective owners.
KeyLoad is built by Managed Code. We get to build it because other people built great tools first. Thank you to their authors and contributors.
A special thanks to ZoneTree and ZoneTree.FullTextSearch for the storage and text-index foundation, Orleans for the cluster runtime, and the MCP C# SDK team for the native agent connection.
| 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 |
| ManagedCode.Orleans.Identity | Native Orleans request identity context |
| ManagedCode.MCPGateway | Agent authentication and on-demand tool discovery |
| ManagedCode.MarkdownLd.Kb | Knowledge-graph search over tool metadata |
| Cartograph | Segmented backup archives and catalogs |
| OpenTelemetry .NET | Logs, metrics and traces |
| Development & presentation | Contribution |
|---|---|
| Aspire · TUnit | Docker orchestration and real operation tests |
| Roslyn · BenchmarkDotNet | Repository analyzers and microbenchmarks |
| Three.js | Website cluster illustration |
The comparison suite uses free community editions and real native clients:
| Workload family | Comparison projects | Client projects |
|---|---|---|
| Records & SQL | PostgreSQL, MongoDB, Redis | Npgsql, MongoDB .NET Driver, StackExchange.Redis |
| Vectors & text | pgvector, Qdrant, OpenSearch | — |
| Queues & events | RabbitMQ, KurrentDB | RabbitMQ .NET Client, KurrentDB .NET Client |
| Graphs & connected models | Neo4j, SurrealDB, HelixDB | — |
| Time series | TimescaleDB | — |
Active scale: 100,000 and 1,000,000 records, at native 1 or 3 nodes where supported. New adapters remain under qualification. The vector comparison contract defines accuracy, latency, index cost and memory checks; only qualified original GitHub results become public figures.
Developed by Managed Code
