hanki

tls_stream

stdlib/core/tls_stream.hk: methods on the TlsStream resource type.

A TLS connection is a runtime-managed native resource (HANKI.md §4) as a TcpStream is: the per-actor heap closes one when its last handle goes, close! releases it eagerly, and it is single-owner and NOT sendable across actors.

Open one with tls.connect!; the methods here read, write, and close. The primitive operations are @intrinsic in sys; this face delegates, and the [net] effect bubbles up through the call.

There are two faces and two error types, and the difference is intended. The inherent methods answer sys.TlsFailure, which says which host failed and whether the fault was the handshake, the certificate, or the socket. The Stream impl cannot: that trait fixes its error as sys.NetError (see core/stream.hk for why an associated type is not available), and the trait face folds a TLS failure into Other, rendered, and the detail survives as text and not as structure. Code that wants to branch on a certificate failure calls the inherent method or tls.connect!; code that only frames bytes takes S: Stream and does not care.

There is no hermetic test block here: every method bottoms out in a real TLS connection via an @intrinsic, with no pure result to assert in the stdlib test pack. The mapping above is the one pure thing, and it is asserted.

asnet

def _as_net(f: sys.TlsFailure) -> sys.NetError

Render a TLS failure into the trait face's NetError. Other reports the whole rendered failure, host included, and only the ability to match on it.

_as_net(sys.TlsFailure(host="h", kind=sys.TlsCertificate("expired"))).to_string() => "h: certificate rejected: expired"

TlsStream

TlsStream, or tls.TlsStream, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.

impl TlsStream

read!

def read!(self, max: u32) -> Result<bytes, sys.TlsFailure> [net]

Read once, returning up to max decrypted bytes. An empty bytes is EOF (the peer closed). Err(TlsFailure) on an I/O or protocol error or a closed stream. @no-doctest: reads a real connection; needs a peer, cannot assert in a doctest

write!

def write!(self, data: bytes) -> Result<i64, sys.TlsFailure> [net]

Write once, returning the count written (which may be less than data.length). For the whole buffer, use tls.write_all!. @no-doctest: writes a real connection; needs a peer, cannot assert in a doctest

version!

def version!(self) -> Result<sys.TlsVersion, sys.TlsFailure> [net]

The protocol version the handshake settled on, sys.V13 or sys.V12. sys.TlsVersion is Ord, ascending, and a policy floor is therefore a comparison: a payload that must not travel over 1.2 checks version >= sys.V13. Err(TlsFailure) once the stream is closed. @no-doctest: reads a real connection; needs a peer, cannot assert in a doctest

close!

def close!(self) -> () [net]

Release the connection now, sending close_notify best-effort so the peer can tell a clean shutdown from a truncation. Idempotent: dropping the last handle also closes a stream, and an explicit close! is eager optimisation. @no-doctest: side-effecting close; no return value to assert

impl Stream<TlsStream>

The Stream face: framing code written against S: Stream works over a TLS connection unchanged, which is the whole reason the trait exists.

Each body forwards to the inherent method of the same name above and maps the error, which is not the self-call it looks like: inherent-wins coherence (HANKI.md §10) resolves self.read! to the inherent impl, never back into this one. Forwarding in place of repeating the sys calls binds the two faces from drifting.

read!

def read!(self, max: u32) -> Result<bytes, sys.NetError> [net]

See stream.Stream.read!. A TlsFailure folds into NetError.Other. @no-doctest: reads a real connection; needs a peer, cannot assert in a doctest

write!

def write!(self, data: bytes) -> Result<i64, sys.NetError> [net]

See stream.Stream.write!. A TlsFailure folds into NetError.Other. @no-doctest: writes a real connection; needs a peer, cannot assert in a doctest

close!

def close!(self) -> () [net]

See stream.Stream.close!. @no-doctest: side-effecting close; no return value to assert