Dolt is the world’s first version-controlled relational database. Dolt lets you branch, fork, clone, merge, and diff your relational data, in all the same ways Git lets you work with your files.
Today, we’re excited to announce support for client-cert authentication, also known as mutual-TLS authentication. This feature allows you to enforce additional security for user accounts when they log into a Dolt SQL server. When configured for a user account, the client must present a certificate and the server will validate that the client’s certificate is chained from the server’s CA (certificate authority) certificate.
Client certificate authentication offers several advantages:
- Strong, cryptographic proof of identity tied to a private key instead of a password.
- No risk of password leaks, reuse, phishing, or replay attacks.
- Built-in mutual authentication: the client proves its identity while also verifying the server.
- Fine-grained access control using certificate subject and issuer attributes.
- Ideal for automated systems and microservices where secure, non-interactive authentication is required.
In the rest of this post, we’ll take a look at how the mutual TLS authentication protocol works, and show it in action with Dolt.
Mutual TLS Overview#
Mutual TLS (mTLS) extends the standard TLS protocol by requiring both sides of a connection to present and validate X.509 certificates. In a typical TLS handshake, only the server proves its identity, ensuring the client is talking to the correct host. With mTLS, the client must also present a certificate, which the server verifies before allowing access. This transforms TLS from a one-way identity proof into a fully authenticated, cryptographically enforced trust relationship.
The result is a defense-in-depth model that significantly strengthens security. Instead of relying solely on passwords, which can be guessed, leaked, or reused, mTLS ensures that only clients with valid, signed certificates can connect. Certificates can encode identity, organizational metadata, or issuance policies, giving you fine-grained control over access. Even if an attacker obtains a username and password, they cannot connect without also possessing the private key corresponding to an approved certificate.
Deep Dive on Mutual TLS Protocol#
Under the hood, mutual TLS builds on the standard TLS 1.2/1.3 handshake, but extends it so that both peers present an X.509 certificate and prove possession of the associated private key. The handshake begins with the client sending a ClientHello message containing supported cipher suites, signature algorithms, and key-exchange mechanisms. The server responds with a ServerHello message selecting the cryptographic parameters, followed by its certificate chain. At this point in a standard TLS connection, only the server authenticates itself. In an mTLS-enabled configuration, the server also sends a CertificateRequest message, formally instructing the client to provide a certificate for identity verification.
When the client receives this request, it responds by sending its own certificate chain and a CertificateVerify message. The CertificateVerify step is essential: it proves that the client actually possesses the private key corresponding to the public key in the certificate. This proof takes the form of a digital signature over the transcript of the handshake so far. The server performs the same process with its own CertificateVerify message, enabling each side to confirm that the peer is not merely presenting certificates but is cryptographically in control of them.
The server then checks the client’s certificate chain against its configured trust anchors. This includes verifying each signature in the chain, ensuring the certificates have not expired, and confirming that the root or intermediate CA is authorized to sign client certificates. TLS libraries also enforce key-usage and extended-key-usage constraints, preventing a certificate issued for server authentication from being misused as a client credential. The server may also evaluate the certificate’s subject DN, SAN entries, or custom extensions, depending on what additional constraints have been configured. Only after all these cryptographic and policy checks succeed does the server allow the session to proceed.
Once both peers have verified each other’s certificates and completed the handshake, they derive shared symmetric encryption keys using the negotiated key exchange algorithm, typically based on ephemeral elliptic-curve Diffie-Hellman in modern TLS 1.3 deployments. These ephemeral exchanges guarantee forward secrecy: even if a private key is compromised in the future, past sessions remain confidential because each session key is destroyed after use. The remainder of the communication between client and server is then encrypted and authenticated using AEAD ciphers such as AES-GCM or ChaCha20-Poly1305, providing confidentiality, integrity, and tamper detection on every packet.
This handshake structure gives mTLS its security properties: identity is tied to cryptographic key control, certificates bind identity to a trust hierarchy, and each session produces fresh keys resistant to passive and active interception. In a database, this means that authentication happens before any SQL is parsed, before any credentials are exchanged, and before any application-level state is touched. Only clients that successfully authenticate at the transport layer are allowed through, giving operators strong, low-friction protection against unauthorized access.

Dolt Demo#
Okay, so now that you are an expert in certificates and authentication, let’s take a look at how we set up a Dolt SQL server to run with TLS, how we configure a user account to require a client certificate, and how we supply the client certificate and private key when connecting to the database.
Certificate and Key Generation#
We’re going to create our own self-signed certificates for this demo using openssl, but in a production setup, you’ll want to work with your security team or network administrator to find the right way your organization handles certificates and private keys. We need to create a CA cert that is shared across the client and server, a cert and private key for the server, and a cert and private key for the client.
First we create the CA key and certificate:
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 365 \
-key ca-key.pem -out ca.pem \
-subj "/C=US/ST=Washington/L=Seattle/O=Mutual TLS Demo/CN=Demo CA"
Next we use the CA cert to create a server key and cert:
openssl genrsa 2048 > server-key.pem
openssl req -new -key server-key.pem -out server-req.pem \
-subj "/C=US/ST=Washington/L=Seattle/O=Mutual TLS Demo/CN=Server"
openssl x509 -req -in server-req.pem -days 365 \
-CA ca.pem -CAkey ca-key.pem -set_serial 01 \
-out server-cert.pem
And last, but not least, we use the CA cert to create a client key and cert:
openssl genrsa 2048 > client-key.pem
openssl req -new -key client-key.pem -out client-req.pem \
-subj "/C=US/ST=Washington/L=Seattle/O=Mutual TLS Demo/CN=Client1"
openssl x509 -req -in client-req.pem -days 365 \
-CA ca.pem -CAkey ca-key.pem -set_serial 02 \
-out client-cert.pem
Once you’ve created all the certs and private keys, copy the following files to a known location on your computer where you can reference them later. I’m moving them to /opt/certs on my computer, but you can use any location you want.
cp ca.pem server-cert.pem server-key.pem client-cert.pem client-key.pem /opt/certs/
Server Configuration#
Now that we’ve got our certs and private keys ready, to enable mutual TLS authentication we need to configure our Dolt SQL server with a couple options. The first certificate is the CA certificate, or Certificate Authority certificate. This is the root of our server’s trust. In order for our server to trust any certificates, they must come from a certificate chain that terminates with our CA cert. The second certificate and the private key are used to enable TLS on our server.
We configure the ca_cert, tls_cert, and tls_key settings in the listener section of a config.yaml file for our server:
log_level: info
listener:
host: "0.0.0.0"
port: 3306
ca_cert: /opt/certs/ca.pem
tls_cert: /opt/certs/server-cert.pem
tls_key: /opt/certs/server-key.pem
Next, we start up the SQL server with this configuration by passing a path to our config.yaml file through the --config parameter:
dolt sql-server --config config.yaml
If the server starts up correctly, you should see output like this:
Starting server with Config HP="0.0.0.0:3306"|T="28800000"|R="false"|L="info"
INFO[0000] Creating root@localhost superuser
INFO[0000] Server ready. Accepting connections.
WARN[0000] secure_file_priv is set to "", which is insecure.
WARN[0000] Any user with GRANT FILE privileges will be able to read any file which the sql-server process can read.
WARN[0000] Please consider restarting the server with secure_file_priv set to a safe (or non-existent) directory.
User Account Setup#
Now that our SQL server is running, we can log into a SQL connection and configure user accounts to require an X509 certificate. Let’s take a look at a few examples of ways to require a client cert for a user. For more details on options and syntax, see the official MySQL reference for TLS options when creating users.
In this first example, we’re creating a user with no password and requiring that the client present a valid client certificate when authenticating. That client certificate will be validated against the server’s CA cert, and if the client cert does not come from a chain of trust terminating in the server’s CA cert, then the connection will be terminated and the user will not get access to the database.
CREATE USER newUser@'%' REQUIRE X509;
GRANT ALL PRIVILEGES ON *.* TO newUser@'%';
Mutual TLS authentication is applied on top of the existing, supported authentication protocols. This means that a user account can be configured for a password as well as requiring that a client certificate is provided. Here’s an example where a newUser account is created with a password for the default authentication plugin and a requirement for a client-side certificate.
CREATE USER newUser@'%' IDENTIFIED BY 'my#password' REQUIRE X509;
GRANT ALL PRIVILEGES ON *.* TO newUser@'%';
In addition to requiring a client certificate when authenticating, the CREATE USER syntax also allows additional constraints to be placed on client certificates. The REQUIRE ISSUER and REQUIRE SUBJECT syntax allows you to specify the exact, case-sensitive values that the client cert’s issuer and subject must contain in order to authenticate successfully. This allows you to further lock down what certificate the client is using. Because REQUIRE ISSUER and REQUIRE SUBJECT automatically imply that a client certificate is required, you don’t need to (and can’t) combine them with REQUIRE X509. Note that the subject we specify here matches the subject we specified when creating the client’s certificate earlier, and the issuer matches the subject of the shared CA cert we used to create the client certificate. If these values do not match exactly, then the server will not allow the client to connect.
CREATE USER newUser@'%' REQUIRE ISSUER '/C=US/ST=Washington/L=Seattle/O=Mutual TLS Demo/CN=Demo CA'
AND SUBJECT '/C=US/ST=Washington/L=Seattle/O=Mutual TLS Demo/CN=Client1';
GRANT ALL PRIVILEGES ON *.* TO newUser@'%';
Client Access#
Now that our server is configured with a CA cert and our user account is configured to require an X509 certificate, let’s see how we use a client to authenticate as that user.
We’re using the mysql client to connect to our Dolt SQL server, but the concept is similar for other clients. We need to supply a few extra pieces of information:
ssl-mode– The mysql client supports several SSL/TLS modes. By default (PREFERRED), the client will attempt an encrypted connection but fall back to an unencrypted one if TLS is unavailable. SpecifyingVERIFY_CArequires that the connection use TLS and that the server’s certificate be validated against the CA provided viassl-ca. If the server cannot negotiate TLS or the certificate cannot be validated, the connection fails.ssl-ca– This is the CA certificate used by the client to validate the server’s certificate during the handshake. The client never sends this CA over the network. Clients can connect without validating the server’s identity, but without this step the client has no proof of who it is talking to and therefore no real mutual trust.ssl-cert– This is the client’s certificate. It is transmitted to the server as part of the mutual TLS handshake. The server validates that this certificate chains to a CA it trusts (configured in the server’s ownssl_casetting). The client typically sends its full certificate chain.ssl-key– This is the client’s private key. It never leaves the client machine. During the handshake, it is used to sign specific TLS messages so the server can verify that the client possesses the private key that corresponds to the certificate it presented. After this proof-of-possession step, the private key is not used for encrypting data; all subsequent data encryption uses symmetric session keys negotiated by the TLS protocol.
mysql -unewUser --protocol TCP -e "SELECT USER();" \
--ssl-mode=VERIFY_CA \
--ssl-ca=/opt/certs/ca.pem \
--ssl-cert=/opt/certs/client-cert.pem \
--ssl-key=/opt/certs/client-key.pem
If this works correctly, then the server will authenticate your connection as newUser and will execute the statement SELECT USER(); and return the results, showing the name of the authenticated user:
+-----------+
| USER() |
+-----------+
| newUser@% |
+-----------+
Wrap Up#
Dolt now supports mutual TLS authentication, enabling your server to verify client identities using X.509 certificates in the same way MySQL does. When mutual TLS is configured for a user, the user’s client must present a valid certificate signed by a trusted CA, giving you strong, cryptographically backed assurance that only authorized users can access your Dolt SQL server. This strengthens the security posture of your system, which is particularly important for teams operating in regulated environments or deploying zero-trust network patterns.
This feature was originally requested by a customer. We love building out features for customers, so let us know if there’s a feature you need. If you want to talk more about connection security and authentication, or if you’re just curious about using a version-controlled relational database, come by our Discord and say hello!
