Configure trust¶
Choose how a TrustSet is sourced: from keys embedded in your binary, from a
key published over WKD, or from both — with the two cross-checked so they
must agree. The high-level entry point is verify.BuildKeyResolver; for full
control you can compose a CompositeResolver directly.
For the reasoning behind cross-checking independent sources, read The trust model.
Sources at a glance¶
KeySource |
Anchors used | Network? |
|---|---|---|
"embedded" |
keys baked into the binary | no |
"external" |
a key fetched via WKD for an email | yes |
"both" (default) |
embedded and WKD, must agree | yes |
Build a resolver from config¶
BuildKeyResolver takes a KeyResolverConfig plus any embedded armoured keys,
and returns a KeyResolver. Call Resolve to obtain a verified TrustSet:
resolver, err := verify.BuildKeyResolver(verify.KeyResolverConfig{
KeySource: "both", // "embedded" | "external" | "both"
ExternalKeyEmail: "[email protected]",
RequireExternalCrosscheck: true,
}, embeddedKey)
if err != nil {
return err
}
trust, err := resolver.Resolve(ctx)
if err != nil {
return err // see the error cases below
}
// use trust.VerifyManifestSignature(...) as usual
KeySource defaults to "both" when empty. ExternalKeyEmail is required for
"external", and enables the WKD leg of "both" when set.
Fail-closed vs fail-open¶
RequireExternalCrosscheck maps to CompositeResolver.RequireAll and controls
how a child resolver error (such as a WKD fetch failure) is handled:
true(fail-closed) — any child error aborts withverify.ErrKeyResolverUnavailable. Use this when the external cross-check is mandatory: a WKD outage must stop the release, not silently downgrade trust to the embedded key alone.false(fail-open) — child errors are logged at Warn (if you injected a*slog.Logger) and the surviving trust set is returned, as long as at least one resolver succeeded. Use this when WKD is a best-effort reinforcement of an already-trusted embedded key.
A fingerprint disagreement between sources is different: it always aborts
with verify.ErrKeyResolverMismatch, regardless of RequireExternalCrosscheck.
Sources that disagree are never reconciled — that is the whole point of the
cross-check.
trust, err := resolver.Resolve(ctx)
switch {
case errors.Is(err, verify.ErrKeyResolverMismatch):
// embedded and WKD keys disagree — treat as a possible compromise
return err
case errors.Is(err, verify.ErrKeyResolverUnavailable):
// no usable source (fail-closed, or every source failed)
return err
case err != nil:
return err
}
Inject an HTTP client and logger¶
WKD fetches use the *http.Client you supply; nil falls back to a stdlib
client with a 30-second timeout. The *slog.Logger receives fail-open warnings;
nil disables logging entirely.
resolver, err := verify.BuildKeyResolver(verify.KeyResolverConfig{
KeySource: "both",
ExternalKeyEmail: "[email protected]",
RequireExternalCrosscheck: false, // fail-open: warnings go to Logger
HTTPClient: hardenedClient, // custom TLS / proxy / instrumentation
Logger: slog.New(handler),
}, embeddedKey)
Compose resolvers directly¶
For arrangements BuildKeyResolver does not cover — multiple WKD emails, a
bespoke KeyResolver, or precise control over which resolvers must agree — build
a CompositeResolver yourself. Any type implementing the KeyResolver
interface (Name() string, Resolve(ctx) (*TrustSet, error)) can be a child.
embedded := verify.NewEmbeddedResolver(embeddedKey)
wkd, err := verify.NewWKDResolver(verify.WKDResolverConfig{
Email: "[email protected]",
HTTPClient: hardenedClient, // nil → stdlib 30s client
})
if err != nil {
return err
}
composite := &verify.CompositeResolver{
Resolvers: []verify.KeyResolver{embedded, wkd},
RequireAll: true, // fail-closed on a child error
Logger: slog.New(handler),
}
trust, err := composite.Resolve(ctx)
The same semantics apply: agreement is mandatory (mismatch always aborts), and
RequireAll governs only the response to a child error. To inspect the WKD URLs
a resolver will query, use verify.WKDURLs(email).
See also¶
- The trust model — composite agreement, fail-open/closed, the RSA-3072 policy.
- Verify a release — using the resulting
TrustSet. - The full API and runnable
Exampletests: pkg.go.dev/gitlab.com/phpboyscout/signing/verify.