omniborId and swhid are two extra identity fields on a CycloneDX 1.6 component. Each holds a content-derived identifier, an OmniBOR artifact ID or a Software Heritage persistent identifier, and both are arrays. Most projects can leave them out and rely on purl, but they help when names and versions are unreliable.
What the schema says
The 1.6 JSON reference lists several ways to assert identity: purl, cpe, swid, omniborId and swhid. For omniborId, the spec says to assert identity using the OmniBOR Artifact ID, conforming to the gitoid URI scheme registered with IANA. Its examples look like this.
gitoid:blob:sha1:a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
gitoid:blob:sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
For swhid, the spec says to assert identity using the Software Heritage persistent identifier, and each field is an array of strings, so a component can have several.
SPDX 2.3 has a matching idea. Its external reference example uses a PERSISTENT-ID category with type swh and a locator such as swh:1:cnt followed by a hash.
How they differ from a purl
| Identifier | Based on | Good at |
|---|---|---|
| purl | Ecosystem, name and version | Matching packages to registry and vulnerability data |
| cpe | Product naming scheme | Matching some vulnerability databases |
| omniborId | Content of the artifact | Showing two artifacts are bit-identical |
| swhid | Archived content or revision | Pointing at a permanent copy in an archive |
A purl says what something is called. A content identifier says what the bytes are. If a package is renamed, forked or republished under a new name, the purl changes while the content identifier stays the same.
When they are worth adding
When you vendor or fork code. A copied file has no registry, so no purl. A content identifier lets a later reader link it back to its origin.
When you need to prove which artifact you shipped. A content identifier is an anchor that does not depend on a registry still existing.
When you analyze binaries. If a tool found a component by its contents, recording the content identifier shows how.
When a registry might remove a package. An archive identifier keeps a pointer to a copy.
When to skip them
For ordinary package manager dependencies, a purl and a hash already do the job, and the extra identifiers add maintenance without much benefit. Add them if your tooling emits them for free, but do not build a process around them.
Pair with evidence
CycloneDX evidence identity can name omniborId or swhid as the identity field being substantiated, along with the technique that produced it. That way readers see both the identifier and how it was derived.
Check before sharing
Validate the format. A gitoid with the wrong hash length, or a swhid with a missing prefix, will fail strict parsers. Compare against the examples in the spec and test on a sample file first.
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.
Quick check
Are omniborId and swhid required?
No. The only required component fields in the 1.6 schema are type and name.
Do these replace purl?
No. They identify content, while a purl identifies a package in an ecosystem. They answer different questions.
Sources
Last reviewed: 19 October 2026.