In SPDX 2.3, the DESCRIBES relationship connects the document itself to the package or files it is about. The spec calls it a logical relationship that is mandatory when more than one package or set of files is present. It tells a reader which element is the top of the tree, so everything else makes sense.
What the spec says
SPDX 2.3.1 defines DESCRIBES for use when SPDXRef-DOCUMENT describes another element. Its example is a document WildFly.spdx describing the package WildFly. The spec notes this is a logical relationship that helps organize related items within a document, and that it is mandatory if more than one package or set of files, not in a package, is present. The reverse, DESCRIBED_BY, states the same thing from the other side.
SPDXID: SPDXRef-DOCUMENT
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-myapp
Relationship: SPDXRef-Package-myapp DEPENDS_ON SPDXRef-Package-lodash
Why it matters
Without it, a document with fifty packages looks like fifty equal items. A reader cannot tell which one is your product and which are things it uses. Tools that compute what you ship, or that walk the tree for a vulnerability search, need an entry point.
In CycloneDX, the same job is done by the root component in the metadata section. If you convert between formats, make sure the root survives. An SPDX file with no DESCRIBES and a CycloneDX file with no metadata component both lose their entry point.
How to use it well
| Case | What to write |
|---|---|
| One application | DESCRIBES the application package, which then has DEPENDS_ON edges |
| A container image | DESCRIBES the image package, with CONTAINS or DEPENDS_ON for what is inside |
| Several deliverables in one file | DESCRIBES each, one line per deliverable |
| A library with no consumer | DESCRIBES the library itself |
Make the described package the one your customer would name. If you ship a product called Atlas, DESCRIBES should point at Atlas, not at an internal build package.
Common mistakes
No DESCRIBES in a multi-package file. This breaks the mandatory rule from the spec.
Describing everything. If the document DESCRIBES every dependency, the root is lost again. Use DESCRIBES for what the document is about, and DEPENDS_ON for the rest.
Pointing at a missing identifier. The target SPDXID must exist in the same document.
Reversed direction. DESCRIBED_BY with the arguments swapped produces the opposite claim.
A quick check
Search the file for DESCRIBES. You should find at least one, and the target should be your product. Follow the DEPENDS_ON edges from it, and confirm that the packages you expect appear within a few hops. If large groups of packages cannot be reached from the root, the graph is incomplete or disconnected, and any impact search will miss them.
Safeguard can generate SBOMs and run dependency scanning across your repositories, so the fields discussed here are checked on real projects instead of by hand. You can try the free scan at gold.safeguard.sh/scan.
Try it yourself
Open the demo, click through the component list, and look at how each package is identified and what is missing.
Click through it at your own pace, or open it full screen.
Quick check
Is DESCRIBES always required?
The spec says it is mandatory if more than one package or set of files not in a package is present.
What is the CycloneDX equivalent?
The root component in metadata plays a similar role, naming what the BOM is about.
Sources
Last reviewed: 19 October 2026.