Skip to content

Resolve your first artefact

By the end of this you will have a verified file on disk, fetched from the live channel, and you will have watched the verification refuse a tampered copy.

It takes about ten minutes. Everything here runs against the public channel, so no credentials are needed.

Before you start

You need Go 1.26.6 or newer:

go version

Step 1 — a module to work in

mkdir artefact-tutorial && cd artefact-tutorial
go mod init example.com/artefact-tutorial
GOPRIVATE='gitlab.com/phpboyscout/*' go get gitlab.com/phpboyscout/go/artifacts

Step 2 — get the signing key

Verification needs a key compiled into your program. Download the channel's public key and keep it beside your source:

curl -fsSL -o release.asc \
  https://gitlab.com/phpboyscout/artifacts/-/raw/main/keys/artifacts-release-signing.asc

Check you got the key you expected before you trust it:

gpg --show-keys release.asc

The fingerprint must read 544E64F3 B87561D5 67393336 34A711C4 B9EAA99A. If it does not, stop — you have someone else's key, and everything below would happily verify against it.

Step 3 — resolve something

Create main.go:

package main

import (
    "context"
    "fmt"
    "log"
    _ "embed"

    "gitlab.com/phpboyscout/go/artifacts"
    "gitlab.com/phpboyscout/go/artifacts/trust"
)

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

func main() {
    verifier, err := trust.Estate(releaseKey)
    if err != nil {
        log.Fatal(err)
    }

    dir, err := artifacts.UserCacheDir()
    if err != nil {
        log.Fatal(err)
    }

    client, err := artifacts.New(verifier,
        artifacts.WithCache(artifacts.NewDirCache(dir)))
    if err != nil {
        log.Fatal(err)
    }

    ref := artifacts.Ref{Name: "onnxruntime", Version: "1.28.0"}

    // Ask what this version contains before downloading any of it.
    files, err := client.Manifest(context.Background(), ref)
    if err != nil {
        log.Fatal(err)
    }

    for name := range files {
        fmt.Println(name)
    }
}

Run it:

go run .

You should see the files published for that version — one archive per platform, plus the manifest itself. Nothing has been downloaded yet beyond the manifest and its signature.

If this fails with a verification error

The most likely cause is the WKD lookup. trust.Estate requires the embedded key and the key published at openpgpkey.phpboyscout.uk to agree, and it fails closed when it cannot reach the second one. On a network that intercepts TLS or blocks the host, that is a refusal by design — see The trust model.

Step 4 — fetch a file

Pick the archive matching your platform and resolve it. Replace the Println loop with:

    path, err := client.Resolve(context.Background(), ref,
        "onnxruntime-linux-x64-1.28.0.tgz")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("verified and cached at:", path)

Run it again. The first run downloads; the second returns immediately from the cache.

go run .
go run .

Step 5 — ask for something that was never published

Change the filename to a platform the channel does not carry:

    path, err := client.Resolve(context.Background(), ref,
        "onnxruntime-osx-x64-1.28.0.tgz")
go run .
artifacts: not published: onnxruntime-osx-x64-1.28.0.tgz is not in onnxruntime@1.28.0's manifest

That answer comes from the signed manifest, not from the server's response code. The manifest is the authoritative list of what a version contains, so a file missing from it does not exist however the URL behaves — and a channel that served a 200 with a plausible-looking archive at that path would still get this answer.

The example is real rather than contrived: upstream dropped macOS Intel builds between ONNX Runtime 1.23.0 and 1.26.0, so 1.28.0 genuinely has no such file.

Match it with errors.Is:

    if errors.Is(err, artifacts.ErrNotFound) {
        // no build for this platform, or a version nobody approved
    }

A note on the cache

If you edit a file in the cache, the next resolve detects it and fetches the artefact again. Entries are re-hashed against a digest from a freshly verified manifest, so an edited copy is replaced rather than returned.

That means a cache hit still talks to the network — it saves the artefact download, not the round trip. See Choose a cache location for what that means in a firewalled environment.

What you built

A program that fetches an artefact only after establishing who published it, stores it somewhere reusable, and refuses bytes that do not match what was signed.

Next