Specification

Semantic Versions 1.0.0

Software versions are a communication mechanism. This specification outlines a set of rules that dictate how software version numbers can be assigned, incremented, grouped, compared and matched.

Introduction

For this system to work, you MUST declare a public API. This MAY consist of documentation or be enforced by the software itself. Regardless, it is important that an API is clear and precise. Changes to your public API are communicated with specific increments to your software's Semantic Version.

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY and OPTIONAL in this document are to be interpreted as described in RFC 2119.

Versions

Given a semantic version MAJOR.MINOR.PATCH, increment the:

  • MAJOR version when you make incompatible API changes;
  • MINOR version when you add functionality in a backward compatible manner;
  • PATCH version when you make a fix for unintended behaviour.

Optional labels for a pre-release and build extend the format: MAJOR.MINOR.PATCH-PRERELEASE+BUILD.

  1. Software using semantic versions MUST declare a public API. This API could be declared in the code itself or exist strictly in documentation. However it is done, it SHOULD be precise and comprehensive.
  2. A normal version number MUST take the form X.Y.Z where X, Y and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: 1.9.01.10.01.11.0.
  3. Once a versioned package has been released, the contents of that version MUST NOT be modified. Any modifications MUST be released as a new version.
  4. Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
  5. Version 1.0.0 SHOULD be the first stable release and MUST define the public API.
  6. Patch version Z (x.y.Z | x > 0) MUST be incremented if only a fix for unintended behaviour is introduced. A fix is defined as an internal change that corrects incorrect behaviour.
  7. Minor version Y (x.Y.z | x > 0) MUST be incremented if new, backward compatible functionality is introduced to the public API. It MUST be incremented if any public API functionality is marked as deprecated. It MAY include patch level changes. Patch version MUST be reset to 0 when minor version is incremented.
  8. Major version X (X.y.z | X > 0) MUST be incremented if any backward incompatible changes are introduced to the public API. Patch and minor versions MUST be reset to 0 when major version is incremented.
  9. A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphens [0-9A-Za-z-], MUST NOT be empty, and numeric identifiers MUST NOT include leading zeroes. Pre-release versions have lower precedence than the associated normal version. Examples: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.
  10. A build MAY be denoted by appending a plus sign and a series of dot separated identifiers immediately following the patch or pre-release version. Identifiers MUST comprise only ASCII alphanumerics and hyphens [0-9A-Za-z-] and MUST NOT be empty. Unlike "build metadata" in later drafts, a build participates in precedence; it is the antithesis of a pre-release. Examples: 1.0.0+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.

Precedence

Precedence refers to how versions are compared to each other when ordered.

  1. Precedence MUST be calculated by separating the version into major, minor, patch, pre-release and build identifiers, in that order.
  2. Major, minor and patch are always compared numerically.1.0.0 < 2.0.0 < 2.1.0 < 2.1.1
  3. When major, minor and patch are equal, a pre-release version has lower precedence than a normal version.1.0.0-alpha < 1.0.0
  4. When major, minor, patch and pre-release are equal, a version with build metadata has higher precedence than one without it. Where SemVer 2.0.0 discards build metadata for ordering, Semantic Versions restores it: a build names a more specific artifact of that release, so it ranks above the plain release rather than below it like a pre-release. 1.0.0 < 1.0.0+build.1 < 1.0.0+build.2
  5. Precedence for two pre-release or build versions with the same major, minor and patch MUST be determined by comparing each dot separated identifier from left to right until a difference is found:
    1. Identifiers consisting of only digits are compared numerically.
    2. Identifiers with letters or hyphens are compared lexically in ASCII sort order.
    3. Numeric identifiers always have lower precedence than non-numeric identifiers.
    4. A larger set of pre-release (or build) fields has higher precedence than a smaller set, if all preceding identifiers are equal.
    1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0 < 1.0.0+build.1

Sets

A Set is a collection of versions. Sets are how the ecosystem actually reasons about software: “every version of this package”, “the 1.x line”, “what this range selects”. Semantic Versions defines them directly, so tools can talk about groups of versions, not just one at a time.

  1. A Set MUST contain a collection of semantic versions.
  2. All potential MAJOR, MINOR, PATCH and PRE-RELEASE versions MUST be interpreted as valid subsets of any set.
  3. A Set MAY be scoped to a context; such a Contextual Set MUST contain only versions sharing that context.
  4. Fixing any single identifier (MAJOR, MINOR, PATCH, PRE-RELEASE or BUILD) partitions a set into Subsets.
  5. A Set MAY be sorted according to version precedence.
  6. Sets MAY contain duplicate versions.
  7. Duplicate versions in a set MUST have unique build metadata.
  8. An Unordered Set imposes no ordering; an Ordered Set MUST be sorted by precedence.

Context

A version rarely travels alone; it is understood in context. A context is the identity a version belongs to: a package, module, image, crate, service or any other named artifact. It scopes a version with an @ separator (context@MAJOR.MINOR.PATCH-PRE+BUILD), so that where a bare 1.2.3 is ambiguous, app@1.2.3-canary.1+sha.1a8c… names a specific build of a specific thing.

Context is deliberately ecosystem-agnostic. Everything above (the version grammar, precedence, sets and ranges) makes no assumption about where a version lives. An ecosystem binds to the spec by qualifying a context with a source, written source:name, which names the origin or ecosystem. The same rules then apply identically everywhere:

npm:react@18.2.0 · pypi:flask@3.0.0 · gem:rails@7.1.3 · cargo:serde@1.0.203 · golang:golang.org/x/text@0.14.0

A source-qualified context is what the extensions build on, and it lines up with a Package-URL. Where an ecosystem already has a Package-URL type, use it as the source, so a reference maps onto a purl one-to-one. The same spec then covers JavaScript, Python, Ruby, Rust, Go, Java, .NET, PHP and container images without change:

Ecosystem Semantic Versions reference Package-URL
JavaScript · npm npm:react@18.2.0 pkg:npm/react@18.2.0
Python · PyPI pypi:flask@3.0.0 pkg:pypi/flask@3.0.0
Ruby · RubyGems gem:rails@7.1.3 pkg:gem/rails@7.1.3
Rust · Cargo cargo:serde@1.0.203 pkg:cargo/serde@1.0.203
Go · modules golang:golang.org/x/text@0.14.0 pkg:golang/golang.org/x/text@0.14.0
Java · Maven maven:com.google.guava/guava@33.0.0 pkg:maven/com.google.guava/guava@33.0.0
.NET · NuGet nuget:Newtonsoft.Json@13.0.3 pkg:nuget/Newtonsoft.Json@13.0.3
PHP · Composer composer:symfony/console@7.0.4 pkg:composer/symfony/console@7.0.4
Containers · OCI oci:alpine@3.20.0 pkg:oci/alpine@3.20.0

Namespaces carry across too: an npm scope or a Maven group is part of the name (npm:@vue/cli@5.0.8, maven:com.google.guava/guava@33.0.0), the same segment a purl puts in its namespace. A source is just a lowercase token, so an ecosystem without a Package-URL type (or a private origin) still fits: napi:sharp@0.33.0, internal:billing@2.4.0.

SubSets

Fixing any one coordinate of a version yields a subset of context@*. Every position (major, minor, patch, pre-release and build) defines its own family. The full set <context>@* is the union of all of them.

<context>@*: every version of a context, partitioned by coordinate

Major SubSet

  • 0.*.*
  • 1.*.*
  • 2.*.*
  • 3.*.*

Minor SubSet

  • *.0.*
  • *.1.*
  • *.2.*
  • *.3.*

Patch SubSet

  • *.*.0
  • *.*.1
  • *.*.2
  • *.*.3

Prerelease SubSet

  • *.*.*-0
  • *.*.*-1
  • *.*.*-2
  • *.*.*-3

Build SubSet

  • *.*.*+0
  • *.*.*+1
  • *.*.*+2
  • *.*.*+3

Ordered & unordered sets

The same members, two arrangements. An unordered set lists versions as they were encountered; an ordered set sorts them by precedence. Ordering is a property of the set, not of its members.

A Unordered
react@9.3.1
react@6.8.0
react@7.1.2
react@1.0.0
B Ordered
react@1.0.0
react@6.8.0
react@7.1.2
react@9.3.1

Contextual sets

A set scoped to a single context (react@*, express@*) is a contextual set: every version that shares that identity. Two contextual sets can hold identical version numbers and still be distinct, because their context differs.

A react@*
react@1.2.3
react@8.9.1
react@7.1.0-alpha.3
B express@*
express@1.2.3
express@8.9.1
express@7.1.0-alpha.3

Examples:

  • Valid set: 5.6.7, 3.2.1+build.123, 3.2.1, 4.0.0, 1.16.3, 0.9.8
  • Invalid set: 3.2.1, 3.2.1
  • Ordered set: 0.9.8, 1.16.3, 3.2.1, 3.2.1+build.123, 4.0.0, 5.6.7
  • Prerelease set: 1.0.0-pre.1, 1.0.0-pre.2, 1.0.0-pre.3
  • Build set: 1.0.0+build.1, 1.0.0+build.2, 1.0.0+build.3

Ranges

A range is a set of comparators which specify the versions that satisfy the range. A range is composed of one or more comparator sets, joined by ||. A version matches a range if and only if every comparator in at least one of the ||-separated comparator sets is satisfied by the version.

>=1.2.7 <1.3.0 matches 1.2.7, 1.2.8 and 1.2.99, but not 1.2.6, 1.3.0 or 1.1.0.
1.2.7 || >=1.2.9 <2.0.0 matches 1.2.7, 1.2.9 and 1.4.6, but not 1.2.8 or 2.0.0.

Operators

An operator expresses how to compare a version. Supported operators are:

  • < — Less than
  • > — Greater than
  • = — Equal to
  • <= — Less than or equal to
  • >= — Greater than or equal to

Comparators

A comparator is composed of an operator and a version. Comparators can be joined by whitespace to form a comparator set, satisfied by the intersection of all the comparators it includes.

  • >=1.2.7 matches 1.2.7, 1.2.8, 2.5.3 and 1.3.9, but not 1.2.6 or 1.1.0.
  • >1 is equivalent to >=2.0.0; it matches 2.0.0 and 3.1.0, but not 1.0.1 or 1.1.0.

Advanced range syntax

Advanced ranges desugar to primitive comparators in deterministic ways.

Hyphen ranges X.Y.Z - A.B.C

Specifies an inclusive set. If a partial version is given as the first version, missing pieces become zeroes; if given as the second, all versions starting with the supplied parts are accepted.

  • 1.2.3 - 2.3.4 := >=1.2.3 <=2.3.4
  • 1.2 - 2.3.4 := >=1.2.0 <=2.3.4
  • 1.2.3 - 2.3 := >=1.2.3 <2.4.0-0
  • 1.2.3 - 2 := >=1.2.3 <3.0.0-0

X-ranges 1.2.x 1.X 1.2.* *

Any of X, x or * may stand in for a numeric value in the [major, minor, patch] tuple. A partial version range is treated as an x-range, so the special character is optional.

  • * := >=0.0.0 (any stable version). Pre-releases opt in through a channel; *-* is the universal channel that admits every pre-release.
  • 1.x := >=1.0.0 <2.0.0-0
  • 1.2.x := >=1.2.0 <1.3.0-0
  • 1 := 1.x.x := >=1.0.0 <2.0.0-0
  • 1.2 := 1.2.x := >=1.2.0 <1.3.0-0

Tilde ranges ~1.2.3 ~1.2 ~1

Allows patch-level changes if a minor version is specified; minor-level changes if not.

  • ~1.2.3 := >=1.2.3 <1.3.0-0
  • ~1.2 := >=1.2.0 <1.3.0-0
  • ~1 := >=1.0.0 <2.0.0-0
  • ~0.2.3 := >=0.2.3 <0.3.0-0
  • ~1.2.3-beta := >=1.2.3-beta <1.3.0-0, a pre-release channel on 1.2.3.

Caret ranges ^1.2.3 ^0.2.5 ^0.0.4

Allows changes that do not modify the left-most non-zero element in the [major, minor, patch] tuple.

  • ^1.2.3 := >=1.2.3 <2.0.0-0
  • ^0.2.3 := >=0.2.3 <0.3.0-0
  • ^0.0.3 := >=0.0.3 <0.0.4-0
  • ^1.0.0-rc := >=1.0.0-rc <2.0.0-0, the rc channel of 1.0.0, plus stable 1.x.

Approximate ranges ~>

The pessimistic operator ~> treats the last supplied digit as the beginning of a range and matches only within that value. It is an alias of the tilde operator.

  • ~>1 := >=1.0.0 <2.0.0-0
  • ~>1.2 := >=1.2.0 <1.3.0-0
  • ~>1.2.3 := >=1.2.3 <1.3.0-0
Ranges may be non-contiguous. A version might be neither greater than, less than, nor a member of a range. For example, 1.2 <1.2.9 || >2.0.0 has a hole from 1.2.9 to 2.0.0. Use satisfies(version, range) to test membership directly, and try it on the range selector.

Pre-release channels ^1.0.0-rc

A pre-release is provisional: it ranks below its release (1.0.0-rc < 1.0.0), a step toward a version rather than a refinement of it. So pre-releases are excluded by default (a range of stable comparators never admits them), and a range opts in by naming the pre-release it wants. That name is a channel: a prefix filter scoped to the comparator's [major, minor, patch] tuple, exactly mirroring a build channel. There is no global “include pre-releases” switch; admission is always a property of the range.

  1. A range with no pre-release token anywhere admits stable versions only.
  2. A comparator, tilde or caret carrying a pre-release declares a channel whose prefix is its pre-release identifiers, scoped to its tuple. A pre-release candidate is admitted if and only if it is within the numeric bounds and its identifiers, compared left to right, begin with some declared channel's on the same tuple.
  3. Two explicit wildcards provide what a global flag used to. X.Y.Z-* is the empty-prefix channel for one tuple (any pre-release of that release), and *-* is the universal channel (pre-releases on every tuple, the in-range equivalent of npm's includePrerelease or pip's --pre).
  4. Stable versions are unaffected by channels: once inside the numeric bounds they are always admitted.
  5. A tilde or caret carrying a pre-release applies its channel to the numeric lower bound it desugars to. A hyphen range keeps each endpoint's pre-release as that endpoint's channel.
  • ^1.0.0-rc (prefix rc): matches 1.0.0-rc, 1.0.0-rc.1, 1.0.0-rc.2; never 1.0.0-beta (different channel) or 1.5.0-rc (different tuple). Stable 1.x still matches.
  • ^1.0.0-rc.2 (prefix rc.2): the rc.2 sub-tree only.
  • ^1.0.0-* := >=1.0.0-0 <2.0.0-0 + any-pre channel on 1.0.0: every pre-release of 1.0.0, plus stable 1.x.
  • *-*: any version at all, stable or pre-release.
Pre-release channels are a deliberate divergence from Semantic Versioning 2.0.0 and its node-semver implementation, which admit any pre-release sharing a comparator's tuple and expose a separate includePrerelease flag. Scoping to a prefix makes ^1.0.0-alpha mean the alpha channel (not “everything from alpha onward”) and folds the flag into the grammar, following NuGet's floating 1.2.3-alpha.* / *-* model. Try channels on the range selector (Release train sample).

Subset selection: one rule for pre-release and build

Pre-release and build are the two labels a version can carry, and a range selects among them with the same channel/prefix rule: identifiers on a comparator, matched left-to-right as a prefix, scoped to that comparator's tuple. They differ only in their default, and that difference follows directly from precedence.

Label Ranks vs. its release Default in a range Opt in / narrow with
Pre-release Below: provisional, on the way to the release (1.0.0-rc < 1.0.0) Excluded name it (-rc), -*, or *-*
Build At/above: a refinement, a specific artifact of the release (1.0.0 < 1.0.0+win) Included a channel narrows (+win)

A version is in a range when it passes every comparator's numeric test and, for whichever labels it carries, the range's channels admit them. Nothing is configured out-of-band.

Build channels ^1.0.0+win

Because build metadata carries precedence, it can also select. A build attached to a comparator, tilde or caret is a channel filter: it is orthogonal to the numeric comparison and admits only versions whose own build metadata begins with the channel's identifiers. This turns build metadata into a direct way to range over release channels and variants (signed builds, platform or architecture targets, content digests) that no registry-hosted numeric range can express.

  1. A comparator’s build metadata MUST be treated as a channel filter, not as part of the numeric comparison.
  2. The numeric comparison MUST ignore build metadata on both the comparator and the candidate version.
  3. A version satisfies a channel if and only if its build identifiers, compared left to right, begin with the channel’s identifiers.
  4. A range with no build channel matches any build (and the build-less release); a channel excludes versions that carry no build.
  5. A tilde or caret carrying a channel MUST apply that channel to every comparator it desugars to. A hyphen range spans two endpoints and MUST ignore their build metadata.
  • ^1.0.0+win := >=1.0.0+win <2.0.0-0+win: the win channel within ^1.0.0, so it matches 1.4.2+win.x64, not 1.4.2+linux.x64 or 1.4.2.
  • >=1.1.0+linux.x64: the linux.x64 variant at or above 1.1.0.
  • 1.0.0+sha.a1b2c3: pins the exact artifact (and any narrower build beneath it).
  • ^1.0.0: no channel, so every variant of every release in range matches, build or not.
Build channels are a deliberate divergence from Semantic Versioning 2.0.0, which discards build metadata during range resolution. See Build Variants for os/arch/abi channels and Package Integrity for a channel built on content digests, and try channels live on the range selector (Build variants sample).

Relationship to Semantic Versioning 2.0.0

Semantic Versions is a peer specification, not a fork or a claimed successor to the version line stewarded at semver.org. It carries its own version (1.0.0) and is a spiritual successor to Semantic Versioning 2.0.0-rc.1, the draft before 2.0.0-rc.2 demoted the build to inert "build metadata."

A version that is valid under Semantic Versioning 2.0.0 is valid here and parses identically. The differences are additive or concern the treatment of builds:

TopicSemantic Versioning 2.0.0Semantic Versions 1.0.0
Build precedence Ignored: two versions differing only in build are equal Participates in precedence, ranking above the plain release; a build is the antithesis of a pre-release
Build channels Build discarded during range resolution Build on a comparator/tilde/caret is a channel filter (^1.0.0+win)
Pre-release ranges Any pre-release on a comparator's tuple is admitted; opt in via a separate flag A pre-release is a prefix channel (^1.0.0-rc); wildcards -* / *-* replace the flag
Sets Undefined Defined: ordered, unordered, normal, pre-release and build sets
Ranges & comparators Out of scope Defined: comparators, comparator sets, and || unions
Operators None < > = <= >=, plus advanced tilde/caret/hyphen/x-ranges
Approximate operator None Pessimistic ~>
Grammar Prose + a single regex Published BNF for versions, ranges and comparators

The one behavioural divergence to be aware of is build precedence: tooling that assumes builds are ignored will order build-bearing versions differently. This is deliberate. It is what lets extensions like Dependency Locks and Package Integrity carry meaning in the build. See the history for the full story.

FAQ

How does this differ from semver.org?

In short: builds participate in precedence, and Sets, Ranges, Operators and Comparators are formally defined. See Relationship to Semantic Versioning 2.0.0 above for the full comparison.

How does this relate to Package-URL (purl)?

They solve different problems and compose cleanly. Package-URL (ECMA-427) is an identifier: pkg:npm/react@18.2.0 standardises how to name and locate a package across ecosystems, and is what SBOMs (CycloneDX, SPDX) and vulnerability databases (OSV, CVE) reference. It deliberately says nothing about version semantics (precedence, ordering or matching) and defers ranges to its separate vers constraint syntax.

Semantic Versions is the complementary layer: it defines what a version means, covering precedence (including build), sets, ranges and comparators. A purl says which package; Semantic Versions says how its versions order and match. The source-qualified identity used here maps onto a purl one-to-one across every ecosystem (see the mapping table under Context), so a tool can carry both. The one thing neither purl qualifiers nor vers model is a precedence-bearing build, which is precisely the slot our extensions occupy.

Where is the grammar?

Backus–Naur forms for versions, ranges and comparators are published on the grammar page, alongside a reference parser.

Can I contribute, or propose an extension?

Yes. The specification, reference parser and extensions are developed in the open at vltpkg/semantic-versions. Open an issue or discussion to propose a change, report a defect, or float a new extension. Anything that rides on a source-qualified version and restored build precedence is in scope.

License

The specification text is licensed under CC BY 3.0, the reference parser under Apache 2.0. See the license page for details and attribution.