permits

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 19, 2026 License: EPL-2.0 Imports: 9 Imported by: 0

README

permits

CI codecov Go Reference

permits collects the full license text of every dependency your project resolves. Instead of trusting the license field in a manifest, it fetches the actual LICENSE and NOTICE files from wherever the package is published and works out the SPDX identifier from the file contents.

It supports npm and Go. Almost all of the behavior lives in the library; the CLI is a small wrapper around it.

Every file is stored exactly as published. If the text doesn't match a known license it's still saved, just without an SPDX id. Detection uses google/licensecheck and runs on the file contents, so one file can produce several ids (a combined MIT/Apache-2.0 LICENSE, for example).

permits also recognizes the source-available licenses licensecheck doesn't cover: FSL-1.1-MIT, FSL-1.1-ALv2, BUSL-1.1, and Elastic-2.0. For an FSL file it reports the current grant, not the future license embedded in the text.

It looks on disk before going to the network:

Ecosystem Scanned file License source
npm pnpm-lock.yaml local node_modules, then the npm registry tarball
Go go.sum local module cache, then the Go module proxy

If a package is already installed (npm in node_modules, including pnpm's .pnpm store, or Go in the module cache), that copy is used and the network is skipped. A package that exists locally but has no license file is taken at face value: permits records "no license" rather than falling back to the registry.

By default it scans the whole dependency graph, transitive dependencies included. -direct limits it to your direct dependencies: the pnpm importers/root deps, and go.mod requires not marked // indirect.

Install

make build      # produces ./permits
# or
go build -o permits ./cmd/permits

CLI

permits \
  -pnpm-lock ./pnpm-lock.yaml \
  -go-sum ./go.sum \
  -out ./licenses
Flag Default Meaning
-pnpm-lock path to a pnpm-lock.yaml (repeatable)
-go-sum path to a go.sum (repeatable)
-out ./licenses output directory
-concurrency 8 parallel fetch workers
-goproxy env GOPROXY override Go proxy list
-npm-registry npmjs.org override npm registry base URL
-node-modules lock sibling node_modules root checked before the registry (repeatable)
-timeout 30s per-request timeout
-direct false only resolve direct (top-level) deps, excluding transitive
-strict false exit non-zero if any dependency yields no license
-v false verbose progress logging

Exit codes: 0 on success, 1 if a dependency failed (or -strict and something had no license), 2 on a usage or I/O error.

Output

permits writes a summary.json and one Markdown file per license file:

licenses/
  summary.json
  npm/@monetr/notify/1.0.4/EPL-2.0.md
  npm/react/19.2.6/MIT.md
  go/golang.org/x/mod/v0.17.0/BSD-3-Clause.md
  go/gopkg.in/yaml.v3/v3.0.1/Apache-2.0.md   # NOTICE -> single SPDX
  go/gopkg.in/yaml.v3/v3.0.1/LICENSE.md      # dual MIT/Apache -> original name

Paths are <ecosystem>/<name>/<version>/<file>.md. Scoped npm names (@scope/pkg) and Go module paths (host.com/x/y) become nested directories. The filename is the SPDX id when exactly one is detected; otherwise it's the original in-package filename, so dual-licensed or unrecognized files keep their LICENSE.md or NOTICE.md. Repeated names get a -2, -3 suffix, and ., .., or path separators inside a segment become _.

Each file is YAML frontmatter followed by the original license text:

---
name: react
version: 18.2.0
ecosystem: npm
declaredLicense: "MIT"
spdx: ["MIT"]
licenseFile: "LICENSE"
source: npm-tarball
sha256: <hex>
retrievedAt: 2026-05-18T00:00:00Z
---

<verbatim license text, emitted exactly as published>

Library

The CLI just calls the library:

import (
	permits "github.com/monetr/permits"
	"github.com/monetr/permits/output"
)

opts := permits.Options{Concurrency: 8}
c := permits.NewCollector(permits.DefaultRegistry(opts), opts)

summary, artifacts, err := c.Collect(ctx, "pnpm-lock.yaml", "go.sum")
// summary and artifacts are ready to use; writing files is optional.
_ = output.Write("./licenses", summary)

artifacts is a flat slice of model.LicenseArtifact including the raw Text, so you can use permits without writing anything to disk.

Adding an ecosystem

Implement provider.Scanner and provider.Fetcher and register them; the collector doesn't change:

reg := permits.DefaultRegistry(opts) // or provider.NewRegistry()
reg.Register(myCargoScanner, myCargoFetcher)
c := permits.NewCollector(reg, opts)

A Scanner parses a lockfile into []model.Dependency; a Fetcher turns one model.Dependency into []model.LicenseArtifact. provider/npm and provider/gomod are working examples to copy.

Development

make test       # go test ./...
make lint       # go vet + gofmt check

Documentation

Overview

Package permits gathers the verbatim license text of every dependency a project resolves. It scans lockfiles (pnpm-lock.yaml, go.sum) and retrieves raw license files from the npm registry and the Go module cache/proxy.

The library is usable on its own: build a Collector and call Collector.Collect. The command in ./cmd/permits is a thin wrapper around this package. New dependency ecosystems are added by registering a provider.Scanner and provider.Fetcher in a *provider.Registry — no changes to the collector.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultRegistry

func DefaultRegistry(opts Options) *provider.Registry

DefaultRegistry returns a registry with the built-in npm and Go providers wired up using opts.

Types

type Collector

type Collector struct {
	// contains filtered or unexported fields
}

Collector orchestrates scanning and fetching against a registry.

func NewCollector

func NewCollector(reg *provider.Registry, opts Options) *Collector

NewCollector returns a Collector backed by reg. Register additional providers in reg before calling to support more ecosystems.

func (*Collector) Collect

func (c *Collector) Collect(ctx context.Context, files ...string) (model.Summary, []model.LicenseArtifact, error)

Collect scans every input file into a deduplicated dependency set, fetches license artifacts concurrently, and returns the run model.Summary together with the flat list of every artifact. A per-dependency failure does not abort the run; it is recorded in the model.Summary.

type Options

type Options struct {
	// Concurrency is the number of dependencies fetched in parallel (min 1).
	Concurrency int
	// Timeout is the per-request timeout for network fetches.
	Timeout time.Duration
	// Strict, when true, makes the run report failure if any dependency yields
	// no license. The collector still returns full results; callers decide.
	Strict bool
	// DirectOnly restricts scanning to direct (top-level) dependencies,
	// excluding transitive ones. Requires each matched scanner to implement
	// [provider.DirectScanner]; otherwise [Collector.Collect] returns an
	// error.
	DirectOnly bool
	// NpmRegistry overrides the npm registry base URL.
	NpmRegistry string
	// NodeModulesDirs are local node_modules roots checked before the npm
	// registry (cache-first, like the Go module cache).
	NodeModulesDirs []string
	// GoProxy overrides the GOPROXY list.
	GoProxy string
	// GoCacheDir overrides the Go module cache location.
	GoCacheDir string
	// Logf, if set, receives progress messages.
	Logf func(format string, args ...any)
}

Options configures a collection run and the default providers.

Directories

Path Synopsis
cmd
permits command
Command permits scans pnpm-lock.yaml and go.sum files and gathers the verbatim license text of every resolved dependency from the npm registry and the Go module cache/proxy.
Command permits scans pnpm-lock.yaml and go.sum files and gathers the verbatim license text of every resolved dependency from the npm registry and the Go module cache/proxy.
internal
httpx
Package httpx is an internal helper: a shared HTTP client with a per-request timeout and bounded exponential-backoff retries on transient failures (5xx, 429, and network errors).
Package httpx is an internal helper: a shared HTTP client with a per-request timeout and bounded exponential-backoff retries on transient failures (5xx, 429, and network errors).
Package licenses provides ecosystem-agnostic detection of license files by filename.
Package licenses provides ecosystem-agnostic detection of license files by filename.
Package model holds the public data types shared across the permits library: the dependencies discovered in lockfiles, the raw license artifacts gathered for them, and the run summary.
Package model holds the public data types shared across the permits library: the dependencies discovered in lockfiles, the raw license artifacts gathered for them, and the run summary.
Package output writes a collection run to disk: a machine-readable summary.json plus one Markdown file per license artifact, laid out as <ecosystem>/<name>/<version>/<spdx-or-original>.md with YAML frontmatter followed by the verbatim license text.
Package output writes a collection run to disk: a machine-readable summary.json plus one Markdown file per license artifact, laid out as <ecosystem>/<name>/<version>/<spdx-or-original>.md with YAML frontmatter followed by the verbatim license text.
Package provider defines the extension point of the permits library.
Package provider defines the extension point of the permits library.
gomod
Package gomod implements the permits provider for the Go module ecosystem: a Scanner for go.sum and a Fetcher that reads raw license text from the local module cache, falling back to the Go module proxy when the module is not cached.
Package gomod implements the permits provider for the Go module ecosystem: a Scanner for go.sum and a Fetcher that reads raw license text from the local module cache, falling back to the Go module proxy when the module is not cached.
npm
Package npm implements the permits provider for the npm ecosystem: a Scanner for pnpm-lock.yaml (lockfileVersion 5, 6 and 9) and a Fetcher that resolves raw license text from the package tarball published to the npm registry.
Package npm implements the permits provider for the npm ecosystem: a Scanner for pnpm-lock.yaml (lockfileVersion 5, 6 and 9) and a Fetcher that resolves raw license text from the package tarball published to the npm registry.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL