net.tls
import net
netexposes this asnet.tls, soimport netis enough and the names are called asnet.tls.*.import net.tlsreaches 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-encodedkey_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, ornilmax(?string) — The highest acceptable version, ornil
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(), soechoandprint()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(), soechoandprint()show something useful
Note: The wrapped
TcpStreamis consumed: onceconnect()/accept()returns, the originalTcpStreaminstance is dead (the same wayTcpStream.close()leaves it dead) and every further read/write happens through the returnedTlsStreaminstead.
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 callconfig(TlsConfig) — The trust roots and options to handshake withserver_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 callconfig(TlsConfig) — Must have a certificate chain set viaTlsConfig.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