Skip to content

net/http: give the HTTPS client trust roots on darwin - #75

Open
yohimik wants to merge 1 commit into
tinygo-org:mainfrom
yohimik:upstream-pr/http-darwin-roots
Open

net/http: give the HTTPS client trust roots on darwin#75
yohimik wants to merge 1 commit into
tinygo-org:mainfrom
yohimik:upstream-pr/http-darwin-roots

Conversation

@yohimik

@yohimik yohimik commented Aug 30, 2026

Copy link
Copy Markdown

net/http: give the HTTPS client trust roots on darwin

Repository tinygo-org/net. Branch upstream-pr/http-darwin-roots, base
main.

What this does

The client dialled TLS with a nil config, which leaves RootCAs nil, which
sends crypto/x509 to the platform verifier on darwin.
crypto/x509/internal/macos in TinyGo is a stub, so every HTTPS request to a
real server fails verification as soon as the package compiles against the
standard library crypto/tls and not against the no-op stub.

The dial now takes a config from defaultTLSConfig.

  • Off darwin that is still nil, because crypto/x509 finds the system roots in
    the usual files.
  • On darwin it carries a pool that is read once from $SSL_CERT_FILE, or from
    the bundle of macOS at /etc/ssl/cert.pem. A non-nil pool makes x509 build
    the chain in pure Go instead of a call to the stubbed verifier.
  • If no file can be read, the config has no roots, so the result is an ordinary
    verification error and not a check that is silently skipped.

Evidence

There is no CI in this repository. Checked by hand on macOS 26.6 arm64 with a
TinyGo build that carries the matching toolchain change.

  • http.Get("https://example.com/") completes with a verified chain.

A downstream product ships binaries built with these changes in a production
release. dispat v1.4.0 is published and is not a prerelease. It carries
dispat-tiny-linux-amd64 and dispat-tiny-linux-arm64, built by the fork
release v0.42.0-net.4 from sha256-pinned tarballs and smoke-executed under
binfmt before upload, beside six binaries from the gc toolchain.
https://github.com/yohimik/dispat/releases/tag/services%2Fdispat%2Fv1.4.0

The acceptance record of that repository is committed at
packages/docs/docs/internals/tinygo.md. It reports the net.2 to net.4
acceptance history, an integration suite of 694 rows that passes with 0 failures
and 1 documented skip on darwin, and a size table of 0.58x to 0.63x against the
gc equivalents with TinyGo -opt=z -no-debug against go build -trimpath -ldflags "-s -w". Those figures come from that document. They are not a
measurement of this branch.

The self-update path of that program runs over real TLS against a live host, and
its suite has negative rows as well. An unknown CA is refused, and a plaintext
server on a TLS port is refused. The shipped binaries in that release are for
linux. The darwin evidence is the acceptance record and the check above.

Dependencies

  • This only matters once the TinyGo toolchain uses the standard library
    crypto/tls on hosted targets. See the tinygo PR "loader: use the real
    crypto/tls on hosted linux and darwin". With the TLS stub the change is
    harmless and has no effect.
  • http/client.go is also touched by "net/http: follow redirects in the client
    again" in this series. The two merge without a conflict, but take the redirect
    PR first and rebase this one on it, or the reverse, to keep the diff small.

Known gaps

  • A direct tls.Dial with a nil config still fails on darwin for the same
    reason. A caller that does not use this package supplies its own RootCAs.
  • The right long-term fix is a working crypto/x509/internal/macos in TinyGo.
    This is the client-side workaround until then.

Related pull requests

This change is part of one body of work. Together the changes make programs that use the network and child processes work on hosted linux and macOS. A full CLI was tested end to end with all of them and ships binaries built this way, see dispat v1.4.0 in the evidence section.

In tinygo-org/tinygo

In this repository

A merge order that works. The three bug fixes are independent. tinygo-org/tinygo#5633 goes before tinygo-org/tinygo#5635. HTTPS on linux needs only tinygo-org/tinygo#5633 and tinygo-org/tinygo#5635. Full darwin support also needs tinygo-org/tinygo#5636, the net changes and a new src/net submodule pin.

The client dialled TLS with a nil config, which leaves RootCAs nil, which sends
crypto/x509 to the platform verifier on darwin. crypto/x509/internal/macos in
TinyGo is a stub, so every HTTPS request to a real server failed verification
as soon as the package compiled against the standard library crypto/tls and not
against the no-op stub.

Give the dial a config from defaultTLSConfig. Off darwin that is still nil,
because crypto/x509 finds the system roots in the usual files. On darwin it
carries a pool that is read once from $SSL_CERT_FILE, or from the bundle of
macOS at /etc/ssl/cert.pem. A non-nil pool makes x509 build the chain in pure
Go instead of a call to the stubbed verifier. If no file can be read, the
config has no roots, so the result is an ordinary verification error and not a
check that is silently skipped.

A direct tls.Dial with a nil config still fails on darwin for the same reason.
A caller that does not use this package supplies its own RootCAs.
@yohimik

yohimik commented Sep 2, 2026

Copy link
Copy Markdown
Author

tinygo-org/net main has not moved since this branch was opened. It is still
70037cf, so the branch needs no rebase and it is current. v0.42.0 of the
toolchain pins src/net at that same commit, so the change applies to the
released toolchain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant