Package Licensing
This section is technical documentation, not a legal opinion. For binding legal decisions, especially publication, redistribution, relicensing, or registry policy questions, consult a qualified lawyer.
Package licensing is distinct from repository approval. Repository approval is planned as a policy check over the licenses found in repository files. Package licensing records and validates the outbound package-level declaration in Project.toml and LICENSE. ReuseLicensing does not infer that declaration from the file-level license set, nor does it decide whether the declared package license is legally compatible with every file-level license.
Package-Level Declarations
REUSE records licensing information at file level. This is the correct foundation for source-code compliance, because different files in the same repository may have different copyright holders, licenses, or exceptions.
A Julia package, however, is also distributed and consumed as a combined work. For that purpose, tooling often needs a package-level license declaration: the license expression under which the package is offered as a package, assuming the generated or declared package composition is used.
In ReuseLicensing, this package-level declaration is separate from file-level licensing information. It does not replace SPDX headers, REUSE.toml, or the license texts under LICENSES/. Instead, it records the outbound package license expression that package tooling, registries, documentation, and users may inspect directly.
A package-level declaration should therefore answer a different question from file-level REUSE metadata:
- file-level licensing narrowly answers: under which license is this file available?
- package-level licensing answers: under which license is this package offered as a combined work?
Package-level declarations are intentionally not treated as a substitute for legal compatibility analysis. ReuseLicensing can parse and validate SPDX license expressions and apply explicit approval policies, but it does not infer that an arbitrary combination of files, dependencies, and licenses forms a legally valid redistributable whole.
Root LICENSE File
For repositories generated or adopted by ReuseLicensing, the root LICENSE file is expected to contain a package-level license declaration followed by the corresponding license texts and, where needed, exception texts. License texts used for file-level licensing remain available under LICENSES/ in accordance with REUSE.
Concretely, ReuseLicensing expects LICENSE to start as shown in the following example:
Copyright © ...
This package is made available under the following package-level outbound license
expression:
EUPL-1.2+The LICENSE file expected and produced by ReuseLicensing will have the following overall structure:
COPYRIGHT NOTICE
PACKAGE-LEVEL LICENSE EXPRESSION
EXPLANATORY TEXT
---
LICENSE AND EXCEPTION TEXTS (as referenced by the package-level license expression)To avoid ambiguity, ReuseLicensing rejects additional root-level license files such as COPYING, COPYRIGHT, or LICENSE.txt.
To avoid inconsistencies, package-level declarations should be changed through ReuseLicensing or ReuseLicensing-aware tooling. An already adopted package can change its package license expression with set_package_license! and its package copyright notice with set_package_copyright!. See Adoption of Legacy REUSE-compliant Packages for migration into the ReuseLicensing layout.
For example, an adopted package can change its package-level license expression without editing Project.toml or LICENSE by hand:
set_package_license!(
".",
"EUPL-1.2+";
license_policy = OSIApproved(),
)Project.toml Metadata
ReuseLicensing records package-level licensing information in a dedicated [reuse_licensing] table in Project.toml. This table is intended to make the package-level declaration machine-readable without duplicating file-level REUSE metadata.
For example, ReuseLicensing itself records the following package-level metadata:
name = "ReuseLicensing"
uuid = ...
...
[reuse_licensing]
reuse_specification_version = "3.3"
spdx_license_list_version = "3.28.0"
package_license_expression = "EUPL-1.2+"
package_copyright_notice = "Copyright © 2026 Guido Wolf Reichert and contributors"
package_license_file = "LICENSE"The first two entries record the specification and license-list versions that formed the semantic frame when the metadata was created or last updated. This matters because validation depends not only on license texts, but also on the known SPDX identifiers, exception identifiers, approval information, and deprecation status available in a particular SPDX License List version.
The remaining entries record the package-level outbound SPDX license expression, the copyright notice used for the package-level declaration, and the root file that contains the human-readable package-level declaration with the corresponding license and exception texts.
This metadata is not a validation result. It does not state that the repository is currently compliant, that all files have been checked, or that the declared package composition is legally redistributable. Validation results should be recomputed from the repository contents and the selected policy.
Use the functions listed in the API Reference to change package-level declarations or adopt a package into the ReuseLicensing layout. This keeps the [reuse_licensing] table and root LICENSE file coherent.
Adoption of Legacy REUSE-compliant Packages
Existing Julia packages can be adopted into the package-level licensing layout expected by ReuseLicensing when they are already REUSE-compliant and do not yet contain a [reuse_licensing] table in Project.toml.
Adoption is intentionally conservative. ReuseLicensing does not infer the outbound package license expression from the file-level license set. The caller must provide the package-level SPDX license expression and the package-level copyright information explicitly:
adopt_package_licensing!(
".";
package_license = "EUPL-1.2+",
year = "2026",
copyright_holders = ["Example Author", "contributors"],
)The function checks REUSE compliance, verifies the package license expression against the selected license_policy, appends the generated [reuse_licensing] table to Project.toml, and writes the canonical root LICENSE file. If a root LICENSE file already exists, adoption fails unless force = true is passed. This is deliberate: replacing an existing package-level license file should be an explicit decision.
Adoption is not a repository repair operation. It does not rewrite README licensing sections, change file-level SPDX notices, or decide whether the chosen package-level declaration is legally compatible with every file-level license.
README Licensing Sections
ReuseLicensing does not treat README licensing sections as authoritative package-level declarations. The authoritative package-level declaration is the combination of [reuse_licensing] metadata in Project.toml and the canonical root LICENSE file.
README licensing sections may summarize or point to these files, but ReuseLicensing does not rewrite them when package-level licensing metadata is adopted or changed. This avoids duplicating license expressions or copyright notices in user-editable prose.
Release Evidence
Package-level metadata records the declared licensing frame, but it is not a snapshot of every dependency resolution a user may create and eventually distribute. For releases, ReuseLicensing can store additional licensing evidence under .licensing/.
Manifest snapshots are stored by package version and Julia host triplet:
.licensing/manifests/vX.Y.Z/<filename>.tomlOptional evidence profiles are stored below the package version:
.licensing/manifests/vX.Y.Z/<profile>/<filename>.tomlwhere <filename> is derived from a filename-safe normalization of the current Julia host triplet:
replace(Base.BinaryPlatforms.host_triplet(), r"_version\+" => "-") * ".toml"Profile names are user-defined labels for selected dependency resolutions. They are normalized to lowercase filename-safe path components. This can be used for advertised environments such as plotting, gpu, full, or extensions/FooExt, without implying that every possible extension, weak dependency, preference, artifact, load path, or local modification has been assessed.
These manifest snapshots document the Julia dependency resolution considered when a package-level licensing assessment was made. They are evidence for that assessment, not license grants for third-party dependencies and not guarantees that other Julia versions, platforms, dependency resolutions, extensions, artifacts, load paths, or local modifications are compatible with the same package-level license declaration.
ReuseLicensing offers the convenience function record_manifest_evidence! to store the current Manifest.toml.
For exampled:
record_manifest_evidence!("."; force = true)
record_manifest_evidence!("."; profile = "extensions/FooExt", force = true)API Reference
ReuseLicensing.check_package_licensing — Function
check_package_licensing(root; license_policy = ValidSPDX(), multiprocessing = true)Check package-level licensing metadata and return a PackageLicensingCheck.
The check is non-mutating. It validates the Julia package metadata, the [reuse_licensing] table in Project.toml, the package-level LICENSE preamble, ambiguous root license-file names, the recorded package license expression under license_policy, and repository-level REUSE compliance.
Use has_valid_package_licensing for a boolean result when diagnostic information is not needed.
Keyword Arguments
license_policy: Approval policy used to check the recorded package license expression.multiprocessing: Whether the externalreusetool may use multiprocessing while checking repository-level REUSE compliance.
ReuseLicensing.has_valid_package_licensing — Function
has_valid_package_licensing(root; license_policy = ValidSPDX(), multiprocessing = true)Return whether package-level licensing metadata are valid for root.
This is a boolean convenience wrapper around check_package_licensing. Use check_package_licensing directly when diagnostic information is needed.
Keyword Arguments
license_policy: Approval policy used to check the recorded package license expression.multiprocessing: Whether the externalreusetool may use multiprocessing while checking repository-level REUSE compliance.
ReuseLicensing.set_package_license! — Function
set_package_license!(root, package_license; license_policy = ValidSPDX(),
multiprocessing = true)Set the package-level license expression for an already adopted package.
The function validates the package licensing setup, preserves the existing package copyright notice, writes updated [reuse_licensing] metadata to Project.toml, and renders the package-level LICENSE file.
Keyword Arguments
license_policy::AbstractExpressionApprovalPolicy: Approval policy used to check the new package license expression before writing metadata.license_ref_dir::Union{AbstractString, Nothing}: Optional directory containing user-supplied license texts forLicenseRef-...identifiers.multiprocessing::Bool: Whether the externalreusetool may use multiprocessing while checking repository-level REUSE compliance. Defaults totrue.
ReuseLicensing.set_package_copyright! — Function
set_package_copyright!(root; year, copyright_holders,
multiprocessing = true)Set the package-level copyright notice for an already adopted package.
The function validates the package licensing setup, requires existing [reuse_licensing] metadata, verifies that the first line of the root LICENSE matches the currently recorded package copyright notice, updates package_copyright_notice in Project.toml, and replaces only the first line of LICENSE.
The package license expression and license texts are not changed.
Keyword Arguments
year::AbstractString: Copyright year string. It must contain at least one four-digit year.copyright_holders::Vector{<:AbstractString}: Copyright holders used to construct the package copyright notice.multiprocessing::Bool: Whether the externalreusetool may use multiprocessing while checking repository-level REUSE compliance. Defaults totrue.
ReuseLicensing.adopt_package_licensing! — Function
adopt_package_licensing!(root; package_license, year, copyright_holders,
license_policy = ValidSPDX(),
license_ref_dir = nothing,
force = false,
multiprocessing = true)Adopt package-level licensing metadata and canonical package-level LICENSE for an already REUSE-compliant Julia package.
The function first checks that the package is REUSE-compliant. It then requires that Project.toml does not yet contain [reuse_licensing] metadata, constructs a package copyright notice from year and copyright_holders, appends canonical [reuse_licensing] metadata to Project.toml, and renders the canonical package-level LICENSE file.
If LICENSE already exists, the function throws unless force = true.
Keyword Arguments
package_license::AbstractString: Package-level outbound SPDX license expression to record inProject.tomland render intoLICENSE.year::AbstractString: Copyright year string. It must contain at least one four-digit year.copyright_holders::Vector{<:AbstractString}: Copyright holders used to construct the package copyright notice.license_policy::AbstractExpressionApprovalPolicy: Approval policy used to check the package license expression before writing metadata.license_ref_dir::Union{AbstractString, Nothing}: Optional directory containing user-supplied license texts forLicenseRef-...identifiers.force::Bool: Allow replacing an existing rootLICENSEfile. Defaults tofalse.multiprocessing::Bool: Whether the externalreusetool may use multiprocessing while checking repository-level REUSE compliance. Defaults totrue.
ReuseLicensing.record_manifest_evidence! — Function
record_manifest_evidence!(root; profile = nothing, force = false, readme = false)Copy Manifest.toml from root to the release-scoped licensing evidence directory .licensing/manifests/vX.Y.Z/, where X.Y.Z is read from Project.toml.
The destination filename is derived from the current Julia host triplet. The function verifies that the manifest's recorded julia_version matches the Julia version running this process. If profile is provided, it is normalized to lowercase filename-safe path components below the version directory. Existing evidence files are not overwritten unless force = true. If readme = true, the function also creates .licensing/README.md when it does not already exist.
Returns the destination path.