Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

net.tls

import net

net exposes this as net.tls, so import net is enough and the names are called as net.tls.*. import net.tls reaches the same definitions directly.

Transport Layer Security over a TcpStream, built on top of rustls. TlsStream.connect()/accept() take an already-connected TcpStream and run a handshake over it, turning it into an encrypted, certificate-verified stream. That works equally well right after connect() (HTTPS, IMAPS) or after a STARTTLS-style plaintext exchange (SMTP, FTPS) - see the second example below.

A basic HTTPS-style client:

import net
import net.tls

var tcp = net.TcpStream()
tcp.connect('example.com:443')

var config = tls.TlsConfig()
var stream = tls.TlsStream.connect(tcp, config, 'example.com')

stream.write_all('GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n')
echo stream.read_as_string()
stream.close()

A STARTTLS-style upgrade looks almost identical, except the wrap happens after a round of plaintext instead of right after connect:

import net
import net.tls

var tcp = net.TcpStream()
tcp.connect('mail.example.com:587')
echo tcp.read_as_string()          # server greeting
tcp.write_all('STARTTLS\r\n')
echo tcp.read_as_string()          # server's go-ahead

var stream = tls.TlsStream.connect(tcp, tls.TlsConfig(), 'mail.example.com')

A minimal server, reusing TcpStream’s own bind/accept for the plaintext side of things:

import net
import net.tls

var config = tls.TlsConfig()
config.set_cert_chain(cert_chain_pem, private_key_pem)

var server = net.TcpStream()
server.bind('0.0.0.0:8443')

while true {
  var client = server.accept()
  var tls_client = tls.TlsStream.accept(client, config)
  tls_client.write_all('hello over TLS\n')
  tls_client.close()
}

Classes

TlsConfig

class net.tls.TlsConfig

The handful of choices a TLS handshake needs: which certificate authorities to trust, an optional certificate and private key to present (as a server, or as a client doing mutual TLS), and which protocol versions and ALPN protocols are acceptable.

A single TlsConfig can be reused for many connections - build it once and pass it to every connect()/accept() call rather than constructing a fresh one per connection.

Constructor

net.tls.TlsConfig()

Returns a new TlsConfig with sensible defaults: the bundled Mozilla root certificate list, no client/server certificate, no ALPN protocols, and both TLS 1.2 and TLS 1.3 enabled.

TlsConfig.native_ptr()

net.tls.TlsConfig.native_ptr() -> Ptr

Hands out this config’s underlying native handle, the same way TcpStream.native_ptr() does - TlsStream’s connect()/accept() need it and, like any _-prefixed field, _ptr isn’t otherwise reachable from outside this class.

Returns Ptr

TlsConfig.set_root_store()

net.tls.TlsConfig.set_root_store(mode)

Chooses where trusted root certificate authorities come from when verifying a peer’s certificate chain.

'bundled' (the default) uses a compiled-in copy of Mozilla’s root list, which is portable and doesn’t depend on anything being installed on the machine. 'native' uses the operating system’s own trust store instead, which picks up locally-installed or enterprise-managed CAs that the bundled list doesn’t know about.

Parameters

  • mode (string) — 'bundled' or 'native'

Raises Error if mode is neither, or (for 'native') if the OS trust store can’t be read

TlsConfig.add_ca_pem()

net.tls.TlsConfig.add_ca_pem(pem)

Adds one or more PEM-encoded certificates to this config’s trusted roots, on top of whatever set_root_store() already selected. Call this more than once to add several custom CAs

  • useful for trusting a self-signed development certificate or a private/internal CA alongside the normal public trust store.

Parameters

  • pem (string) — One or more PEM-encoded CA certificates

Raises Error if none of the certificates in pem could be parsed

TlsConfig.set_cert_chain()

net.tls.TlsConfig.set_cert_chain(cert_chain_pem, key_pem)

Sets the certificate chain and private key this side of the connection presents to the other. Required before TlsStream.accept() can be used with this config (a server always needs a certificate); optional for TlsStream.connect(), where it’s only needed if the server requires a client certificate (mutual TLS).

Parameters

  • cert_chain_pem (string) — The end-entity certificate followed by any intermediate certificates, all PEM-encoded
  • key_pem (string) — The PEM-encoded private key matching the end-entity certificate

Raises Error if either PEM can’t be parsed, or if the key doesn’t match the certificate

TlsConfig.require_client_cert()

net.tls.TlsConfig.require_client_cert(required)

For a server config: whether to require the connecting client to present a trusted certificate (mutual TLS). Verified against the same root store set_root_store()/add_ca_pem() configured. Has no effect on a config only ever used with TlsStream.connect().

Parameters

  • required (bool)

TlsConfig.set_alpn()

net.tls.TlsConfig.set_alpn(protocols)

Sets the list of ALPN (Application-Layer Protocol Negotiation) protocol names this side is willing to speak, in preference order, e.g. ['h2', 'http/1.1']. The negotiated protocol - if any - is available afterwards via TlsStream.alpn_protocol().

Parameters

  • protocols (list) — Protocol names, most preferred first

TlsConfig.set_versions()

net.tls.TlsConfig.set_versions(min, max)

Restricts which TLS protocol versions are acceptable. Pass '1.2' or '1.3' for either bound, or nil to leave that bound open. Both versions are enabled by default.

Parameters

  • min (?string) — The lowest acceptable version, or nil
  • max (?string) — The highest acceptable version, or nil

Raises Error if min or max is neither '1.2', '1.3', nor nil

TlsConfig.set_insecure()

net.tls.TlsConfig.set_insecure(insecure)

Disables certificate verification entirely when insecure is true - no chain-of-trust check, no hostname check, no expiry check. Handshake signatures are still verified cryptographically, so this isn’t a total bypass, just an untrusted one.

Parameters

  • insecure (bool)

Note: This exists for local development and testing against self-signed certificates. Never enable it for a connection that handles real traffic.

PeerCertificate

class net.tls.PeerCertificate

The subset of a peer’s certificate that’s useful to inspect from Zuri code, without needing a full ASN.1/X.509 library on hand. Returned by TlsStream.peer_certificate().

  • printable — has a @to_string(), so echo and print() show something useful

Constructor

net.tls.PeerCertificate(der, subject, issuer, sans, not_before, not_after)

PeerCertificate.der()

net.tls.PeerCertificate.der() -> bytes

The raw DER-encoded certificate, exactly as the peer presented it.

Returns bytes

PeerCertificate.subject()

net.tls.PeerCertificate.subject() -> string

The certificate’s subject, as an RFC 4514-style distinguished name string, e.g. 'CN=example.com,O=Example Inc,C=US'.

Returns string

PeerCertificate.issuer()

net.tls.PeerCertificate.issuer() -> string

The certificate’s issuer, in the same distinguished-name format as subject().

Returns string

PeerCertificate.sans()

net.tls.PeerCertificate.sans() -> list

The certificate’s Subject Alternative Names - the DNS names, IP addresses, email addresses, and URIs it’s actually valid for.

Returns list — a list of strings, possibly empty

PeerCertificate.not_before()

net.tls.PeerCertificate.not_before() -> number

The start of the certificate’s validity period, as seconds since the Unix epoch (UTC).

Returns number

PeerCertificate.not_after()

net.tls.PeerCertificate.not_after() -> number

The end of the certificate’s validity period, as seconds since the Unix epoch (UTC).

Returns number

PeerCertificate.to_string()

net.tls.PeerCertificate.to_string()

TlsStream

class net.tls.TlsStream

An established TLS connection, wrapping an already-connected TcpStream. Always created by wrapping an existing stream - via connect() on the client side or accept() on the server side - never constructed directly.

  • printable — has a @to_string(), so echo and print() show something useful

Note: The wrapped TcpStream is consumed: once connect()/accept() returns, the original TcpStream instance is dead (the same way TcpStream.close() leaves it dead) and every further read/write happens through the returned TlsStream instead.

Constructor

net.tls.TlsStream(_ptr)

TlsStream.connect()

net.tls.TlsStream.connect(tcp_stream, config, server_name) -> TlsStream

Performs a client-side TLS handshake over tcp_stream, verifying the peer’s certificate against config’s trusted roots and against server_name.

tcp_stream must already be connected - to the target host for an implicit-TLS protocol like HTTPS, or to a plaintext connection that’s already had some protocol-specific greeting exchanged over it for a STARTTLS-style protocol.

Parameters

  • tcp_stream (TcpStream) — An already-connected stream; consumed by this call
  • config (TlsConfig) — The trust roots and options to handshake with
  • server_name (string) — The hostname to verify the peer’s certificate against (also sent as the SNI extension)

Returns TlsStream

Raises Error if the handshake fails, or the peer’s certificate isn’t trusted or doesn’t match server_name

TlsStream.accept()

net.tls.TlsStream.accept(tcp_stream, config) -> TlsStream

Performs a server-side TLS handshake over tcp_stream, presenting config’s certificate chain to the connecting peer.

tcp_stream must already be connected - typically the result of TcpStream.accept(), either wrapped immediately (implicit TLS) or after exchanging a protocol-specific plaintext greeting first (STARTTLS-style).

Parameters

  • tcp_stream (TcpStream) — An already-accepted stream; consumed by this call
  • config (TlsConfig) — Must have a certificate chain set via TlsConfig.set_cert_chain()

Returns TlsStream

Raises Error if the handshake fails, config has no certificate chain, or (with require_client_cert()) the client didn’t present a trusted certificate

TlsStream.native_ptr()

net.tls.TlsStream.native_ptr() -> Ptr

Hands out this stream’s underlying native handle, the same way TcpStream.native_ptr() does. net.Poller needs it in order to watch the socket underneath the TLS session, and _ptr is not otherwise reachable from outside this class.

Returns Ptr

TlsStream.read()

net.tls.TlsStream.read(length) -> bytes

Reads up to length bytes of decrypted application data. Same blocking/timeout/EOF semantics as TcpStream.read().

Parameters

  • length (number) — The maximum number of bytes to read

Returns bytes — up to length bytes (may be empty on EOF)

Raises Error on an underlying I/O or TLS record error

TlsStream.read_exact()

net.tls.TlsStream.read_exact(length) -> bytes

Reads exactly length bytes, blocking until that many bytes have arrived.

Parameters

  • length (number) — The exact number of bytes to read

Returns bytes — exactly length bytes

Raises Error if EOF is reached early, or on any other I/O or TLS record error

TlsStream.read_all()

net.tls.TlsStream.read_all() -> bytes

Reads until EOF (i.e. until the peer sends close_notify and closes the connection), returning everything received.

Returns bytes — every byte read up to EOF

Raises Error on an underlying I/O or TLS record error

TlsStream.read_as_string()

net.tls.TlsStream.read_as_string() -> string

Reads until EOF, decoding everything received as UTF-8 text.

Returns string — every byte read up to EOF, decoded as UTF-8

Raises Error on an underlying I/O error, a TLS record error, or if the data isn’t valid UTF-8

TlsStream.write()

net.tls.TlsStream.write(data) -> number

Encrypts and writes data, returning the number of plaintext bytes actually written. A successful call may write fewer bytes than data contains; use write_all() if every byte needs to go out before continuing.

Parameters

  • data (bytes|string) — The bytes (or UTF-8 text) to write

Returns number — the number of bytes actually written

Raises Error on an underlying I/O or TLS record error

TlsStream.write_all()

net.tls.TlsStream.write_all(data)

Encrypts and writes the entirety of data, blocking and retrying internally until every byte has been written.

Parameters

  • data (bytes|string) — The bytes (or UTF-8 text) to write

Raises Error on an underlying I/O or TLS record error

TlsStream.flush()

net.tls.TlsStream.flush()

Flushes any buffered ciphertext out to the underlying socket.

Raises Error on an underlying I/O error

TlsStream.shutdown()

net.tls.TlsStream.shutdown()

Sends a close_notify alert, telling the peer this side is done writing. Unlike close(), the stream stays usable afterwards for reading whatever the peer still has left to send.

Raises Error on an underlying I/O error

TlsStream.close()

net.tls.TlsStream.close()

Sends close_notify (best-effort - a peer that’s already gone won’t stop this from succeeding) and releases the underlying socket. Safe to call more than once.

TlsStream.alpn_protocol()

net.tls.TlsStream.alpn_protocol() -> ?string

The ALPN protocol negotiated during the handshake, or nil if neither side offered one or none matched.

Returns ?string

TlsStream.peer_certificate()

net.tls.TlsStream.peer_certificate() -> ?PeerCertificate

The peer’s end-entity certificate, or nil if the peer didn’t present one (only possible on the server side, when TlsConfig.require_client_cert() wasn’t set).

Returns ?PeerCertificate

TlsStream.to_string()

net.tls.TlsStream.to_string()

2026, Richard Ore and Zuri contributors