I found the error: I didn’t pass the peer name to createTLSStream. This works:

import vibe.core.net;
import vibe.stream.tls;
import std.stdio;

void main()
{
	auto conn = connectTCP("kernel.org", 443);
	auto sslctx = createTLSContext(TLSContextKind.client);
	sslctx.useTrustedCertificateFile("/etc/ssl/certs/ca-certificates.crt");
	auto stream = createTLSStream(conn, sslctx, "kernel.org"); // <- Pass the peer name!

	stream.write(
		"HEAD / HTTP/1.1\r\n" ~
		"Host: kernel.org\r\n\r\n"
	);

	while (true)
	{
		auto line = cast(const(char)[])stream.readLine();
		if (!line.length) break;
		stdout.writeln(cast(char[])line);
	}

	stream.finalize();
	conn.close();
}

I suggest updating the example to show a real-world rather than a unit test scenario.