# go-licenses turns a Go module graph into a redistribution notice

> go-licenses is an Apache-2.0 licensed Google tool that walks a Go package or binary's dependency tree, reports each library with a versioned license URL, and can save every license document, copyright notice and source file into a directory for redistribution. It ships two commands, report and save, and warns that non-Go code in a dependency cannot be inspected for further dependencies.

**google/go-licenses** — A lightweight tool to report on the licenses used by a Go package and its dependencies. Highlight! Versioned external URL to licenses can be found at the same time.

- Repository: https://github.com/google/go-licenses
- Stars: 1,020 · Forks: 140
- Language: Go
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/google-go-licenses

## A module graph is a licence graph, and this walks it

go-licenses analyzes the dependency tree of a Go package or binary, and it can output a report on the libraries used and under what license they can be used. It can also collect all of the license documents, copyright notices and source code into a directory in order to comply with license terms on redistribution.

The second capability is the one that is legally load bearing. Producing a third party notices file is not a report, it is an obligation under most permissive licences, and doing it by hand against a growing module graph is exactly the task that quietly stops being done. The save command handles it.

The unit of reporting is defined precisely, which matters for interpreting the output. A library is considered to be one or more Go packages that share a license file. That is why the report lists sub-packages of the same module separately, for example github.com/google/licenseclassifier and github.com/google/licenseclassifier/stringclassifier appear as two rows with the same commit but different licence files, and github.com/jbenet/go-context/io is a row of its own with an MIT licence file. A module can therefore contribute several licences to a single report, and a report row is a package, not a repository.

The URLs are versioned based on go modules metadata, so each row links to the licence file at the exact commit the build used rather than at the tip of a branch. The examples show both forms, a GitHub blob URL pinned to a commit hash or a tag, and a cs.opensource.google URL for the golang.org/x modules.

The tool is Apache-2.0 licensed, and the README opens with a line worth repeating, that this is not an officially supported Google product.

## Two commands, and stdout is the report

The installation is a single go install, and the README includes a useful migration note for people arriving from an older workflow.

```shell
go install github.com/google/go-licenses/v2@latest
```

If you were using go get to install this tool, note that starting in Go 1.17, go get is deprecated for installing binaries. The module path carries a v2 suffix, so the import path and the install path are versioned in the usual Go way.

The report command prints a comma-separated report to stdout and writes info, warnings and errors to stderr.

```shell
go-licenses report github.com/google/go-licenses > licenses.csv
```

And the version that also captures the diagnostics.

```shell
go-licenses report github.com/google/go-licenses > licenses.csv 2> errors
```

Separating the report from the logs is the right design, because it means the same command works in a pipeline where the CSV is consumed by another tool and the warnings are consumed by a human. The tip to save the CSV to a file appears in the README with that reasoning.

The output format is three columns, the library path, the URL where the licence can be viewed, and the licence type, so a downstream script can check the third column for a value it does not want to ship. The report also supports a custom template, invoked with the --template flag pointing at a .tpl file.

The other command is save, and its output is a directory rather than a stream. The repository layout shows the code split by function, with check.go, csv.go, report.go and save.go at the top level and main.go and e2e_test.go alongside them, plus a testdata/ directory holding the example templates.

## The warnings are the part you should not skip

The example output in the README is more useful than it looks, because it is dominated by warnings rather than by report rows, and those warnings describe a real limitation.

The recurring message is that a package contains non-Go code that can't be inspected for further dependencies. The examples are assembly files, unix/asm_linux_amd64.s in golang.org/x/sys/unix, fe_amd64.s in golang.org/x/crypto/curve25519/internal/field, sum_amd64.s in golang.org/x/crypto/internal/poly1305, and the Darwin equivalents in the second example. The consequence is specific. When a dependency is a cgo package, the Go tool can follow its imports, but when it is assembly or a native library, the tool cannot see what that native code links against, so a licence obligation hidden in a C library or a third party shared object is not found.

The README says plainly that some warnings and errors may be expected and refers to a Warnings and Errors section, so the tool does not hide this.

The second warning is about versions. The module github.com/google/go-licenses has empty version, defaults to HEAD, and the licence URL may be incorrect, please verify. That happens when the tool is asked to report on itself, or on a module built from a working tree, and the licence URL then points at HEAD rather than at a pinned commit. For a module you did not build from source, the URLs are versioned and the warning does not appear.

Both warnings are the sort of thing that is easy to filter out of a log and expensive to have filtered out. A licence review that ignores the non-Go code warning is a licence review with a hole in it, and the hole is exactly where a cgo dependency pulls in a GPL library.

The library detection mechanism is worth noting too, since it explains the tool's accuracy. The dependency list includes github.com/google/licenseclassifier/v2 v2.0.0 and github.com/google/licenseclassifier/stringclassifier, so the licence type in the third column comes from classifying the licence text rather than from reading a metadata field, which is why a package with an unusual or dual licence can be identified at all.

## The container bakes the notices into the image

The Dockerfile shows a workflow that is more useful than a build recipe, because it uses the tool to produce an artefact as part of the image build.

The build stage is a golang:1.18 image. Dependencies are downloaded first, in a step the comment marks as cacheable, with the go.mod and go.sum copied before the repository itself, which is the standard layering trick for a Go build. The repository is then copied, the build is checked with go build ./..., the tests are run with go test -v ./..., and the binary is installed with go install .

Then the step that matters for the tool's own purpose.

```dockerfile
# Save licenses, etc.
RUN go run . save . --save_path /THIRD_PARTY_NOTICES
```

The image generates the third party notices from the module graph as part of the build, so the notices cannot drift from the dependencies. The final stage copies the binary and the generated directory into a distroless base image and sets the entrypoint.

That is a pattern worth stealing regardless of whether you adopt the tool. Generating the notices in the same build that compiles the binary means the file describes exactly the code that shipped, and it removes the separate human step where someone regenerates it before a release and sometimes forgets.

One thing to notice is the mismatch between the Dockerfile's golang:1.18 and the go.mod, which declares go 1.23.0. The module requires a newer Go than the image provides, so the container build as written would not compile the current source. The README separately states that you need Go v1.16 or later installed, which is a floor for the tool as used, and the go.mod is the more accurate statement of what the source needs. A team building their own image should align those two.

## Where go-licenses is the wrong tool

The first wrong case is when you need a licence compatibility decision. The tool reports what each dependency is licensed under and where the text lives. It does not tell you whether Apache-2.0 in your project and GPL in a transitive dependency can be shipped together in a container image, and it does not consider the distribution model, whether that is an internal service, a binary, a device or a hosted offering. That reasoning is a lawyer's job with a legal team's sign-off, and no output format from a Go tool changes that.

The second wrong case is a dependency with native code. The warning about non-Go code that cannot be inspected is the tool telling you its own coverage limit. A project whose dependencies include substantial cgo, for example a database driver with a bundled library or a compression library, will get a report that is incomplete in exactly the region where the risk is highest.

The third wrong case is a project that is not Go. The tool walks a Go module graph and it uses the go command's own dependency resolution. For a JavaScript, Python or Rust project the equivalent work is a different tool, and the licence files it collects are collected by Go module path.

The fourth wrong case is a monorepo with vendored or replaced dependencies. The report is derived from the module graph, so a replace directive pointing at a local directory, or a vendored tree, changes what the tool sees. The tool handles the module path, but a legal review of a shipped artefact needs to cover what actually got compiled into the binary, and that is the artefact the save command collects, which is the part to trust.

## How it differs from a manual licence file

The comparison worth making is with the standard practice, which is a checked-in THIRD_PARTY_NOTICES or LICENSES file that a developer maintains when they add a dependency.

That approach has one advantage over any tool, it is explicit and readable, and a reviewer can see the whole inventory in one place without running anything. Its disadvantage is that it is a snapshot. It is correct on the day it was written and it is wrong the moment someone adds a dependency and forgets to update it, and nothing in the build fails because of it. The failure mode is silent, which is the worst kind for a legal obligation.

go-licenses inverts that. The report is derived from the module graph at the moment you run it, so it is correct by construction, and the save command produces the actual licence texts and notices rather than a list of names.

The second difference is the granularity. A manual notices file is usually organised by licence type, a section for MIT, a section for Apache, a section for BSD, each with a list of copyright holders. The go-licenses report is organised by package, with a versioned URL and a licence type per row, so the mapping from dependency to obligation is one to one. That is a better structure for an audit because a reviewer can ask about a specific package and get a specific answer, and it is a worse structure for a human who wants to skim the whole set of obligations at a glance.

The third difference is the maintenance burden. A manual file is cheap to keep if the project has three dependencies and a disciplined maintainer. It becomes a tax at two hundred, and the discipline requirement is the part that does not scale. A generated file is free to maintain, and the cost moves to running the tool in the build, which is the trade the Dockerfile makes explicitly.

## Conclusion

go-licenses fits a team shipping a Go binary who has to produce a third party notices file for redistribution and who needs the license inventory to be regenerated from the module graph rather than maintained by hand. It does not fit a team that needs legal analysis, since the tool classifies licenses and collects texts but does not tell you whether a combination of licences is compatible with your distribution model, and it does not cover the parts of a dependency that are not Go code. Before adopting it, install it with go install github.com/google/go-licenses/v2@latest, run the report against your binary and read the warnings rather than ignoring them, since assembly and other non-Go files are explicitly not inspected, and note that the current release is v2.0.1 from 2025-09-08 with the last push on 2026-07-09.

## FAQ

### How do I install go-licenses?

Run go install github.com/google/go-licenses/v2@latest. The README notes that if you were using go get to install the tool, go get is deprecated for installing binaries starting in Go 1.17, and that you need Go v1.16 or later installed.

### What does the go-licenses report output look like?

It is a comma-separated report on stdout with the library path, the URL where the licence can be viewed and the licence type, while info, warnings and errors go to stderr. A library is one or more Go packages that share a licence file, and the URLs are versioned based on go modules metadata.

### How do I save the license documents and notices for redistribution?

Use the save command with a save_path, for example go run . save . --save_path /THIRD_PARTY_NOTICES, which is what the repository Dockerfile does. It collects the licence documents, copyright notices and source code into that directory so the redistribution requirements can be met.

### What do the warnings from go-licenses mean?

The recurring warning says a package contains non-Go code that can't be inspected for further dependencies, with assembly files as the examples. That means a cgo or native dependency can hide a licence obligation the tool cannot see. The README says some warnings and errors may be expected and refers to its Warnings and Errors section.

### Is go-licenses an officially supported Google product?

No, the README states at the top that this is not an officially supported Google product. The tool is Apache-2.0 licensed, the current release is v2.0.1 from 2025-09-08, and the last push to master was on 2026-07-09.

## Sources

- [google/go-licenses on GitHub](https://github.com/google/go-licenses)
- [Issues](https://github.com/google/go-licenses/issues)
- [License: Apache-2.0](https://github.com/google/go-licenses/blob/master/LICENSE)
- [README](https://github.com/google/go-licenses/blob/master/README.md)
- [Releases](https://github.com/google/go-licenses/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/google-go-licenses
