A MongoDB driver for Zig, built from the wire protocol up.
Named after my cat, Bongo.
Status: experimental. Bongo has a stable low-level/single-server API and a production-oriented replica-set
RuntimeClientfor Zig 0.16. The managed runtime covers verified TLS + SCRAM, URI/SRV configuration, CMAP-style pooling, SDAM discovery/monitoring, primary and read-preference selection, sessions, transactions, retryable reads, and retryable single-document writes. It is still not a production-complete MongoDB driver across every deployment type and specification feature.
const std = @import("std");
const bongo = @import("bongo");
fn run(io: std.Io, allocator: std.mem.Allocator) !void {
var client = try bongo.Client.connect(
io,
allocator,
.{
.username = "admin",
.password = "secretpassword",
},
);
defer client.deinit();
const users = client.database("test").collection("users");
_ = try users.insertOne(.{
.name = "Bongo",
.active = true,
});
var cursor = try users.find(.{ .active = true });
defer cursor.deinit();
while (try cursor.next()) |document| {
const name = (try bongo.bson.Reader.get(document, "name")).?.string;
std.debug.print("{s}\n", .{name});
}
}Collection.find() returns a cursor. Bongo reads firstBatch, sends getMore as needed, and cleans up an unfinished server cursor with killCursors.
For URI/SRV/TLS/pooling/topology/session-aware application code, use bongo.RuntimeClient:
var client = try bongo.RuntimeClient.connectUri(
io,
allocator,
"mongodb://user:password@localhost:27017/app?authSource=admin",
.{},
);
defer client.deinit();For a replica set, Bongo can discover members from one seed, monitor them in the background, select the current primary for writes, and route reads according to supported read preferences:
var client = try bongo.RuntimeClient.connectUri(
io,
allocator,
"mongodb://localhost:27017/app?replicaSet=rs0&readPreference=secondaryPreferred",
.{},
);
defer client.deinit();
var cursor = try client.findWithReadPreference(
"app",
"cards",
.{},
.{ .limit = @as(i64, 10) },
.{ .mode = .secondary_preferred },
);
defer cursor.deinit();| Area | Current support |
|---|---|
| BSON | Encoding, decoding, validation, raw BSON values/documents |
| Wire protocol | OP_MSG encoding/decoding, request/response IDs |
| Connection | TCP plus verified server-authenticated TLS |
| Connection strings | mongodb://, typed options, mongodb+srv:// SRV/TXT discovery |
| Authentication | SCRAM-SHA-256 and SCRAM-SHA-1; negotiated/speculative auth |
| Driver handles | Client, Database, Collection, and managed RuntimeClient |
| Reads | find, findOne, cursors, countDocuments, estimatedDocumentCount, distinct |
| Find options | projection, sort, skip, limit, collation, hint, comment, maxTimeMS, let |
| Writes | insertOne, insertMany, updateOne, updateMany, replaceOne, deleteOne, deleteMany |
| Atomic operations | findOneAndUpdate, findOneAndReplace, findOneAndDelete |
| Write features | upsert, ordered/unordered mixed bulkWrite, write concern |
| Query helpers | Zig helpers for operators such as lte, gte, in, set, and inc |
| Read configuration | read concern plus primary/primaryPreferred/secondary/secondaryPreferred/nearest selection; tag and max-staleness filtering in the replica-set selector |
| Aggregation | multi-stage aggregate cursors and explain |
| Collections | create, list, rename, drop |
| Indexes | create, list, drop |
| Databases | list and drop |
| Commands | generic runCommand with owned raw BSON responses |
| Runtime topology | owned replica-set topology, member discovery, set-name validation, heartbeat polling, RTT tracking, primary changes |
| Server selection | primary/write selection, read-preference selection, serverSelectionTimeoutMS, localThresholdMS latency window |
| Pooling | bounded CMAP-style reusable pools with min/max sizing, maxConnecting, idle lifetime, checkout deadlines, generations/clear semantics, monitoring events |
| Sessions | logical session IDs and transaction numbers |
| Transactions | pinned connection, startTransaction, commit, abort, and one retry of UnknownTransactionCommitResult; live-tested on a replica set |
| Retryability | one retry for the initial find command and retryable insertOne, updateOne, deleteOne, and findOneAndUpdate on replica sets; write retries reuse the same (lsid, txnNumber) |
| Shutdown | deterministic monitor stop/join, new-work rejection, pool close, active-handle checks |
| Timeouts | connect, socket, whole-operation, bounded pool checkout, and server-selection budgets |
| Testing | unit, pinned spec fixtures, standalone integration, TLS+SCRAM, transactions, CMAP, three-member SDAM/failover, retry failpoints, malformed-wire stress, Deez readiness, Linux/Fly validation, MongoDB 7.0/8.0 CI, macOS CI |
The v0.6 runtime is designed so applications do not recreate the client when a primary changes and so eligible operations can recover once from retryable server or transport failures.
RuntimeClientconnects from configured or discovered seeds.- SDAM owns copied server descriptions instead of borrowing a one-shot hello buffer.
- Dedicated monitor work sends periodic hello probes and tracks RTT.
- Writes select the current primary.
- Reads can select a primary or secondary according to the requested mode and latency window.
- Non-primary OP_MSG reads carry
$readPreferencemetadata on the wire. - When the primary changes, the affected application pool generation is cleared and the runtime selects the replacement primary.
- An eligible initial
findis retried once after a retryable read failure.getMoreis deliberately not retried. - Eligible single-document writes are retried once on replica sets while preserving the same logical write identity
(lsid, txnNumber). - New operations continue through the same
RuntimeClient.
The repository includes real three-member replica-set integration tests for primary stepdown/election, secondary reads, retryable read/write failpoints, transaction commit retry, and shutdown with a live secondary cursor.
Bongo deliberately exposes unfinished boundaries instead of pretending to be a complete production driver.
- The original
Clientremains the simpler single-server API. Managed pooling, SDAM, sessions, retryability, and replica-set failover live inRuntimeClient. - v0.6 remains a replica-set runtime milestone. Full sharded/mongos deployment support (#64) and load-balanced mode (#65) are not complete.
- Retryable writes in v0.6 are replica-set scoped and currently cover
insertOne,updateOne,deleteOne, andfindOneAndUpdate.getMoreis intentionally non-retryable. - Complete public session/causal-consistency behavior (#66-#69) and the remaining transaction convenience/body-retry semantics (#74-#75) remain incomplete.
- The specification harness now executes a pinned subset of official MongoDB retryable-read/write fixtures, but it does not claim full upstream fixture-corpus conformance; #96 remains incremental work.
- SDAM currently uses periodic hello polling; it does not yet claim the complete upstream SDAM monitoring specification surface.
- Zig 0.16's standard TLS client cannot present a client certificate. Server-authenticated TLS + SCRAM is supported; built-in mutual TLS / end-to-end
MONGODB-X509is not. See Zig 0.16 TLS gap. - Wire compression is not enabled yet (#46).
- Operation cancellation is not implemented yet (#95).
- Typed BSON struct decoding is planned in #78.
- SCRAM-SHA-256 password preparation currently accepts printable ASCII passwords; full SASLprep support is still incomplete.
Start here:
- Getting started — connect, choose a collection, and understand ownership.
- CRUD — inserts, reads, updates, replacements, deletes, upserts, and bulk writes.
- Querying — filters, options, cursors, aggregation, explain, and concerns.
- Connection strings — URI options and SRV configuration.
- Zig 0.16 TLS gap — the exact standard-library TLS boundary and what remains missing for mutual TLS/X.509.
- Raw commands — use
runCommandsafely when Bongo has no high-level wrapper yet. - Administration — collection, index, and database management.
- Architecture — how the public API reaches BSON, OP_MSG, pooling, and MongoDB.
- Testing and quality — Bongo Style expectations, negative-space testing, assertions, errors, and merge gates.
- MongoDB cursors —
firstBatch,getMore, cleanup, and document lifetimes. - SCRAM — how SCRAM authentication works internally.
- Bongo Style — engineering rules for correctness, safety, performance, and tests.
- Roadmap — what shipped and what comes next.
The normal local gate is intentionally one command:
make testIt provisions the required MongoDB fixtures and runs the complete sequence:
unit
spec harness
standalone integration
TLS + SCRAM
runtime / transactions
CMAP
three-member SDAM / failover
retryable reads / writes / commit handling
malformed-wire stress
Deez-facing readiness
Linux/Fly-style validation uses:
docker compose build --no-cache
docker compose up --abort-on-container-exit --exit-code-from bongo-linux-validationFor release candidates, Deez is also tested as an external consumer against the exact Bongo checkout:
zig build test --fork=../bongo
zig build mongo-integration-test --fork=../bongoBongo exists to learn MongoDB from first principles and grow that understanding into a real Zig driver. The goal is not merely to make commands work; the driver should make wire behavior, ownership, failure modes, and protocol boundaries understandable and deliberate.
