Skip to content

Secure q connections with SSL/TLS

This page explains how to set up, configure, and verify TLS-encrypted connections between q server and client processes for local development.

TLS encrypts a q connection end to end, so neither the credentials nor the data can be read in transit. q implements it through the OpenSSL libraries, which you install and configure separately from q itself. For connections without encryption, see IPC basics.

Prerequisites

Before you configure TLS, install the OpenSSL libraries and make sure q can find them. For background, see the OpenSSL documentation.

Install OpenSSL

Install the latest OpenSSL libraries for your operating system, then add the library path to the environment variable your system uses:

  • Linux: LD_LIBRARY_PATH
  • macOS: DYLD_LIBRARY_PATH
  • Windows: PATH

Verify the OpenSSL version

q searches for compatible OpenSSL shared libraries at startup. It has supported OpenSSL 1.1 since v4.0 2020.03.17.

Since v4.1t 2022.03.25, q tries versioned shared libraries in this order:

libssl.so, libssl.so.3, libssl.so.1.1, libssl.so.1.0, libssl.so.10, libssl.so.1.0.2, libssl.so.1.0.1, libssl.so.1.0.0

libssl.3.dylib, libssl.1.1.dylib

q loads both libssl and libcrypto, in this order of preference:

Architecture libssl libcrypto
w64 libssl-3-x64.dll
libssl-1_1-x64.dll
libcrypto-3-x64.dll
libcrypto-1_1-x64.dll
w32 libssl-3.dll
libssl-1_1.dll
libcrypto-3.dll
libcrypto-1_1.dll

Windows also needs the Visual C++ redistributable

KX tested the Windows build against the precompiled libraries from Win32/Win64 OpenSSL. Install the Microsoft Visual C++ redistributable package from Microsoft's download page as well.

Before v4.1t 2022.03.25, q loaded these unversioned files:

libssl.so

libssl.dylib

q loads both libssl and libcrypto, in this order of preference:

Architecture libssl libcrypto
w64 libssl-1_1-x64.dll libcrypto-1_1-x64.dll
w32 libssl-1_1.dll libcrypto-1_1.dll

Basic TLS setup

This section walks through creating self-signed certificates and configuring a server and a client for a TLS connection. It suits a local development environment; in production, use certificates from a certificate authority your organization trusts.

Step 1: Generate certificates

TLS requires the server to hold a certificate and a private key. For local testing, generate your own with the openssl command-line tool.

The script below creates a local certificate authority (CA) and uses it to sign a certificate for a server and one for a client:

# Create a dedicated directory for the certificates
mkdir $HOME/certs && cd $HOME/certs

# --- 1. Create the certificate authority (CA) ---
# A private key for the CA. Keep this secret.
openssl genrsa -out ca-private-key.pem 2048
# A self-signed root certificate for the CA. This is the trust anchor.
openssl req -x509 -new -nodes -key ca-private-key.pem -sha256 -days 365 \
  -out ca-cert.pem -subj "/C=US/ST=CA/L=Somewhere/O=Someone/CN=MyTestCA"

# --- 2. Create the server certificate ---
openssl genrsa -out server-private-key.pem 2048
# A certificate signing request (CSR) for the server
openssl req -new -sha256 -key server-private-key.pem \
  -subj "/C=US/ST=CA/L=Somewhere/O=Someone/CN=MyTestServer" -out server.csr
# Sign the server's CSR with the CA, producing the certificate
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-private-key.pem \
  -CAcreateserial -out server-cert.pem -days 365 -sha256

# --- 3. Create the client certificate ---
openssl genrsa -out client-private-key.pem 2048
openssl req -new -sha256 -key client-private-key.pem \
  -subj "/C=US/ST=CA/L=Somewhere/O=Someone/CN=MyTestClient" -out client.csr
openssl x509 -req -in client.csr -CA ca-cert.pem -CAkey ca-private-key.pem \
  -CAcreateserial -out client-cert.pem -days 365 -sha256

To inspect what you generated:

openssl req  -in server.csr      -noout -text   # the server's CSR
openssl x509 -in server-cert.pem -noout -text   # the server's X.509 certificate
openssl req  -in client.csr      -noout -text   # the client's CSR
openssl x509 -in client-cert.pem -noout -text   # the client's X.509 certificate

To verify both certificates against the CA:

openssl verify -CAfile ca-cert.pem server-cert.pem
openssl verify -CAfile ca-cert.pem client-cert.pem

A shorter route to local certificates

For local development, mkcert produces locally trusted certificates without the openssl invocations above.

Keep private keys out of reach of q

Store certificates and private keys outside any directory reachable from within q. A remote user who can read your q home directory can read your server's private key.

Step 2: Configure and start the server

Point the environment variables at the server's certificate, its private key, and the CA certificate that verifies connecting clients:

export SSL_CERT_FILE=$HOME/certs/server-cert.pem            # the server's certificate
export SSL_KEY_FILE=$HOME/certs/server-private-key.pem      # the server's private key
export SSL_CA_CERT_FILE=$HOME/certs/ca-cert.pem             # verifies client certificates

Start q with the -E command-line option to enable TLS server mode. For the port itself, see how to configure listening ports.

q -p 5001 -E 1

Step 3: Configure and connect the client

In a new terminal, set the client's environment variables. For mutual authentication the client needs its own certificate and key, and it needs the CA certificate to verify the server:

export SSL_CERT_FILE=$HOME/certs/client-cert.pem            # the client's certificate
export SSL_KEY_FILE=$HOME/certs/client-private-key.pem      # the client's private key
export SSL_CA_CERT_FILE=$HOME/certs/ca-cert.pem             # verifies the server's certificate

Start a client q process and open a secure connection with hopen and the tcps:// scheme. The general form is:

h: hopen `:tcps://hostname:port[:username:password]

So, against the server from step 2:

q)h: hopen `:tcps://localhost:5001   / open a secure handle
q)h "2+2"                            / send a command over the encrypted connection
4

A client can also make secure HTTP (HTTPS) requests through the REST server module, and secure WebSocket (WSS) connections — see using WebSockets with q.

Verify the connection

Check the configuration

To see the TLS settings a q process loaded, use -26!. It returns a dictionary:

q)(-26!)[]
SSLEAY_VERSION   | OpenSSL 3.0.13 30 Jan 2024
SSL_CERT_FILE    | /home/user/certs/server-cert.pem
SSL_CA_CERT_FILE | /home/user/certs/ca-cert.pem
SSL_CA_CERT_PATH | /usr/lib/ssl
SSL_KEY_FILE     | /home/user/certs/server-private-ke..
SSL_CIPHER_LIST  | ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RS..
SSL_VERIFY_CLIENT| NO
SSL_VERIFY_SERVER| YES

Inspect the connection status

To get protocol details for an active connection handle, run .z.e on the remote process. It returns a dictionary describing the connection, including whether the peer's certificate verified:

q)h".z.e"
CIPHER  | TLS_AES_256_GCM_SHA384
PROTOCOL| TLSv1.3

Troubleshoot common errors

When a connection fails, check the q console for messages from the OpenSSL library. Two common cases:

  • No shared cipher. The client and server cannot agree on a cipher suite. Compare the SSL_CIPHER_LIST settings on both processes. q reports a 'conn error.

    140735201689680:error:1408A0C1:SSL routines:ssl3_get_client_hello:no
    shared cipher:s3_srvr.c:1417:
    'conn
    
  • CA certificate not found. The path in SSL_CA_CERT_FILE or SSL_CA_CERT_PATH is wrong, or the file is unreadable. q reports a 'conn error.

    ...error:02001002:system library:fopen:No such file or directory...
    ...error:2006D080:BIO routines:BIO_new_file:no such file...
    ...error:0B084002:x509 certificate routines:X509_load_cert_crl_file:system lib...
    'conn. OS reports: Protocol not available
    

Configuration reference: environment variables

Set these environment variables before you start the process; q reads them at startup.

A KX_ prefix takes precedence

Since v3.6, q prefers a KX_-prefixed variable over the plain name, so KX_SSL_CERT_FILE overrides SSL_CERT_FILE. The prefix avoids collisions with other applications that read the same variable names.

Where a variable holds no absolute path, q resolves it against the default directory that openssl version -d reports:

$ openssl version -d
OPENSSLDIR: "/opt/local/etc/openssl"

SSL_CERT_FILE

The path to a file holding the local process's certificate in PEM format. Order the file starting with the subject's own certificate, then any intermediate CA certificates, ending with the root.

Default: <OPENSSLDIR>/server-crt.pem

SSL_KEY_FILE

The path to the private key that belongs to the certificate in SSL_CERT_FILE, also in PEM format.

Default: <OPENSSLDIR>/server-key.pem

SSL_CA_CERT_FILE

The path to a file holding one or more trusted certificate authority (CA) certificates, which q uses to verify the certificate a peer presents. The file must be PEM format, and can hold several concatenated certificates, each delimited as:

-----BEGIN CERTIFICATE-----
... (CA certificate in base64 encoding) ...
-----END CERTIFICATE-----

Text before, between, and after the certificates is permitted, which is useful for describing them.

Default: <OPENSSLDIR>/cacert.pem

Verifying public websites needs a public CA bundle

To verify certificates from public websites, such as when .Q.hg fetches over HTTPS, import a CA bundle from a reputable source such as the curl CA extract page:

curl https://curl.se/ca/cacert.pem > $HOME/certs/cabundle.pem
export SSL_CA_CERT_FILE=$HOME/certs/cabundle.pem

You can append your own self-signed CA certificates to that file.

SSL_CA_CERT_PATH

The path to a directory holding individual trusted CA certificate files in PEM format.

Default: <OPENSSLDIR>

SSL_CIPHER_LIST

The cipher suites the q process offers during the TLS handshake. Override the default to comply with a specific security policy.

Default: the intermediate compatibility list recommended by Mozilla Server Side TLS:

ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-
AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-
SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-
AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:
ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:
ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:
DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-
RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-
RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:
AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS

SSL_VERIFY_CLIENT

How a q server validates a client's certificate.

  • NO (default): the server doesn't request a certificate from the client.
  • YES: the server requests a certificate, and disconnects if it is missing or not valid.
  • REQUESTONLY (since v4.1t 2024.02.07): the server requests a certificate, and continues even if it is missing or not valid.
  • IFPRESENT (since v4.1t 2024.02.07): the server requests a certificate, disconnects if the certificate presented is not valid, and continues if none is presented.

SSL_VERIFY_SERVER

How a q client validates the server's certificate.

  • YES (default): the client verifies the server's certificate against the CAs in SSL_CA_CERT_FILE or SSL_CA_CERT_PATH. An expired certificate fails.
  • NO: no certificate verification at all. This is insecure — use it only on a trusted, private network.
  • HOSTIP (since v4.1 2025.11.25): everything YES does, and additionally checks that the certificate's subject alternative name (SAN) or common name (CN) matches the host you named in hopen. Requires OpenSSL 1.0.2 or later.

With HOSTIP, the check depends on how you addressed the server:

  • For a hostname, q checks the certificate's SAN or CN against it, as X509_check_host does.
  • For an IP address, q checks the certificate against the IPv4 address, as X509_check_ip does. Connecting to 127.0.0.1, for example, requires this in the certificate:

    X509v3 extensions:
            X509v3 Subject Alternative Name:
                IP Address:127.0.0.1
    

SSL_MINPROTOCOL

The minimum TLS protocol version the process accepts. Accepted values are SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3, and None for no limit. Requires OpenSSL 1.1.0 or later. Since q 5.0.20260401.

Default: TLSv1

SSL_MAXPROTOCOL

The maximum TLS protocol version the process accepts. Accepted values are SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3, and None for no limit. Requires OpenSSL 1.1.0 or later. Since q 5.0.20260401.

SSL_CIPHERSUITES

The cipher suites available for TLSv1.3, as a colon-separated list in order of preference. q combines this list with any TLSv1.2 ciphers from SSL_CIPHER_LIST. To list the cipher suites your system offers, run openssl ciphers -s -tls1_3. Requires OpenSSL 1.1.0 or later. Since q 5.0.20260401.

Performance and limitations

Performance

TLS adds overhead to both connection setup and data transfer:

  • Connection setup: a TLS hopen on localhost can be 40 to 50 times slower than a plain TCP connection, because of the certificate exchange and cryptographic handshake.
  • Data transfer: once connected, transfer costs roughly 1.5 times a plain connection, assuming your OpenSSL library has AES-NI hardware acceleration available.

Because of that overhead, TLS suits long-lived, latency-insensitive, low-throughput connections best.

Threading support

TLS support across multi-threaded q has changed over time.

Before v4.1t 2023.11.10, TLS was unavailable in secondary threads, which ruled out:

Since v4.1t 2023.11.10, TLS works on any thread that supports messaging at all, including:

Next steps