TLS/SSL negotiation for PostgreSQL connections.
Implements both libpq negotiation styles: the traditional SSLRequest handshake (sslnegotiation=postgres) and Direct SSL, which starts TLS immediately and requires the "postgresql" ALPN protocol (sslnegotiation=direct, PostgreSQL 17+). The TLS handshake itself is shared by establishTls under both async backends:
- chronos: BearSSL-based TLS via chronos/streams/tlsstream, with custom trust anchor parsing (parseTrustAnchors) and X.509 capture for SCRAM-SHA-256-PLUS channel binding (installX509Capture).
- asyncdispatch: OpenSSL via std/asyncnet.wrapConnectedSocket, with PEM trust anchors written to a temp file and SSL_get_peer_certificate used for channel binding.
Internal: not re-exported through pg_connection.nim; import this module directly.
Procs
proc negotiateSSL(conn: PgConnection; config: ConnConfig; sslHost: string): owned( Future[void]) {....stackTrace: false, raises: [Exception, PgConnectionError, ValueError, SslError], tags: [RootEffect, WriteIOEffect], forbids: [].}
- Negotiate TLS. sslnegotiation=postgres (default) sends an SSLRequest first; sslnegotiation=direct starts TLS immediately (PostgreSQL 17+). sslHost is the name matched against the server certificate (libpq semantics: the entry's host, never hostaddr).
proc sniName(sslHost: string; sslSni: bool): string {....raises: [], tags: [], forbids: [].}
- Value for the TLS SNI extension. Empty means "do not send SNI". Matches libpq: SNI is on by default and suppressed for IP literals (RFC 6066 ยง3 forbids IPs in server_name) and when the host name is unknown (hostaddr-only).
proc validateDirectSslCompatible(config: ConnConfig) {. ...raises: [PgConnectionError], tags: [], forbids: [].}
- Reject sslnegotiation=direct under a weak sslmode. Direct SSL skips the SSLRequest probe so it has no plaintext fall-back path (libpq parity). Idempotent โ safe to call from any layer.