PRODUCTS

KEYWORDS

DumboDB: Announcing TLS Support

DumboDB Logo

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:

ModePlaintextTLS
disabledacceptedrefused
allowTLSacceptedaccepted
requireTLSrefusedaccepted

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!