libcurl dependency review¶
This note records the dependency review for libcurl.
Decision¶
libcurl is accepted as the selected HTTP transport for the federation
outbound HTTP client, and (since 0.12.44) as the SMTP client for email
pushers (src/email/smtp_client.cpp, ADR-0220). It is wrapped behind merovingian::http::OutboundClient
and must not leak CURL*, CURLcode, or curl_slist* types into routing,
federation logic, room logic, or any caller above the HTTP transport
boundary.
Why it is needed¶
Federation outbound traffic requires an HTTPS client with peer and hostname certificate verification, redirect refusal, pinned-address resolution to enforce the SSRF policy, bounded response capture, and a stable error contract that supports retries and circuit breaker decisions. Rolling these from scratch is high-risk; libcurl is widely audited, packaged across the supported Linux and BSD targets, and ships in mainline distributions.
Security boundary¶
CURL*,curl_slist*, andCURLcodeownership is RAII-managed insidesrc/http/outbound_client.cpp.curl_global_initruns once through a function-local static guard so the call is serialized before any easy-handle initialization on other threads.- Every request runs with
CURLOPT_SSL_VERIFYPEER=1,CURLOPT_SSL_VERIFYHOST=2,CURLOPT_FOLLOWLOCATION=0,CURLOPT_PROTOCOLS_STR="https", andCURLOPT_NOSIGNAL=1. Each security option is checked and the request fails closed if libcurl refuses to apply it. - Hostnames are pinned to caller-supplied addresses through
CURLOPT_RESOLVEso the connection cannot drift to a different address after the federation security policy validates the destination. - Response bodies are captured behind a per-request byte cap; oversized
bodies abort the transfer and surface as
OutboundError::response_too_large. - 3xx responses are not followed; they surface as
OutboundError::redirect_rejectedwith the status and headers preserved on the result for audit.
SMTP (email pushers)¶
src/email/smtp_client.cpp is the second, and only other, place CURL* and
curl_slist* appear, under the same RAII rules. Each email is one easy handle,
destroyed when the send ends, with CURLOPT_PROTOCOLS_STR restricted to the
configured smtps or smtp, CURLOPT_USE_SSL=CURLUSESSL_ALL unless the
operator chose a cleartext loopback relay, peer and host verification on,
CURLOPT_SSLVERSION at least TLS 1.2, no proxy, and no connection reuse. Startup
refuses a configured relay whose scheme curl_version_info does not list, so the
static build's default protocol set (SMTP included) is now relied on; a
--disable-smtp build would refuse to start with a relay configured rather than
accept email pushers it cannot deliver. See ADR-0220. RHEL-compatible distributions install
libcurl-minimal (no SMTP) by default; operators who want email install the full libcurl,
and the integration scenarios that need an SMTP session skip where it is missing.
Maintenance and platform posture¶
libcurl is a system-provided dependency on every platform — the meson
dependency uses allow_fallback: false, so the build always links the operating
system's libcurl and never vendors it. The build host must provide the libcurl
development files (libcurl4-openssl-dev, libcurl-devel, or the curl
package depending on platform), and the OS packages declare libcurl as a runtime
dependency so deployments receive distro security updates. The TLS backend
follows the host libcurl's OpenSSL/LibreSSL selection; the OutboundClient
integration suite catches backend behavior drift across supported platforms.
The portable static (musl) build is the one exception. Alpine's libcurl.a
pulls in libpsl, libidn2 and libunistring, whose static archives Alpine does
not ship, so scripts/build-static-linux.sh passes -Dstatic_curl_wrap=true
and builds curl from subprojects/curl.wrap (curl 8.23.0) instead.
subprojects/packagefiles/curl/meson.build runs curl's own configure as a
Meson external project, static only, against the host OpenSSL, with every
optional backend it does not need turned off (--without-libpsl,
--without-libidn2, compression, HTTP/2 and HTTP/3, SSH, GSS-API). Check its
configure options against each new curl release: autoconf only warns about an
option it no longer knows, so a removed one fails silently (RTMP support, and
with it --without-librtmp, was removed in curl 8.20.0). <curl/curl.h>
resolves from the installed include root the same way on Linux and BSD.
License¶
libcurl is released under the curl license, an MIT-style license (SPDX:
curl). It is compatible with Merovingian's GPL-3.0-or-later distribution
and is resolved from the operating-system package.
Current limitations¶
- The libcurl-backed
perform()is synchronous. Async/multi-handle support is intentionally deferred. - Connection pooling and keep-alive across requests is deferred until the federation outbound transaction flow demands it.
- Per-platform integration tests against a local TLS test server land with the federation wiring slice.