
DumboDB is a NoSQL database which aims to be a drop-in replacement for MongoDB Community Edition, with the addition of version control features you expect from Git. This means you can branch and merge for isolated agent workflows, see the history of any document, and push and pull your data to other servers.
Today, we are happy to announce that DumboDB has added support for Transport Layer Security (TLS). This feature enables users to share data securely between a client and server over an insecure network. It’s the bedrock requirement for all internet commerce, so we consider this a table stakes feature.
We’ll demonstrate this feature with two examples: first, setting up TLS, and second, using client certs for authentication. Let’s go!
Background#
TLS is the protocol that puts the “s” on the end of https. It sits between
the network and whatever is talking over it, so an application sends and
receives the same bytes it always did while the transport underneath takes care
of three separate problems. The traffic is encrypted, so somebody watching
the network sees noise rather than your documents. It is tamper-evident, so
a byte altered in flight is detected rather than delivered. And at least one
side is authenticated, so you know whose server you actually reached.
That third one is the job of the certificate, and it is the part people tend to skip past. Encryption on its own only guarantees that your conversation is private with somebody; the certificate is what establishes that the somebody is the machine you meant. It works because a Certificate Authority (CA), which both sides already trust, has signed a statement binding a public key to a name. The server proves it holds the matching private key, and the client checks the signature and the name before it sends anything.
DumboDB was started in the beginning of 2026, and Mongo 8.0 was the base we emulated because that was the latest and greatest version. That said, there are many Mongo installations which have not moved to 8.0. The security expectations in the 8.0 product are more stringent than in previous versions.
Probably the one most worth calling out is that the default behavior of MongoDB is to use client-side certificates. Client-side certificates provide a very high level of security, but come with the burden of certificate distribution and maintenance.
A client certificate is the same idea as the server’s, pointed the other way. The client holds a private key and a certificate signed by an authority the server trusts. During the handshake, the client signs a value derived from that specific conversation, and the server checks that signature against the public key in the certificate and checks that the certificate chains back to the CA it was configured with. Two things have to hold: you possess the key, and somebody trusted vouched for who you are.
The private key stays on the client, and the signature is bound to the connection it was made on, so there is nothing an eavesdropper can capture and reuse. It also changes what a server breach is worth. A stolen password store is a list of things people log in with; a stolen certificate store is a pile of public documents, because the server never had the keys in the first place. The trade, as noted above, is that somebody has to issue those certificates, get them onto the right machines, and replace them before they expire. Certificate distribution is not covered by this post, but there are products and companies which do nothing but attempt to solve this problem.
Examples#
Now that you have a high-level idea of what TLS solves for you (strong trust of servers and clients), let’s demonstrate how to use it in DumboDB!
What You’ll Need#
Everything below was run on DumboDB v0.7.1. TLS is not in v0.7.0, so an
earlier binary will reject the flags outright rather than fail mysteriously,
but you do need the new one. Grab the
v0.7.1 release; we
publish pre-built binaries for Linux, MacOS, and Windows on the
releases page, so drop the one
for your platform somewhere on your PATH. There’s a Docker image and
build-from-source instructions in the
README if you prefer either of
those.
You’ll also want mongosh, the
MongoDB shell, which is its own download separate from the server. Any MongoDB
driver works just as well, since everything here is standard MongoDB TLS
configuration. Pick your poison!
Finally, we use openssl to mint the certificates. It’s worth calling out that
generating your own certificates is only appropriate in a demo or prototype.
MacOS and Linux generally have openssl, but on Windows, it’s probably easiest
to use Git for Windows. Full details on installing
openssl can be found here.
Running DumboDB with TLS Enabled#
TLS needs a certificate, and a certificate needs an authority to sign it. For a demo we can be our own authority. Certificates are configured by path, separately from the data directory, so they are conventionally kept somewhere of their own. DumboDB follows the same pattern. Create two directories:
$ mkdir -p /tmp/tlsdemo/certs /tmp/tlsdemo/data
$ cd /tmp/tlsdemo
First, create your CA:
$ openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout certs/ca.key -out certs/ca.crt \
-subj "/CN=DumboDB Demo CA/O=Example"
Then the server’s own certificate, signed by the CA. The one detail worth
getting right is the subjectAltName: we’re going to connect to 127.0.0.1,
and that has to appear as an IP entry. A DNS entry for the same thing will
not verify, which can be confusing.
$ openssl req -utf8 -newkey rsa:2048 -nodes \
-keyout certs/server.key -out certs/server.csr \
-subj "/CN=localhost/O=Example"
$ printf 'subjectAltName=DNS:localhost,IP:127.0.0.1\nextendedKeyUsage=serverAuth\n' > certs/server.ext
$ openssl x509 -req -in certs/server.csr -CA certs/ca.crt -CAkey certs/ca.key \
-CAcreateserial -days 365 -out certs/server.crt -extfile certs/server.ext
$ cat certs/server.crt certs/server.key > certs/server.pem
That last line matters: --tlsCertificateKeyFile wants the certificate and its
key concatenated into a single file, not two separate flags.
Now start the server. Open a new terminal for this one and leave it running:
dumbodb --addr 127.0.0.1:27017 --data-dir /tmp/tlsdemo/data \
--tlsMode requireTLS \
--tlsCertificateKeyFile /tmp/tlsdemo/certs/server.pem \
--tlsCAFile /tmp/tlsdemo/certs/ca.crt \
--tlsAllowConnectionsWithoutCertificates
time=2026-10-05T17:17:33.788Z level=INFO msg="Listening on TLS 127.0.0.1:27017..." name=listener
time=2026-10-05T17:17:33.789Z level=INFO msg="DumboDB server started" addr=127.0.0.1:27017 tlsMode=requireTLS data-dir=./data
You may be comparing how most web applications use a plain text port (80) and a
TLS port (443) to differentiate. MongoDB doesn’t work that way - it supports both
on the same port. The --tlsMode flag facilitates this. Three of its
values are best read as a ladder rather than as unrelated settings:
| Mode | Plaintext | TLS |
|---|---|---|
disabled | accepted | refused |
allowTLS | accepted | accepted |
requireTLS | refused | accepted |
This feature exists because you cannot flip a fleet of applications
over in one go. You move the server to allowTLS, migrate clients at whatever
pace they can manage while both kinds of connection keep working on the same
port, and only then tighten to requireTLS once nothing is still speaking
plaintext. There is a fourth value, preferTLS, which a client cannot tell
apart from allowTLS; the difference is in how a server talks to other servers
in a replica set, so it does not come up here.
We use requireTLS throughout this post, because a demo that silently accepts
plaintext is a demo where you can’t tell whether any of this is working.
Two additional flags to mention. First is --tlsCAFile, which is not optional. As of
MongoDB 8.0, a server refuses to run TLS without a chain of trust, and DumboDB
follows suit. But configuring a CA also means the server starts demanding a
certificate from the client, which isn’t what we want yet, so
--tlsAllowConnectionsWithoutCertificates turns that requirement off. We’ll take
it away again in the next section, because that’s exactly the switch that turns
encryption into identity.
Okay, so back to the demo. A plaintext connection now gets rejected:
$ mongosh mongodb://localhost
MongoServerSelectionError: connection <monitor> to 127.0.0.1:27017 closed
Connecting with --tls and a path to the CA should work:
$ mongosh --tls --tlsCAFile ./certs/ca.crt mongodb://localhost
Current Mongosh Log ID: 6ac3dbc4c636ee4d44964032
Connecting to: mongodb://localhost/?directConnection=true&tls=true&tlsCAFile=.%2Fcerts%2Fca.crt&appName=mongosh+2.3.1
[... snip ...]
With the above setup, you are performing operations over a secure channel that can’t be read by others. TLS over the wire is a basic requirement for most applications, and swapping our home-made CA for certificates from an authority you already trust is most of the way to a real deployment. DumboDB is still pre-1.0, so we don’t recommend serving production loads with it yet, but you can get yourself set up for success nevertheless!
One thing to note before moving on: this server has no authentication at all. The channel is encrypted, but anyone who can reach the port gets in. That’s what the next section fixes.
Authenticating Users With Client Certs#
So far the certificate proves the server is who it claims to be. Point it the other way and it can prove who the client is, too, with no password involved at all.
A client certificate per user, because the certificate’s subject is the user
name. We’ll create four: admin, alice, bob, and carol, who we come back
to at the end:
$ printf 'extendedKeyUsage=clientAuth\n' > certs/client.ext
$ for u in admin alice bob carol; do
openssl req -utf8 -newkey rsa:2048 -nodes \
-keyout certs/$u.key -out certs/$u.csr -subj "/CN=$u/OU=engineering/O=Example"
openssl x509 -req -in certs/$u.csr -CA certs/ca.crt -CAkey certs/ca.key \
-CAcreateserial -days 365 -out certs/$u.crt -extfile certs/client.ext
cat certs/$u.crt certs/$u.key > certs/$u.pem
done
The name MongoDB will expect is the subject rendered as a distinguished name
string, the format specified by
RFC 4514. It’s worth printing that
rather than guessing, because the attribute order comes out reversed from the
order you wrote it in. Note that openssl still spells the option RFC2253,
after the older specification that RFC 4514 replaced; the output is the same
either way:
$ openssl x509 -in certs/alice.crt -noout -subject -nameopt RFC2253
subject=O=Example,OU=engineering,CN=alice
Restart the server without --tlsAllowConnectionsWithoutCertificates, and with
--auth so identities mean something. Again, leave this one running:
dumbodb --addr 127.0.0.1:27017 --data-dir /tmp/tlsdemo/data \
--auth \
--tlsMode requireTLS \
--tlsCertificateKeyFile /tmp/tlsdemo/certs/server.pem \
--tlsCAFile /tmp/tlsdemo/certs/ca.crt
DumboDB, similar to Mongo, allows the first localhost connection to create a single user. This allows you to create an admin user, and after that user is created the connection will be unable to do anything else. This operation works only once.
We have to present a client certificate now, because the server stopped allowing connections without one. We are not authenticating yet, so the certificate is only giving us the ability to connect for our first user creation:
$ mongosh --tls --tlsCAFile ./certs/ca.crt \
--tlsCertificateKeyFile ./certs/admin.pem mongodb://localhost
The administrator gets a certificate rather than a password, exactly like
everybody else. Note the name: it’s the subject of admin.pem, not a name we
chose:
test> db.getSiblingDB("$external").runCommand({
createUser: "O=Example,OU=engineering,CN=admin",
roles: [ { role: "root", db: "admin" } ]
})
{ ok: 1 }
That connection is now spent, and so is the exception:
test> db.getSiblingDB("$external").runCommand({createUser: "whoever", roles: ["root"]})
MongoServerError[Unauthorized]: Command createUser requires authentication
Reconnect as the administrator. There is no password on this command line, and there will not be one anywhere else in this post:
$ mongosh --tls --tlsCAFile ./certs/ca.crt \
--tlsCertificateKeyFile ./certs/admin.pem \
--authenticationMechanism MONGODB-X509 mongodb://localhost
Now that you’ve connected, look at the connection status:
test> db.runCommand({connectionStatus: 1}).authInfo.authenticatedUsers
[ { user: 'O=Example,OU=engineering,CN=admin', db: '$external' } ]
You can see that the user is the name provided in the admin client certificate,
and the db is a special value of $external. That’s the indication that
the user is authenticated with their client certificate. Use that privileged
user to create the alice and bob users. Note that only Alice has write
permissions, while Bob does not.
test> db.getSiblingDB("$external").runCommand({
createUser: "O=Example,OU=engineering,CN=alice",
roles: [ { role: "readWrite", db: "shop" } ]
})
{ ok: 1 }
test> db.getSiblingDB("$external").runCommand({
createUser: "O=Example,OU=engineering,CN=bob",
roles: [ { role: "read", db: "shop" } ]
})
{ ok: 1 }
Three users, no passwords between them:
test> db.getSiblingDB("$external").runCommand({usersInfo: 1}).users
[
{
_id: '$external.O=Example,OU=engineering,CN=admin',
db: '$external',
roles: [ { db: 'admin', role: 'root' } ],
user: 'O=Example,OU=engineering,CN=admin',
userId: UUID('89aa244f-292d-4f78-982c-188b08350e09')
},
{
_id: '$external.O=Example,OU=engineering,CN=alice',
db: '$external',
roles: [ { db: 'shop', role: 'readWrite' } ],
user: 'O=Example,OU=engineering,CN=alice',
userId: UUID('256068c4-fe0f-4dd1-a13b-634d4fd2b33d')
},
{
_id: '$external.O=Example,OU=engineering,CN=bob',
db: '$external',
roles: [ { db: 'shop', role: 'read' } ],
user: 'O=Example,OU=engineering,CN=bob',
userId: UUID('382816a0-1621-4664-a389-a47085c68e08')
}
]
Now log in as Alice. Same shape as the administrator’s connection, different file:
$ mongosh --tls --tlsCAFile ./certs/ca.crt \
--tlsCertificateKeyFile ./certs/alice.pem \
--authenticationMechanism MONGODB-X509 mongodb://localhost
[... snip ...]
test> db.runCommand({connectionStatus: 1}).authInfo.authenticatedUsers
[ { user: 'O=Example,OU=engineering,CN=alice', db: '$external' } ]
test> db.getSiblingDB("shop").orders.insertMany([
{_id: 1, item: "widget", qty: 3},
{_id: 2, item: "gizmo", qty: 1},
{_id: 3, item: "sprocket", qty: 7}
])
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }
Swap one file on the command line and you are somebody else, with somebody
else’s permissions. Bob holds read, not readWrite:
$ mongosh --tls --tlsCAFile ./certs/ca.crt \
--tlsCertificateKeyFile ./certs/bob.pem \
--authenticationMechanism MONGODB-X509 mongodb://localhost
[... snip ...]
test> db.runCommand({connectionStatus: 1}).authInfo.authenticatedUsers
[ { user: 'O=Example,OU=engineering,CN=bob', db: '$external' } ]
test> db.getSiblingDB("shop").orders.countDocuments({})
3
test> db.getSiblingDB("shop").orders.insertOne({_id: 4})
Uncaught MongoServerError[Unauthorized]: not authorized on shop to execute command
{ insert: "orders", documents: [ { _id: 4 } ], ordered: true, ... }
Nothing about the roles here is special to certificates. alice and bob are
ordinary users with ordinary RBAC roles; the only difference is that they proved
who they were during the TLS handshake instead of by sending a password. If you
want finer control than read and readWrite, say a user who can update one
collection and not even see another, then all of the
RBAC machinery we
shipped in August applies unchanged.
One last thing worth seeing, because it’s the distinction the whole feature
rests on. We made a certificate for carol along with the others, but never
created a user to go with it:
$ mongosh --tls --tlsCAFile ./certs/ca.crt \
--tlsCertificateKeyFile ./certs/carol.pem \
--authenticationMechanism MONGODB-X509 mongodb://localhost
MongoServerError: Could not find user "O=Example,OU=engineering,CN=carol" for db "$external"
The certificate is perfectly valid and the server trusts the authority that
signed it. There’s just nobody of that name registered in the
admin.system.users collection, so the connection is rejected even though
they have a valid certificate.
Now with Version Control#
Reconnect as Alice, the same way as before:
$ mongosh --tls --tlsCAFile ./certs/ca.crt \
--tlsCertificateKeyFile ./certs/alice.pem \
--authenticationMechanism MONGODB-X509 mongodb://localhost
Her inserts from the last section are still uncommitted. When she commits them, the identity her certificate established is what gets recorded:
test> db.getSiblingDB("shop").runCommand({dumboCommit: 1, message: "Opening stock"})
{
commitId: 'ff9a63u14cnd3re6v13gst2611itndc2',
branch: 'main',
message: 'Opening stock',
author: 'O=Example,OU=engineering,CN=alice <O=Example,OU=engineering,CN=alice@$external>',
timestamp: ISODate('2026-10-05T21:50:18.350Z'),
committer: 'O=Example,OU=engineering,CN=alice <O=Example,OU=engineering,CN=alice@$external>',
committerTimestamp: ISODate('2026-10-05T21:50:18.350Z'),
ok: 1
}
The name in that commit is the subject of a certificate signed by your CA. It
wasn’t typed into a config file or taken from a client’s say-so. This is
always the case when running with --auth, and client certificates work
the same way.
Conclusion#
TLS is a basic requirement for claiming that DumboDB is a drop-in replacement for MongoDB. It’s one of many features we are grinding on to get DumboDB to be a 1.0 product by the end of the year.
There is no document database which supports version control like Dumbo does. How about you jump on our Discord server and tell us what you’re gonna build!