A SWID tag is a small XML file, defined by ISO/IEC 19770-2:2015, that identifies one release of a software product and is installed and removed along with it. CoSWID, defined by IETF RFC 9393 in June 2023, carries the same information in compact CBOR. Both identify software. Neither is a full SBOM, and CISA's 2026 minimum elements no longer list SWID as an SBOM format.
The tags did not disappear. They moved into narrower jobs: software asset management, endpoint inventory and remote attestation, where a compact signed identity record is what is needed. This guide covers each format field by field, how they map to each other and to SPDX and CycloneDX, what conversion loses, and a walkthrough you can run that validates every file against its published schema.
If you only need the policy answer, our shorter post on whether SWID is still an acceptable SBOM format covers it. This post goes into the structure of the tags themselves.
Key takeaways
- SWID (Software Identification) tags are XML documents defined by ISO/IEC 19770-2. The current text is the 2015 edition, which ISO lists as edition 2. A tag describes one software component, patch or installer, and its presence on a device is meant to track the presence of the software.
- CoSWID (Concise SWID) is RFC 9393, an IETF Standards Track document published in June 2023. It encodes the same implicit information model in CBOR with integer keys. The RFC reports a 50 to 85 percent size reduction in early experiments.
- RFC 9393 says a CoSWID must be signed with COSE (COSE_Sign1 or COSE_Sign). SWID uses XML Digital Signatures.
- A SWID or CoSWID tag identifies a product. It does not carry a dependency graph, license expressions, or vulnerability status the way an SPDX or CycloneDX SBOM does.
- CISA's 2026 minimum elements (published 29 July 2026) removed SWID from the list of SBOM data formats, saying SWID tags are "not a widely used SBOM data format for which multiple tools exist". The 2021 NTIA minimum elements had listed it next to SPDX and CycloneDX.
- Where tags still matter: software asset management and license reconciliation, endpoint posture checks through SWIMA (RFC 8412), and remote attestation, where the IETF CoRIM draft carries CoSWID tags next to hardware and firmware claims.
- Both SBOM standards can point to a SWID tag: CycloneDX has a
swidobject on every component, SPDX 2.3 has aswidexternal reference type, and SPDX 3.0.1 has aswidexternal identifier type.
What a SWID tag is
ISO/IEC 19770 is the ISO family of standards for IT asset management. Part 2 defines software identification tags. ISO's catalogue page lists ISO/IEC 19770-2:2015 as edition 2, published in October 2015, and as last reviewed and confirmed in 2021 (ISO's site blocks scripted clients, so we confirmed this through a search of the ISO pages; one ISO committee page variant shows the standard as under systematic review). An earlier 2009 edition existed: RFC 8412 has separate data model entries for "ISO 2015 SWID Tags Using XML" and "ISO 2009 SWID Tags Using XML". Anything you build today should target the 2015 text.
The core idea is a lifecycle rule, not a file format. RFC 9393 summarises what ISO/IEC 19770-2:2015 requires: when a software component is installed on an endpoint, its SWID tag is installed too; when the component is removed or replaced, the tag is deleted or replaced. The result is that the set of tags on a device should match the set of software installed on it. An inventory tool can then read tags instead of guessing from file names, registry keys or package databases.
NIST's SWID project page says NIST recommends adoption of the SWID standard by software producers, and that both the Trusted Computing Group and the IETF use SWID tags in their standards.
The practical guidance most implementers follow is NIST Internal Report 8060, "Guidelines for the Creation of Interoperable Software Identification (SWID) Tags", published in April 2016 by authors from NIST, MITRE and G2. It adds rules on top of the ISO text so that tags from different producers can be read the same way. NIST publishes a schema definition and a guideline summary spreadsheet with it.
The four tag types
RFC 9393 paraphrases the four types from ISO/IEC 19770-2:2015 and NISTIR 8060:
| Tag type | What it describes | When it exists | Flag that sets it |
|---|---|---|---|
| Primary | An installed software component | Installed with the software, removed with it | corpus, patch and supplemental all false |
| Patch | An installed patch that changes files but not the product version | Installed with the patch | patch = true |
| Corpus | An installable package or installer in its pre-installation state | Travels with installation media, not deployed to the target | corpus = true |
| Supplemental | Extra metadata about another tag, added without changing the original | Any time, by the vendor, an integrator or the user | supplemental = true |
RFC 9393 Section 3 sets an order for deciding the type: the first matching rule wins. A tag with all three flags false is primary. Then supplemental, then corpus, then patch. The RFC gives an example: a patch installer can have both corpus and patch set to true, and it is a corpus tag. The tag it installs on the endpoint has only patch set, so that one is a patch tag.
There are also co-constraints. patch and supplemental must not both be true. A patch tag must contain at least one link with rel set to patches and an href that points at the software it patches, otherwise the target cannot be identified. Primary and corpus tags must include a software version.
A patch changes files without changing the product version; an update changes the version and gets a new primary tag. RFC 9393 says patch must not be true if installing the package changes the version.
The lifecycle in one table
RFC 9393 Figure 1 shows which tags exist at each stage. Here is the same information as a table:
| Lifecycle stage | Tags added | Tags removed |
|---|---|---|
| Deployment (before install) | Corpus, supplemental | None |
| Installation | Primary, supplemental (patch tags possible) | None |
| Patching | Patch (primary and supplemental remain) | None |
| Upgrading | New primary, new supplemental | Old primary, old supplemental, old patch |
| Removal | None | Primary, supplemental |
Corpus tags are not deployed on the target system. They support deployment, for example to check installation media before it is used.
What ISO/IEC 19770-2:2015 defines in the XML
The root element is SoftwareIdentity. The XML schema that ISO publishes for the 2015 edition makes two attributes required: name and tagId. Everything else is optional with defaults. version defaults to 0.0, versionScheme to multipartnumeric, tagVersion to 0, and the three type flags to false.
Child elements are Entity (at least one), and optionally Evidence, Link, Meta and Payload, plus an XML Digital Signature. Entity has a required name and role, and an optional regid that defaults to http://invalid.unavailable. That default exists because the schema needs a value, and the schema comments point to RFC 6761 for why invalid is a safe reserved name. A tag that leaves it in place tells you nothing about who wrote it.
Payload and Evidence share a resource collection: directories, files, processes and other resources. The difference is who wrote it. Payload comes from the software's producer and says what the software installs. Evidence is written by a discovery tool on the device and records why it believes the software is present. RFC 9393 makes the two mutually exclusive in a CoSWID.
One detail will trip up anyone validating XML tags. The schema file served at standards.iso.org/iso/19770/-2/2015/schema.xsd, as we fetched it on 23 October 2026, declares its target namespace as http://standards.iso.org/iso/19770/-2/2014-DIS/schema.xsd, the draft namespace, while its own comment and most published tags use the 2015 URL as the namespace. A tag written with the 2015 namespace fails validation against that file with a "no matching global declaration" error until you account for the mismatch. The walkthrough below shows the workaround.
What CoSWID changes
RFC 9393, written by authors from Fraunhofer SIT, the NSA, MITRE and NIST, states the problem in its abstract: SWID tags "can be too large for devices with network and storage constraints". A tag with only required fields is a few hundred bytes. A tag with many optional fields can be orders of magnitude larger.
CoSWID solves this in four ways.
- CBOR instead of XML. CBOR (RFC 8949) is a binary format with a JSON-like data model. The structure is defined in CDDL (RFC 8610), the schema language for CBOR.
- Integer keys. The 57 text labels of the vocabulary are mapped to small integers.
tag-idis 0,software-nameis 1,entityis 2, and so on. The RFC notes that the values themselves are often unchanged, but the scaffolding around them shrinks to 10 percent or less of its XML size. Early experiments showed 50 to 85 percent smaller tags overall. - Singleton optimisation. Many fields accept either one value or an array of two or more, so a single entity or link does not pay for array markers.
- No version number. Instead of a CoSWID version, new constructs get new integer indices through an IANA "CoSWID Items" registry. Decoders must accept and ignore text labels they do not recognise.
RFC 9393 also adds things ISO does not have. It gives explicit types to some attributes that the XML model leaves to an any-attribute catch-all, and it defines CDDL extension points ("sockets") on every major map so other specifications can add fields cleanly. The RFC itself warns that SWID and CoSWID are maintained by different standards bodies and that future revisions could cause the shared information model to diverge. It also says it does not define a formal mapping between the two formats.
Registrations that make CoSWID usable on the wire
| Registration | Value | Source |
|---|---|---|
| Media type | application/swid+cbor | RFC 9393 Section 6.3 |
| File extension | .coswid | RFC 9393 Section 6.3 |
| CoAP Content-Format ID | 258 | RFC 9393 Section 6.4 |
| CBOR tag | 1398229316 | RFC 9393 Section 6.5 |
| Magic bytes when tagged | da 53 57 49 44 | RFC 9393 Section 6.3 |
| URI schemes | swid: and swidpath: | RFC 9393 Section 6.6 |
| SWIMA data model | PEN 0, integer 2, "Concise Software Identifier (CoSWID)" | RFC 9393 Section 6.7 |
The CBOR tag number is not arbitrary. In hex, 1398229316 is 0x53574944, which is the ASCII bytes for "SWID". So a tagged CoSWID starts with da (a CBOR tag with a four byte number) followed by the letters S, W, I, D.
Signing
RFC 9393 Section 7 says a CoSWID must be signed to protect its integrity, and it defines how. The tag is wrapped in a COSE (RFC 9052) structure: COSE_Sign1 for a single signature, which must come from the tag creator, or COSE_Sign for several signatures, one of which must be the tag creator's. The protected header must carry the algorithm identifier and the content type application/swid+cbor. A COSE countersignature (RFC 9338) may be added in the unprotected header so a second party can show a signature existed at a given time.
The security considerations explain why. A tag is "authoritative" when it comes from the software provider. A signed authoritative tag whose signature validates can be trusted not to have changed since signing. An unsigned tag can be altered by any process with write access to it. How you bind the right key to the right provider is left to the application.
The XML side uses XML Digital Signatures (XMLDSIG), which the ISO schema imports.
Field by field: SWID and CoSWID
The table below lists the fields that matter most, with the CoSWID integer index, the SWID XML equivalent, and what the field means. The indices and mappings come from RFC 9393 Sections 2.3 to 2.9; the XML attribute names and defaults come from the ISO 2015 schema.
| CoSWID name (index) | SWID XML | Required | Meaning |
|---|---|---|---|
| tag-id (0) | SoftwareIdentity/@tagId | Yes | Globally unique tag identifier. Text, or 16 bytes that must be a valid UUID. Text must not contain two underscores in a row. |
| software-name (1) | @name | Yes | Product name, usually what a package manager would show |
| entity (2) | Entity | Yes, one or more | Organisations and their roles |
| evidence (3) | Evidence | No | Discovery results written on the device. Excludes payload. |
| link (4) | Link | No | Relationship to another tag or resource |
| software-meta (5) | Meta | No | Open key and value metadata |
| payload (6) | Payload | No | Files, directories, processes the producer says the software installs. Excludes evidence. |
| hash (7) | namespaced hash attribute | No | [hash-alg-id, bytes] with an IANA Named Information algorithm ID |
| corpus (8) | @corpus | No, default false | Installer or package, pre-installation |
| patch (9) | @patch | No, default false | Installed patch |
| media (10) | @media | No | Target platform hint as a W3C media query |
| supplemental (11) | @supplemental | No, default false | Adds data to another tag |
| tag-version (12) | @tagVersion | Yes in CoSWID, default 0 in SWID | Revision of the tag itself, not the software |
| software-version (13) | @version | Conditional | Release version. Required for primary and corpus tags. |
| version-scheme (14) | @versionScheme | No | 1 multipartnumeric, 2 multipartnumeric+suffix, 3 alphanumeric, 4 decimal, 16384 semver |
| lang (15) | xml:lang | No | Language of text values |
tag-version deserves a word. It lets the producer fix a wrong tag (a broken link, a missing payload entry) without implying that the software changed. A new revision must have a higher value than the old one.
Entities and roles
An entity-entry has entity-name (31), reg-id (32), role (33) and thumbprint (34). Registered roles are:
| Role | Index | Rule in RFC 9393 |
|---|---|---|
| tag-creator | 1 | Must appear in every CoSWID |
| software-creator | 2 | Should appear when known |
| aggregator | 3 | Optional |
| distributor | 4 | Optional |
| licensor | 5 | Optional |
| maintainer | 6 | Optional |
reg-id names the naming authority, usually the organisation. RFC 9393 says it must be a URI and is not meant to be dereferenced. thumbprint is a hash of the signer's public key certificate, which ties the entity to the signature on the tag.
The reg-id of the tag creator also builds the Software Identifier used in SWIMA: TAG_CREATOR_REGID followed by two underscores and the tag-id. That is why tag-id may not contain a double underscore, and why changing how you write reg-id changes the identifier.
Links
A link-entry has a required href (38) and rel (40), plus optional artifact (37), media (10), ownership (39), media-type (41) and use (42). Registered rel values are ancestor (1), component (2), feature (3), installationmedia (4), packageinstaller (5), parent (6), patches (7), requires (8), see-also (9), supersedes (10) and supplemental (11). A text rel must be a private use name or a relation name from the IANA Link Relation Types registry.
href can be a normal URL, a relative path, a swid: URI that names another tag by its tag-id, or a swidpath: URI that holds an XPath query, kept for SWID compatibility. RFC 9393 says it does not define how to resolve XPath against CBOR.
The component and requires relations are the closest SWID gets to a dependency graph. They point from one tag to another. There is no document level structure that holds the whole graph in one place, which is the main structural difference from an SBOM.
Software metadata
software-meta carries indices 43 to 57, from activation-status to unspsc-version. Many of them (channel type, entitlement key, entitlement data required) are about licensing, which shows where SWID came from: the NTIA 2021 report notes that SWID tags "were designed, in part, to convey information around commercial licenses".
Payload and evidence
Both use directory (16), file (17), process (18) and resource (19). Files carry fs-name (24), location (23), root (25), size (20), file-version (21), key (22, marks files that prove the software is installed) and hash (7). Evidence adds date (35) and device-id (36).
The hash field is where CoSWID tightened the SWID model. ISO SWID tags have no way to name the hash algorithm in that attribute. CoSWID requires an algorithm ID from the IANA Named Information Hash Algorithm Registry with status "current", and uses 0 when the algorithm is unknown, which exists to allow conversion from ISO tags.
Walkthrough: build, validate and encode a tag
This walkthrough uses a fictional product, Acme Agent 2.4.1, published by example.com. Every file here was run and validated on 23 October 2026. You need curl, xmllint and Python 3 with the cbor2 and jsonschema packages.
Step 1: set up a working folder
mkdir swid-lab && cd swid-lab
python3 -m venv .venv && . .venv/bin/activate
pip install cbor2 jsonschema
curl -sSL -o swid-2015.xsd https://standards.iso.org/iso/19770/-2/2015/schema.xsd
Step 2: write a primary SWID tag
Save this as example.com_acme-agent-2.4.1.swidtag:
<?xml version="1.0" encoding="utf-8"?>
<SoftwareIdentity
xmlns="http://standards.iso.org/iso/19770/-2/2015/schema.xsd"
xmlns:SHA256="http://www.w3.org/2001/04/xmlenc#sha256"
name="Acme Agent"
tagId="example.com/acme-agent@2.4.1"
tagVersion="0"
version="2.4.1"
versionScheme="semver">
<Entity name="Acme Example Corp" regid="example.com" role="tagCreator softwareCreator"/>
<Link rel="license" href="https://example.com/acme-agent/LICENSE"/>
<Meta product="Acme Agent" colloquialVersion="2" summary="Endpoint inventory agent."/>
<Payload>
<Directory root="%programdata%" name="acme-agent">
<File name="acme-agent.exe" size="532712"
SHA256:hash="a314fc2dc663ae7a6b6bc6787594057396e6b3f569cd50fd5ddb4d1bbafd2b6a"/>
</Directory>
</Payload>
</SoftwareIdentity>
The one Entity holds two roles, separated by a space. All three type flags are left at their default of false, so this is a primary tag.
Step 3: validate it against the ISO schema
Because of the namespace mismatch described earlier, validate a copy with the namespace rewritten to the one the schema file declares. Do not ship the rewritten copy.
sed 's#iso/19770/-2/2015/schema.xsd"#iso/19770/-2/2014-DIS/schema.xsd"#' \
example.com_acme-agent-2.4.1.swidtag > /tmp/check.swidtag
xmllint --noout --schema swid-2015.xsd /tmp/check.swidtag
# /tmp/check.swidtag validates
Without the rewrite, xmllint reports that there is no matching global declaration for the root element. xmllint will fetch the XMLDSIG and xml.xsd schemas the ISO schema imports, so it needs network access the first time.
For deeper checks, NIST publishes swidval in its open source usnistgov/swid-tools repository. According to its README, it validates SWID and CoSWID tags against ISO/IEC 19770-2:2015, the IETF CoSWID specification and NISTIR 8060. The same repository has swid-builder, a Java API that produces tags in XML and CBOR, and swid-maven-plugin, which generates a tag as part of a Maven build. The README links to the CoSWID Internet-Draft rather than to RFC 9393, so check the version you build against.
Step 4: encode it as a CoSWID
The script below maps the XML attributes to RFC 9393 integer indices and writes a tagged CBOR file. It handles only the elements used in this example; it is a teaching aid, not a general converter.
"""Map a minimal ISO/IEC 19770-2 SWID tag to an RFC 9393 CoSWID (CBOR)."""
import sys, xml.etree.ElementTree as ET
import cbor2
NS = {"swid": "http://standards.iso.org/iso/19770/-2/2015/schema.xsd"}
SHA256_ATTR = "{http://www.w3.org/2001/04/xmlenc#sha256}hash"
ROLES = {"tagCreator": 1, "softwareCreator": 2, "aggregator": 3,
"distributor": 4, "licensor": 5, "maintainer": 6}
SCHEMES = {"multipartnumeric": 1, "multipartnumeric+suffix": 2,
"alphanumeric": 3, "decimal": 4, "semver": 16384}
REL = {"ancestor": 1, "component": 2, "feature": 3, "installationmedia": 4,
"packageinstaller": 5, "parent": 6, "patches": 7, "requires": 8,
"see-also": 9, "supersedes": 10, "supplemental": 11}
def one_or_more(items):
return items[0] if len(items) == 1 else items
root = ET.parse(sys.argv[1]).getroot()
tag = {
0: root.get("tagId"), # tag-id
12: int(root.get("tagVersion", "0")), # tag-version
1: root.get("name"), # software-name
13: root.get("version", "0.0"), # software-version
14: SCHEMES[root.get("versionScheme", "multipartnumeric")],
}
for flag, idx in (("corpus", 8), ("patch", 9), ("supplemental", 11)):
if root.get(flag) == "true":
tag[idx] = True
entities = []
for e in root.findall("swid:Entity", NS):
ent = {31: e.get("name"), 33: one_or_more([ROLES[r] for r in e.get("role").split()])}
if e.get("regid"):
regid = e.get("regid")
ent[32] = regid if "://" in regid else "https://" + regid # reg-id must be a URI
entities.append(ent)
tag[2] = one_or_more(entities)
links = [{38: l.get("href"), 40: REL.get(l.get("rel"), l.get("rel"))}
for l in root.findall("swid:Link", NS)]
if links:
tag[4] = one_or_more(links)
META = {"product": 52, "colloquialVersion": 45, "summary": 55}
metas = [{META[k]: v for k, v in m.attrib.items() if k in META}
for m in root.findall("swid:Meta", NS)]
if metas:
tag[5] = one_or_more(metas)
payload = root.find("swid:Payload", NS)
if payload is not None:
dirs = []
for d in payload.findall("swid:Directory", NS):
files = []
for f in d.findall("swid:File", NS):
fe = {24: f.get("name")}
if f.get("size"):
fe[20] = int(f.get("size"))
if f.get(SHA256_ATTR):
fe[7] = [1, bytes.fromhex(f.get(SHA256_ATTR))] # 1 = sha-256
files.append(fe)
dirs.append({24: d.get("name"), 25: d.get("root"), 26: {17: one_or_more(files)}})
tag[6] = {16: one_or_more(dirs)}
untagged = cbor2.dumps(tag)
tagged = cbor2.dumps(cbor2.CBORTag(1398229316, tag))
open(sys.argv[2], "wb").write(tagged)
print("XML bytes:", len(open(sys.argv[1], "rb").read()))
print("CoSWID bytes (untagged):", len(untagged))
print("CoSWID bytes (tagged):", len(tagged))
print("first five bytes:", tagged[:5].hex(" "))
Run it:
python swid2coswid.py example.com_acme-agent-2.4.1.swidtag acme-agent.coswid
# XML bytes: 820
# CoSWID bytes (untagged): 306
# CoSWID bytes (tagged): 311
# first five bytes: da 53 57 49 44
The CBOR file is about 62 percent smaller than the XML, which sits inside the 50 to 85 percent range RFC 9393 reports. The first five bytes match the magic number registered for application/swid+cbor. Decoding the file shows the integer keyed map:
{0: 'example.com/acme-agent@2.4.1',
1: 'Acme Agent',
2: {31: 'Acme Example Corp', 32: 'https://example.com', 33: [1, 2]},
4: {38: 'https://example.com/acme-agent/LICENSE', 40: 'license'},
5: {45: '2', 52: 'Acme Agent', 55: 'Endpoint inventory agent.'},
6: {16: {24: 'acme-agent', 25: '%programdata%',
26: {17: {7: [1, b'...32 bytes...'], 20: 532712, 24: 'acme-agent.exe'}}}},
12: 0,
13: '2.4.1',
14: 16384}
Three things in that output are judgement calls a real converter has to make and document:
reg-id. The XML saysexample.com. RFC 9393 requires a URI, so the script writeshttps://example.com. Another converter might writehttp://example.comor aurn:. Since the SWIMA Software Identifier is built from the tag creator'sreg-id, two converters can produce different identifiers for the same product.relas text.licenseis not in the CoSWID link relation registry, so it stays as text. RFC 9393 allows that becauselicenseis a registered IANA link relation name.- Signing. The output is unsigned. RFC 9393 says a CoSWID must be signed, so a real pipeline wraps this map in COSE_Sign1 with the producer's key before distribution. A signature on the XML tag does not carry over, because XMLDSIG signs the XML bytes and those bytes no longer exist.
If you work in Go, the veraison/swid package, part of the Veraison project, is described in its repository as a "SWID and CoSWID manipulation library". Its README links to the CoSWID Internet-Draft, so confirm which revision of the index assignments it implements before you rely on it for RFC 9393 output.
Step 5: reference the tag from a CycloneDX SBOM
CycloneDX components carry a swid object. In both the 1.6 and 1.7 JSON schemas it has required tagId and name, and optional version (default 0.0), tagVersion (default 0), patch (default false), text (an attachment holding the tag itself) and url. The schema describes each field as mapping to the matching attribute of a SWID SoftwareIdentity. Save this as acme-agent.cdx.json:
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-10-23T09:00:00Z",
"component": {
"type": "application",
"bom-ref": "acme-agent@2.4.1",
"name": "acme-agent",
"version": "2.4.1",
"supplier": { "name": "Acme Example Corp" },
"swid": {
"tagId": "example.com/acme-agent@2.4.1",
"name": "Acme Agent",
"version": "2.4.1",
"tagVersion": 0,
"patch": false,
"url": "https://example.com/tags/example.com_acme-agent-2.4.1.swidtag"
}
}
},
"components": [
{
"type": "library",
"bom-ref": "pkg:npm/lodash@4.17.21",
"name": "lodash",
"version": "4.17.21",
"purl": "pkg:npm/lodash@4.17.21",
"licenses": [ { "license": { "id": "MIT" } } ]
}
],
"dependencies": [
{ "ref": "acme-agent@2.4.1", "dependsOn": [ "pkg:npm/lodash@4.17.21" ] },
{ "ref": "pkg:npm/lodash@4.17.21", "dependsOn": [] }
]
}
This document validates against the CycloneDX 1.6 JSON schema, and against the 1.7 schema when specVersion is changed to 1.7. To check it yourself, download bom-1.6.schema.json, spdx.schema.json, jsf-0.82.schema.json and cryptography-defs.schema.json from the schema folder of the CycloneDX specification repository, or use the official CycloneDX CLI's validate command. Removing tagId from the swid object makes validation fail with "'tagId' is a required property", which is a quick way to prove your validator is actually reading the schema.
Notice what the SBOM adds: a dependency on lodash, a package URL, a license ID, and a dependency graph. The SWID tag has none of those for the library, because a SWID tag describes one product, not its contents.
Step 6: reference the tag from an SPDX 2.3 document
SPDX 2.3 Annex F defines a swid external reference type in the SECURITY category. Its locator is a swid: URI, and the spec notes that a binary tag-id should be base64url encoded.
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "acme-agent-2.4.1",
"documentNamespace": "https://example.com/spdx/acme-agent-2.4.1-3e671687",
"creationInfo": {
"created": "2026-10-23T09:00:00Z",
"creators": [ "Organization: Acme Example Corp" ]
},
"packages": [
{
"SPDXID": "SPDXRef-acme-agent",
"name": "acme-agent",
"versionInfo": "2.4.1",
"supplier": "Organization: Acme Example Corp",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceType": "swid",
"referenceLocator": "swid:example.com/acme-agent@2.4.1"
}
]
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-acme-agent"
}
]
}
This validates against the SPDX 2.3 JSON schema published in the spdx/spdx-spec repository (the schema file is titled SPDX 2.3.1). In SPDX 3.0.1 the same idea moves to the ExternalIdentifierType vocabulary, whose swid entry is defined as a CoSWID tag "as defined in RFC 9393 Section 2.3".
Where SWID and CoSWID are used today
Each use below is tied to a primary source. Where we could not confirm current status, we say so.
Software asset management and license reconciliation
This is the original job. ISO/IEC 19770 is an IT asset management family, and many of the CoSWID metadata fields (activation status, channel type, entitlement key, entitlement data required) exist to reconcile installed software against licenses. If your organisation runs software asset management, tags installed by commercial products are still a cleaner signal than heuristics.
Endpoint inventory and posture: SWIMA
RFC 8412, "Software Inventory Message and Attributes (SWIMA) for PA-TNC", defines a standard way to collect a device's software inventory as part of network access posture checks. It registers data models for ISO 2015 and ISO 2009 SWID tags in XML, and RFC 9393 adds CoSWID as data model 2. In that setting a tag answers "is this exact product release present on this device", which is what network access control needs.
Vulnerability data and NVD
NIST's SWID project page says NIST "is working to incorporate SWID Tag data" into the National Vulnerability Database dataset and has incorporated SWID tag data into SCAP version 1.3. The NIST swid-tools README describes an experimental client that posts tags to the NVD and says the NVD is maintaining a repository of producer published SWID and CoSWID tags. We could not find a primary source showing that NVD vulnerability records today match on SWID tag IDs at any scale, and the CISA 2026 minimum elements name CPE and Package URL as the common identifiers. Treat SWID as a supplementary identifier for vulnerability matching, not the primary one.
Remote attestation: IETF RATS and CoRIM
RFC 9393 lists remote attestation as a core use: linking Reference Integrity Manifests to the event logs an attester produces. The IETF RATS working group's Concise Reference Integrity Manifest draft (draft-ietf-rats-corim-11, last updated 6 July 2026, still an Internet-Draft) defines a CoRIM as a bundle that carries CoMID tags for hardware and firmware modules, CoSWID tags for software, and CoTL tag lists. It also defines a CoMID to CoSWID linking triple. This is where CoSWID is most active: a verifier compares measured software against signed reference values, and CoSWID payload hashes are a natural carrier for those values. NIST's project page also notes that the Trusted Computing Group uses SWID tags in its standards.
Software updates for constrained devices: SUIT
The topic plan for this post listed the IETF SUIT manifest as a CoSWID user. We checked the current manifest draft (draft-ietf-suit-manifest-37, in the RFC Editor queue) and did not find CoSWID defined in it. We are not claiming a SUIT and CoSWID link.
SBOM programmes
This is where SWID has lost ground. The NTIA "Minimum Elements For a Software Bill of Materials" (12 July 2021) named three formats: SPDX, CycloneDX and SWID tags, and listed SWID tags among unique identifiers next to CPE and PURL. The CISA "2026 Minimum Elements for a Software Bill of Materials" (29 July 2026, co-sealed by many national agencies) records the change directly: remove SWID tags from the list of data formats, because they are not widely used as an SBOM format with multiple tools. It names SPDX and CycloneDX as the two widely used formats and says organisations should avoid accepting SBOMs for new software in deprecated format versions. Our post on whether an SBOM format is an official standard covers the ISO and Ecma status of those two.
Version and status timeline
| Date | Event | Source |
|---|---|---|
| Before 2015 | ISO/IEC 19770-2 first edition (cited by RFC 8412 as "ISO 2009 SWID Tags") | RFC 8412 |
| October 2015 | ISO/IEC 19770-2:2015, edition 2, published | ISO catalogue |
| 22 April 2016 | NISTIR 8060 published with schema and guideline summary | NIST CSRC |
| 5 February 2018 | NIST SWID Tagging project page created | NIST CSRC |
| 2021 | ISO/IEC 19770-2:2015 reviewed and confirmed | ISO catalogue |
| 12 July 2021 | NTIA minimum elements list SWID as one of three SBOM formats | NTIA |
| June 2023 | RFC 9393 (CoSWID) published, Standards Track | RFC Editor |
| 6 July 2026 | CoRIM draft 11 carries CoSWID tags | IETF Datatracker |
| 29 July 2026 | CISA 2026 minimum elements remove SWID from SBOM data formats | CISA |
Three undated entries complete the picture. RFC 8412 (SWIMA) registered SWID data models for software inventory exchange. SPDX 2.3 added a swid external reference type and SPDX 3.0.1 a swid external identifier type that points to RFC 9393. CycloneDX carries a swid object on components in both its 1.6 and 1.7 schemas. We did not confirm the release dates of those versions for this post, so they are left out of the table.
How SWID and CoSWID compare with SPDX and CycloneDX
For a broader comparison of the formats, see our post on SBOM standard formats compared. The table below focuses on the structural differences that decide what each one can do.
| Question | SWID (ISO/IEC 19770-2:2015) | CoSWID (RFC 9393) | SPDX (2.3 and 3.0.1) | CycloneDX (1.6 and 1.7) |
|---|---|---|---|---|
| Unit of description | One product, patch or installer | One product, patch or installer | A document describing packages, files, snippets and relationships | A BOM describing components, services, dependencies and more |
| Encoding | XML | CBOR (binary) | JSON, YAML, RDF and others; JSON-LD in 3.x | JSON, XML, Protocol Buffers |
| Schema language | XSD | CDDL | JSON Schema, RDF model | JSON Schema, XSD |
| Lifecycle rule | Tag installed and removed with software | Same as SWID | None | None |
| Dependency graph | Only through links between tags | Only through links between tags | Relationships | dependencies array |
| License data | Entitlement fields; no SPDX license expressions | Same | SPDX license expressions | SPDX IDs and expressions |
| Package identifiers | tag-id; no PURL field | tag-id; no PURL field | PURL, CPE, SWID references | PURL, CPE, SWID object |
| File hashes | Hash attribute without algorithm ID | IANA algorithm ID plus raw bytes | Checksums with named algorithms | Hashes with named algorithms |
| Signing | XMLDSIG | COSE, required by the RFC | External signing or attestations | JSF signature or external signing |
| Vulnerability and VEX | No | No | Security profile in 3.x | VEX and VDR in the same model |
| Who maintains it | ISO/IEC JTC 1 | IETF | Linux Foundation; ISO/IEC 5962:2021 for 2.2.1 | OWASP and Ecma (ECMA-424) |
| Listed as an SBOM format by CISA 2026 | No | No | Yes | Yes |
| Best fit | Installed software inventory, SAM | Constrained devices, attestation | SBOM exchange, licensing | SBOM exchange, security use cases |
The short version: SWID and CoSWID answer "what is this product, and is it installed here". SPDX and CycloneDX answer "what is this product made of". You can use the first inside the second, but not the other way round.
Can you convert between them, and what is lost?
There are four directions people ask about. None of them is lossless in general.
SWID XML to CoSWID
This is the closest to a clean mapping, because RFC 9393 was designed against the ISO schema and most names convert by changing CamelCase to kebab-case. The RFC still warns that it does not define the mapping and that the models may diverge. Known gaps:
- Hash algorithms. SWID file hashes do not name an algorithm. Unless the XML uses a namespaced attribute that implies one, as the SHA-256 namespace in our example does, the CoSWID must use algorithm ID 0 (unknown).
reg-id. SWID allows a bare domain; CoSWID requires a URI. You choose the rewrite rule.- Signatures. XMLDSIG does not survive. The converter, or the original producer, must sign the CoSWID with COSE. If the converter signs it, the signature says the converter vouches for it, not the vendor.
swidpathlinks. XPath queries are carried as text but RFC 9393 does not say how to resolve them in CBOR.
SWID or CoSWID to SPDX or CycloneDX
You can map identity: name, version, tag-id, producer entity, and file hashes from the payload. CycloneDX can embed the whole tag through swid.text. What you cannot get is information the tag never had: the libraries inside the product, their package URLs, their license expressions, and the dependency graph. A converter that turns a set of tags from one device into a CycloneDX BOM produces an inventory of products, not an SBOM of any one product. Several lossy details also apply:
tag-version,corpus,supplemental, andmediahave no direct field in either SBOM format, except that CycloneDX keepstagVersionandpatchin itsswidobject.- Evidence (process IDs, discovery dates, device IDs) is about one device, not about the software. It does not belong in a supplier SBOM.
- Entitlement and activation metadata have no standard home and usually should not be shared.
SPDX or CycloneDX to SWID or CoSWID
You can produce one primary tag for the top level product from the SBOM's root component. You can produce more tags for subcomponents, but they will not follow the SWID lifecycle rule, because nobody installs or removes a library tag with the library. The license expressions, relationships and vulnerability data in the SBOM are dropped. Unless the vendor signs the result, it is not an authoritative tag.
No official SPDX or CycloneDX tool we checked converts to or from SWID tags. The official CycloneDX tooling can carry a SWID reference in the swid field; the conversion logic is yours to write and document.
When should an organisation still accept or produce SWID or CoSWID tags?
Use this as a decision table.
| Your situation | Recommendation |
|---|---|
| A buyer asks for an SBOM for new software | Deliver SPDX or CycloneDX. CISA 2026 no longer lists SWID as an SBOM format. |
| A supplier sends only SWID tags as their "SBOM" | Accept them as identity records, and ask for SPDX or CycloneDX for component content. |
| You run software asset management or license reconciliation | Keep consuming installed SWID tags; they are what that standard was built for. |
| You ship software that installs on managed Windows or Linux fleets of enterprise customers | Consider shipping a primary SWID tag; it costs little and helps their inventory tools. |
| You build firmware or software for devices that do remote attestation | Look at CoSWID inside CoRIM, signed with COSE, as reference values. |
| You sell into a network access control or SWIMA based posture system | Produce SWID or CoSWID tags as that system expects. |
| You want one identifier for vulnerability matching | Use Package URL and CPE in your SBOM; add the SWID tag ID as an extra identifier. |
| You need to prove a tag is from the vendor | Require signed tags: XMLDSIG for SWID, COSE for CoSWID. |
The common thread: keep SWID and CoSWID for the jobs that need a per product identity record bound to an installation or a measurement. Use SBOM formats for composition. In an incident you query the SBOMs to find which products contain the vulnerable component, then installed tags or package data to find where those products run; our post on finding affected products fast with SBOMs covers the first half.
Where Safeguard fits
The fit here is indirect, and we want to be precise about it. Safeguard does not generate or validate SWID or CoSWID tags, and it does not convert them to other formats. What it does cover is the composition and integrity layers that sit next to them.
SBOM generation and analysis is generally available: Safeguard produces SPDX (JSON, XML, RDF) and CycloneDX (JSON, XML) SBOMs, offers more than 30 export formats including CSV, Excel and PDF, and scans continuously on push, on a schedule or on release. If a supplier sends SWID tags, you can turn them into a CycloneDX document with your own script, as in the walkthrough, and analyse SBOM files with safeguard scan --sbom <file>. The Third-Party Risk Manager, also generally available, lets you onboard vendors and request SBOMs, projects or products from them, which is the practical way to ask a SWID only supplier for SPDX or CycloneDX. CSAF and VEX upload is generally available for vendor exploitability statements. Attestation and signing (SLSA, Sigstore keyless signing with Fulcio and Rekor, in-toto and X.509) is generally available for your own build outputs. None of these replace a COSE signed CoSWID in an attestation flow; they solve the SBOM side of the same trust problem.
Common mistakes
- Treating a SWID tag as an SBOM. A primary tag identifies one product. It has no component list or dependency graph. If a contract says "SBOM", a folder of SWID tags does not meet the 2026 CISA baseline.
- Leaving
regidat its default.http://invalid.unavailableis a schema default, not an identity. It makes the Software Identifier useless. - Reusing a
tagIdacross versions. Tag IDs must be globally unique per release. Bump the tag ID for a new version; bumptagVersiononly when fixing the tag itself. - Marking an update as a patch. If the version changes, it is an upgrade with a new primary tag, not a patch tag.
- Patch tags without a
patcheslink. RFC 9393 requires one. Without it the patch cannot be tied to the software it modifies. - Validating against the wrong namespace. The ISO schema file declares the 2014 DIS namespace. Know that before you conclude your tags are invalid, and do not "fix" shipped tags to the draft namespace.
- Shipping unsigned CoSWIDs. RFC 9393 says a CoSWID must be signed. An unsigned tag can be edited by anything with write access.
- Converting and claiming authority. A tag you converted or generated is not authoritative. Record yourself as tag-creator, not the vendor.
- Assuming a library handles RFC 9393. Some open source libraries still link to the Internet-Draft. Check index assignments against the RFC.
FAQ
What is the difference between SWID and CoSWID?
SWID tags are XML documents defined by ISO/IEC 19770-2:2015. CoSWID tags, defined by RFC 9393, carry the same information model in CBOR with integer keys, which makes them much smaller. CoSWID also adds explicit typing, extension points, IANA registries for values, and a requirement to sign with COSE.
Is a SWID tag an SBOM?
No. A SWID tag identifies one software product, patch or installer. An SBOM lists the components inside a product and how they depend on each other. The NTIA 2021 minimum elements listed SWID as an SBOM data format; CISA's 2026 minimum elements removed it.
Is SWID still accepted for SBOMs in 2026?
CISA's 2026 minimum elements name SPDX and CycloneDX as the widely used formats and removed SWID from the list. Buyers may still accept SWID tags as identity data, but for SBOM delivery you should plan on SPDX or CycloneDX. Our SWID acceptability post has the details.
What are the four types of SWID tags?
Primary (installed software), patch (an installed patch that does not change the version), corpus (an installer or package before installation) and supplemental (extra metadata about another tag). Three boolean flags set the type, with a precedence rule when more than one is true.
What is RFC 9393?
RFC 9393, "Concise Software Identification Tags", is an IETF Standards Track document published in June 2023. It defines CoSWID, the application/swid+cbor media type, CBOR tag 1398229316, the swid: and swidpath: URI schemes, and how to sign CoSWIDs with COSE.
How do I validate a SWID tag?
Validate the XML against the ISO 2015 schema with a tool such as xmllint, keeping in mind that the schema file declares the 2014 DIS namespace. For checks against NISTIR 8060 guidance as well, NIST's open source swidval tool validates SWID and CoSWID tags.
Does CycloneDX support SWID tags?
Yes. CycloneDX components have a swid object with tagId, name, version, tagVersion, patch, text and url. tagId and name are required. The object is present in the 1.6 and 1.7 schemas.
Does SPDX support SWID tags?
Yes, as a reference. SPDX 2.3 has a swid external reference type in the SECURITY category with a swid: URI locator. SPDX 3.0.1 has a swid value in its external identifier type vocabulary that points to RFC 9393.
Fix it without waiting for upstream
A SWID tag or an SBOM only tells you what is installed or what a product contains; neither one ships a fixed build. Once a tag or an SBOM points you at a vulnerable component, the work is getting the fixed version built and deployed, which is where the steps below apply to the software you build yourself.
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
These demos show the SBOM and remediation side, not SWID tagging. Safeguard does not produce SWID or CoSWID tags.
The first demo is a real CLI session. You install the CLI, ask it to fix the lodash CVE in a repository, and it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, and runs npm install and npm test with your approval, then shows /scan .. The lodash component in the CycloneDX example above is the same kind of finding: once an SBOM names a vulnerable library, this is how the fix gets made.
Click through it at your own pace, or open it full screen.
The second demo sets up the runner on your own machine, approves it with a device code, and scans from the terminal. Teams that hold supplier tags and SBOMs on internal systems often want analysis to run locally, and this is the flow for that.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use. Once connected, you can ask about projects and vulnerabilities in plain language, for example which products contain a component that a supplier's SBOM just flagged.
Click through it at your own pace, or open it full screen.
To try it on your own code, download Safeguard or read the CLI documentation.
Quick check
A CoSWID has corpus and patch both set to true. What type of tag is it?
A corpus tag. RFC 9393 applies the first matching rule in the order primary, supplemental, corpus, patch, so a patch installer with both flags set is a corpus tag. The tag it installs on the endpoint, with only patch set, is a patch tag.
Why do the first bytes of a tagged CoSWID file read "SWID"?
The CBOR tag registered for CoSWID is 1398229316, which is 0x53574944 in hex, the ASCII codes for S, W, I and D. A tagged file starts with da 53 57 49 44, the magic number listed in the media type registration.
Sources
- RFC 9393: Concise Software Identification Tags (RFC Editor)
- RFC 8412: Software Inventory Message and Attributes (SWIMA) for PA-TNC (RFC Editor)
- ISO/IEC 19770-2:2015 catalogue page (ISO)
- ISO/IEC 19770-2:2015 XML schema (ISO standards maintenance portal)
- NIST IR 8060: Guidelines for the Creation of Interoperable SWID Tags (NIST CSRC)
- Software Identification (SWID) Tagging project (NIST CSRC)
- The Minimum Elements For a Software Bill of Materials, 2021 (NTIA)
- 2026 Minimum Elements for a Software Bill of Materials (CISA)
- CISA SBOM resources (CISA)
- CycloneDX 1.6 JSON schema (CycloneDX specification repository)
- CycloneDX 1.7 JSON schema (CycloneDX specification repository)
- SPDX 2.3.1 Annex F: External repository identifiers (SPDX)
- SPDX 2.3 JSON schema (spdx-spec repository)
- SPDX 3.0.1 ExternalIdentifierType vocabulary (SPDX)
- draft-ietf-rats-corim: Concise Reference Integrity Manifest (IETF Datatracker)
- draft-ietf-suit-manifest: SUIT Manifest (IETF Datatracker)
- usnistgov/swid-tools (NIST, GitHub)
- veraison/swid (Veraison project, GitHub)
Last reviewed: 23 October 2026. Standards and drafts change; check the linked specifications for the current text.