mtls.ai

Regular TLS asks “who are you?”
mTLS asks both sides.

Mutual TLS is how services prove their identity to each other — no API keys, no shared secrets, just certificates verified on every connection. This page teaches it the way engineers actually learn it: by watching the handshake happen and getting rejected without a cert.

TLS 1.3handshake, step by step
4 stacksworking config generator
6 commandsthe openssl cheat sheet
zsh — client
$ curl --cacert ca.crt \ --cert client.crt --key client.key \ https://api.mtls.ai:8443/whoami HTTP/2 200 { "identity": "checkout-service", "issuer": "mtls.ai demo CA", "auth": "mTLS — certificate verified" } # No cert? You don’t even get an HTTP error. # The connection dies in the handshake. ↓ Try it below.
~/mtls.ai/handshake

The handshake, step by step

In one-way TLS, only the server proves who it is — your browser checks the site's certificate and that's the end of it. mTLS adds the second half: the server demands the same proof from the client. Step through it.

CLIENT
checkout-service
holds client.crt + client.key
ClientHello
SERVER
api.mtls.ai
holds server.crt + ca.crt

    ~/mtls.ai/demo

    Get rejected. Then don't.

    This is a simulation of a real mTLS-protected endpoint — the same behaviour you'd see against nginx, Envoy, or a Go server with RequireAndVerifyClientCert. Call it once bare, once with a client certificate, and watch both sides of the wire.

    client — curl
    # waiting for you to make a call…
    server — api.mtls.ai access log
    # listening on :8443 (mTLS required)…
    The client certificate that just got accepted — issued by the demo CA, presented during the handshake, verified before a single HTTP byte was read:
    Subject CN
    checkout-service
    SAN (SPIFFE ID)
    spiffe://mtls.ai/ns/prod/sa/checkout
    Issuer
    CN = mtls.ai demo CA
    Serial
    04:A3:91:7C:22
    Valid
    24 hours only — short-lived by design, rotated automatically
    Fingerprint
    9F:2C:…:41:B7 (SHA-256, truncated)
    ~/mtls.ai/playground

    The cert playground

    Pick your stack, name your service, and take a working mTLS server config. Each one does the same three things: present a server cert, demand a client cert, and verify it against your CA before any request is served.

    nginx.conf

    Assumes a CA at /etc/mtls/ca.crt and certs issued from it — generate them with the cheat sheet below. Client identity is passed to your app so it can authorise the caller, not just authenticate it.

    ~/mtls.ai/cheat-sheet

    The 2 a.m. cheat sheet

    Every openssl command you actually need to stand up a demo CA and test an mTLS endpoint — the ones you end up Googling every single time.

    ~/mtls.ai/agents

    AI agents need identity too

    Here's the problem nobody's demo mentions: the moment AI agents start calling each other's APIs — a research agent invoking a payments agent invoking a database tool — API keys stop being an identity strategy. Keys leak, keys get shared, and a key tells you nothing about which workload is calling, only that someone once knew a secret.

    mTLS already solved this for microservices. Every agent gets a short-lived certificate with a cryptographic identity — a SPIFFE ID like spiffe://mtls.ai/agent/research — issued automatically by the platform (SPIRE, cert-manager, or a cloud workload-identity service), rotated in hours, and verified on every single hop. No secrets in prompts. No keys in environment variables waiting to be exfiltrated by a prompt injection.

    When an agent misbehaves, you revoke its identity and it stops existing on the network. Try doing that with a shared bearer token pasted into forty configs.

    Identity before access

    An agent proves what it is cryptographically before it's allowed to ask for anything — authentication from the transport layer, not an afterthought header.

    Short-lived by default

    Certificates measured in hours, not years. A stolen credential expires before it's useful, and rotation is the platform's job, not a calendar reminder.

    Verify every hop

    Agent-to-agent, agent-to-tool, tool-to-database: every connection is mutually authenticated, so a compromised caller can't move sideways unnoticed.