2.2.0 shipped TrustAnchors with an honest support matrix and one uncomfortable row: pinning works on
the JVM and nowhere else, and a PEM bundle works on the JVM and Linux and nowhere else. A client
configured to pin on iOS is refused at construction with a message naming the keychain, which is the
right behaviour and is not the end of the story.
Two of the three gaps are closable and one may not be.
Linux cannot pin because Ktor's Curl engine exposes no CURLOPT_PINNEDPUBLICKEY. That is a Ktor gap
rather than a libcurl one, so the options are an upstream contribution, or a configureEngine hook per
target that hands back the typed engine config instead of the star-projected HttpClientConfig<*> that
configureClient gives. The second is the escape hatch 2.2.0 deliberately did not build, and it is
worth revisiting now that there is a concrete thing it would unblock rather than a hypothetical one.
Darwin cannot take a PEM bundle because NSURLSession decides trust from the keychain. A custom
handleChallenge can implement trust evaluation with the Security framework, which is real work and
is the kind of code that is wrong in a way nobody notices: a trust evaluation with a bug accepts more
than it should, and the failure is silent by construction. If it is built, it is built with a test that
proves the negative case, not only the positive one.
Windows may simply be a paragraph. WinHttp offers no per-handle root override, so the answer might stay
"put it in the machine store", and writing that down as a decision closes the row rather than leaving
it looking unfinished.
Exit criterion: each row of the TrustAnchors support matrix in STABILITY.md is either filled in with
a working implementation proven against a self-signed certificate in CI, or carries a decision note
saying what would have to change, and no row is left reading as work in progress.
2.2.0shippedTrustAnchorswith an honest support matrix and one uncomfortable row: pinning works onthe JVM and nowhere else, and a PEM bundle works on the JVM and Linux and nowhere else. A client
configured to pin on iOS is refused at construction with a message naming the keychain, which is the
right behaviour and is not the end of the story.
Two of the three gaps are closable and one may not be.
Linux cannot pin because Ktor's Curl engine exposes no
CURLOPT_PINNEDPUBLICKEY. That is a Ktor gaprather than a libcurl one, so the options are an upstream contribution, or a
configureEnginehook pertarget that hands back the typed engine config instead of the star-projected
HttpClientConfig<*>thatconfigureClientgives. The second is the escape hatch2.2.0deliberately did not build, and it isworth revisiting now that there is a concrete thing it would unblock rather than a hypothetical one.
Darwin cannot take a PEM bundle because NSURLSession decides trust from the keychain. A custom
handleChallengecan implement trust evaluation with the Security framework, which is real work andis the kind of code that is wrong in a way nobody notices: a trust evaluation with a bug accepts more
than it should, and the failure is silent by construction. If it is built, it is built with a test that
proves the negative case, not only the positive one.
Windows may simply be a paragraph. WinHttp offers no per-handle root override, so the answer might stay
"put it in the machine store", and writing that down as a decision closes the row rather than leaving
it looking unfinished.
Exit criterion: each row of the
TrustAnchorssupport matrix inSTABILITY.mdis either filled in witha working implementation proven against a self-signed certificate in CI, or carries a decision note
saying what would have to change, and no row is left reading as work in progress.