Supply a verifier¶
A Client cannot be built without a Verifier. New(nil, …) returns an error,
because a resolver with a fetch-without-verify mode has a configuration that will
eventually be the one in use.
The estate verifier¶
That is the whole wiring. trust.Estate requires the embedded key and the
key published over WKD at artifacts-release@phpboyscout.uk to agree, and fails
closed if either is unavailable.
Get the key from the channel and embed a copy in your binary:
curl -fsSL -o release.asc \
https://gitlab.com/phpboyscout/artifacts/-/raw/main/keys/artifacts-release-signing.asc
gpg --show-keys release.asc # expect 544E64F3B87561D56739333634A711C4B9EAA99A
Embedding is the point — a key fetched at runtime from the same place as the signature it validates proves nothing. See The trust model.
Options¶
verifier, err := trust.Estate(releaseKey,
trust.WithHTTPClient(myClient), // proxy, custom TLS, instrumentation
trust.WithLogger(logger)) // diagnostics from key resolution
The logger will not receive a fail-open warning, because Estate does not
fail open. If you are watching for that warning to tell you WKD is down, you will
get an error from Verify instead.
Inspecting what is trusted¶
Useful at startup, to log which key a build will accept before it resolves anything.
Common failures¶
Empty or missing embedded key — Estate returns an error at construction
rather than at first use, so a build that forgot its go:embed fails on startup.
This is a deliberate refusal: the underlying go/signing builder would otherwise
degrade to a WKD-only resolver and discard the crosscheck requirement, giving
single-anchor verification that reports success.
WKD unreachable — a captive portal, an intercepting proxy, an air-gapped
network. Verify errors. That is the fail-closed posture working; there is no
option to relax it, because an attacker who can block the WKD host would
otherwise downgrade you to a single anchor silently.
Fingerprint mismatch — the embedded key and the WKD key are different keys. Either your embedded copy is stale after a rotation, or one of the two anchors has been tampered with. Re-download the key and compare fingerprints before assuming the former.
Your own verifier¶
Verifier is a one-method interface:
Implement it when you have a genuinely different trust posture — a private mirror signed by your own key, a hardware-backed verifier, a corporate PKI.
In tests, a stub keeps the cryptography out of the way:
type alwaysValid struct{}
func (alwaysValid) Verify(context.Context, []byte, []byte) error { return nil }
Be deliberate about this. A tool that builds its own Verifier is choosing its
own trust anchors, which is exactly the decision that should be hard to make by
accident — and a stub that reaches production is a resolver that verifies
nothing while appearing to.