Module crypto/tls
TLS client streams over OpenSSL — plans/archive/D6_TLS_PLAN.md (the O2
decision: one TlsStream implementing the D5 Reader/Writer traits).
On unix targets this requires OpenSSL 1.1.1+/3.x at BUILD time: the
compiler probes pkg-config openssl (with Homebrew keg fallbacks) and
adds the include and link flags; without it, importing this module
fails at the C step with the linker naming the missing library. Windows
needs no OpenSSL — it gets a Schannel/SSPI backend behind the same
fifteen __yo_tls_* functions (shipped in v0.2.26,
plans/archive/D6_TLS_PLAN.md). On wasm there is no backend and
tls_available() answers false.
Integration model: the TCP socket belongs to Yo's per-thread async
runtime, so OpenSSL never sees the fd. The SSL object speaks through two
MEMORY BIOs; this module pumps ciphertext between them and the
TcpStream around every handshake/read/write step.
{ TlsStream } :: import "std/crypto/tls";
s := io.await(TlsStream.connect(`example.com`.to_string(), u16(443), io), e);
_n := io.await(s.write_str("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n", io), e);
n := io.await(s.read(buf.ptr().unwrap(), buf.capacity(), io), e);
io.await(s.close(io), e);
Stability
unstable — the transport works on every supported target (D6 closed
2026-09-04; the compiler's own downloader runs over it) and both
backends verify the chain against the system trust store and check the
hostname, so what is unstable is how narrow the surface is. There is
exactly one entry point, TlsStream.connect(host, port, io), and no
way to say anything else: no options value, so no custom CA bundle, no
client certificate, no ALPN (which HTTP/2 would need), no session
resumption, no "accept this self-signed certificate for a test", and no
server side. Every one of those arrives as a new parameter or a new
options type, which is why connect's signature is not frozen.
Certificate REVOCATION is deliberately not checked on either backend,
matching them to each other rather than to a policy. Freezing follows
the first of those features, not a release count.
Types
TLS failures, D1 style. Handshake/Io carry OpenSSL's own message.
Variants
| Variant | Fields | Description |
|---|---|---|
Connect | host: String | The TCP connection could not be established or resolved. |
Handshake | detail: String | The handshake failed (certificate verification failures land here — OpenSSL reports them through the handshake result). |
Io | detail: String | A read/write failed after the session was established. |
Closed | The peer closed the TLS session (clean close_notify) during an operation that needed data. |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
Methods
to_string : (TlsError) fn(self : TlsError) -> Stringsource : (TlsError) fn(self : TlsError) -> Option(dyn( + ToString))The error that caused this one, or .None at the root of the chain.
Rust's Error::source. Defaulted to .None, so an error with nothing
underneath it implements the trait by saying only what it is; a wrapper
overrides it to hand back what it wrapped. Walking the chain to the root
cause is not expressible yet — the returned Dyn loses the Error
trait on an erased receiver, so a caller can print one link but cannot
follow it (#521,
issues/self-trait-in-a-return-type-loses-the-trait-on-an-erased-receiver.md).
Parameters
| Name | Type | Notes |
|---|---|---|
self | TlsError |
A connected TLS client stream.
Fields
| Name | Type | Description |
|---|---|---|
_tcp | TcpStream | |
_ctx | *(void) | |
_ssl | *(void) | |
_rbio | *(void) | |
_wbio | *(void) | |
_closed | bool |
Trait Implementations
impl(TlsStream, ...)
connect : (TlsStream) fn(host : String, port : u16, io : Io) -> Impl : (Future[Future](TlsStream) IoExn : IoExn)read : (TlsStream) fn(self : TlsStream, buf : *(u8), size : usize, io : Io) -> Impl : (Future[Future](usize) IoExn : IoExn)write : (TlsStream) fn(self : TlsStream, buf : *(u8), size : usize, io : Io) -> Impl : (Future[Future](usize) IoExn : IoExn)write_str : (TlsStream) fn(self : TlsStream, data : str, io : Io) -> Impl : (Future[Future](usize) IoExn : IoExn)write_string : (TlsStream) fn(self : TlsStream, data : String, io : Io) -> Impl : (Future[Future](usize) IoExn : IoExn)close : (TlsStream) fn(self : TlsStream, io : Io) -> Impl : (Future[Future](unit) IoExn : IoExn)impl(TlsStream, IoTraits)
impl(TlsStream, IoTraits)
Methods
read_to_end : (TlsStream) fn(self : TlsStream, io : Io) -> Impl : (Future[Future](ArrayList(u8)) IoExn : IoExn)read_to_string : (TlsStream) fn(self : TlsStream, io : Io) -> Impl : (Future[Future](String) IoExn : IoExn)flush : (TlsStream) fn(self : TlsStream, io : Io) -> Impl : (Future[Future](unit) IoExn : IoExn)Functions
True when this build's TLS backend can connect (unix + OpenSSL).
Returns: bool