Skip to content

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

//go:embed release.asc
var releaseKey []byte

verifier, err := trust.Estate(releaseKey)

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

prints, err := verifier.Fingerprints(ctx)

Useful at startup, to log which key a build will accept before it resolves anything.

Common failures

Empty or missing embedded keyEstate 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:

type Verifier interface {
    Verify(ctx context.Context, manifest, sig []byte) error
}

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.