Table of Contents
- Epoch Semantic Versioning 1.0.0
- Abstract
- 1. Introduction
- 2. Conformance and Terminology
- 3. Logical Version Model
- 4. Increment and Reset Rules
- 4.1 New Logical Versions
- 4.2 Significance Order
- 4.3 Reset Requirements
- 4.4 Uniqueness
- 4.5 Pre-release and Identity
- 5. Epoch Zero
- 6. Encoding
- 7. Decoding
- 8. Ordering and Precedence
- 9. Canonical Version File
- 10. Canonical File Format
- 11. Release State Semantics
- 12. Codename
- 12.1 Purpose
- 12.2 Codename Rules
- 12.3 Codename Scope in Multi-unit Repositories
- 12.4 Codename Semantics
- 13. Derived Encoded Version
- 14. Pre-release Identifiers
- 15. Validation Requirements
- 16. Examples
- 16.1 Standard SemVer-compatible Version
- 16.2 First Epoch Release
- 16.3 Pre-release Example
- 16.4 Logical and Encoded Progression
- 16.5 Independent-version Multi-unit Repository
- 16.6 Shared-version Multi-unit Repository
- 17. Security and Operational Considerations
- 18. IANA Considerations
- 19. Non-normative Summary
Epoch Semantic Versioning 1.0.0
Abstract
Epoch Semantic Versioning ("Epoch SemVer") is a versioning scheme that extends Semantic Versioning by introducing a fourth logical version component named epoch. Inspired by Anthony Fu's Epoch Semantic Versioning proposal.
Epoch SemVer preserves compatibility with existing SemVer-aware tooling by encoding the logical tuple (epoch, major, minor, patch) into a standard three-part Semantic Version string using a fixed multiplier.
This specification defines the logical version model, encoding and decoding rules, precedence, canonical metadata format, validation requirements, monorepo policies, and pre-release handling.
1. Introduction
Semantic Versioning provides a widely adopted model for expressing compatibility and change magnitude. In some environments, teams also need a visible mechanism for representing rare, high-signal generational shifts that are more significant than ordinary major-version changes.
Epoch SemVer addresses this need by adding an epoch component to the logical version model while keeping the externally visible version string SemVer-compatible.
This specification is intended primarily for internal packages, services, applications, and release workflows.
2. Conformance and Terminology
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.
For the purposes of this specification:
- A logical version is the tuple
(epoch, major, minor, patch). - An encoded version is the SemVer string derived from a logical version, optionally with a SemVer pre-release suffix.
- A versioned unit is a package, service, application, library, module, or other independently versioned artifact.
- A release state is the metadata state associated with a versioned unit at a particular logical version.
- A canonical version file is a file named
epoch-version.jsoncontaining the canonical version state for a versioned unit.
3. Logical Version Model
3.1 Components
Every Epoch SemVer logical version consists of exactly four components:
epochmajorminorpatch
Each component MUST be an integer greater than or equal to 0.
3.2 Version Identity
The identity of an Epoch SemVer release is the logical tuple:
(epoch, major, minor, patch)
The following fields are not part of logical version identity:
preReleasereleasedAtcommitHashcodename
3.3 Intended Meaning
The intended meaning of the logical components is as follows:
| Component | Intended Meaning |
|---|---|
patch |
Backwards-compatible fixes |
minor |
Backwards-compatible additions |
major |
Ordinary breaking changes |
epoch |
Rare generational or landmark changes |
These meanings are descriptive guidance for human consumers. Conforming tooling MUST follow the encoding, decoding, and precedence rules in this specification rather than attempting to infer semantics from release notes or implementation details.
4. Increment and Reset Rules
4.1 New Logical Versions
To create a new logical version, an implementation MUST increment exactly one logical component and MUST reset all less-significant logical components to 0.
4.2 Significance Order
The logical components are ordered from most significant to least significant as follows:
epoch > major > minor > patch
4.3 Reset Requirements
When incrementing a component, the following reset behavior MUST be applied:
| Incremented Component | Required Reset Behavior |
|---|---|
patch |
No reset |
minor |
patch = 0 |
major |
minor = 0, patch = 0 |
epoch |
major = 0, minor = 0, patch = 0 |
4.4 Uniqueness
A versioned unit MUST NOT publish two distinct releases with the same fully encoded version string.
A versioned unit MUST NOT publish two distinct releases with the same logical tuple and the same preRelease value.
4.5 Pre-release and Identity
A change to preRelease alone does not change the logical version identity.
5. Epoch Zero
When epoch = 0, Epoch SemVer behaves as ordinary Semantic Versioning with respect to the encoded version string:
encodedMajor = major
version = major.minor.patch
If a pre-release identifier is present, the encoded version is:
version = major.minor.patch-preRelease
A project that never increments epoch therefore remains operationally equivalent to an ordinary SemVer project.
6. Encoding
6.1 Fixed Multiplier
Epoch SemVer MUST use a fixed multiplier of 1000.
The encoded major version MUST be computed as:
encodedMajor = epoch * 1000 + major
6.2 Major Range
The logical major component MUST satisfy:
0 <= major <= 999
If a versioned unit needs to exceed 999 major releases within a single epoch, it MUST increment epoch.
6.3 Encoded Version Form
The encoded version MUST be emitted as a valid Semantic Version string in one of the following forms:
<encodedMajor>.<minor>.<patch>
<encodedMajor>.<minor>.<patch>-<preRelease>
Build metadata MAY be appended using ordinary SemVer build metadata syntax, but such metadata is outside the scope of this specification and MUST NOT be stored as an Epoch SemVer logical component.
7. Decoding
Given an encoded version string of the form:
encodedMajor.minor.patch
or
encodedMajor.minor.patch-preRelease
the logical components MUST be recovered as follows:
epoch = floor(encodedMajor / 1000)major = encodedMajor mod 1000minor = minorpatch = patch
If present, preRelease is the SemVer pre-release suffix.
Example:
1003.2.0 => epoch=1, major=3, minor=2, patch=0
1003.2.0-beta.1 => epoch=1, major=3, minor=2, patch=0, preRelease=beta.1
8. Ordering and Precedence
8.1 Logical Ordering
Logical versions MUST be ordered lexicographically by:
(epoch, major, minor, patch)
8.2 Encoded Precedence
Encoded versions MUST be compared using ordinary Semantic Versioning precedence rules.
8.3 Monotonicity
If logical version A is greater than logical version B under the ordering defined in Section 8.1, then the encoded version of A MUST have greater SemVer precedence than the encoded version of B.
8.4 Pre-release Ordering
When two encoded versions share the same logical tuple, any precedence relationship involving preRelease MUST be determined exclusively by standard SemVer pre-release rules.
9. Canonical Version File
9.1 General Requirement
Each versioned unit MUST have exactly one applicable canonical version file named epoch-version.json.
This file MUST be the canonical source of truth for the logical version and release metadata of that versioned unit.
Derived version fields in other files, such as package.json versions, SHOULD be generated from the applicable canonical version file rather than maintained independently.
9.2 Single-unit Repositories
In a repository containing exactly one versioned unit, the canonical version file SHOULD be located at the repository root.
9.3 Multi-unit Repositories
A repository MAY contain multiple versioned units.
A multi-unit repository MUST adopt exactly one of the following policies for each versioned unit:
-
Independent-version policy
Each versioned unit has its own canonical version file, typically located at the root of that unit's directory. -
Shared-version policy
A single canonical version file governs multiple versioned units.
A repository MAY apply the shared-version policy to one group of versioned units and the independent-version policy to another group of versioned units, provided that each versioned unit has exactly one applicable canonical version file.
9.4 Applicability
Tooling MUST derive the encoded version of a versioned unit from the canonical version file applicable to that unit under the repository's chosen policy.
10. Canonical File Format
10.1 Required Fields
A canonical version file MUST contain exactly the following top-level fields:
{
"epoch": 0,
"major": 0,
"minor": 0,
"patch": 0,
"preRelease": null,
"releasedAt": null,
"commitHash": null,
"codename": null
}
Additional fields MAY be allowed by implementation-specific tooling, but such fields are outside the scope of this specification and MUST NOT affect conformance.
10.2 Field Definitions
| Field | Type | Requirements |
|---|---|---|
epoch |
integer | non-negative |
major |
integer | integer in the range 0..999 |
minor |
integer | non-negative |
patch |
integer | non-negative |
preRelease |
string or null |
valid SemVer pre-release identifier string, or null |
releasedAt |
integer or null |
UTC Unix epoch timestamp in milliseconds, or null |
commitHash |
string or null |
short Git SHA, or null |
codename |
string or null |
non-empty, non-whitespace-only string, or null |
11. Release State Semantics
11.1 Unreleased State
In an unreleased working state:
releasedAtMUST benullcommitHashMUST benull
11.2 Released State
In a released state:
releasedAtMUST be a UTC Unix epoch timestamp in millisecondscommitHashMUST be a non-empty short Git SHA
releasedAt and commitHash MUST transition together from null to non-null.
11.3 Release Metadata Scope
releasedAt and commitHash are release metadata only. They are not part of logical version identity and MUST NOT affect version precedence.
12. Codename
12.1 Purpose
A codename identifies the current epoch using a human-friendly label.
Examples include "obsidian" and "aurora".
12.2 Codename Rules
- If
epoch > 0,codenameMUST be present and MUST be a non-empty, non-whitespace-only string. - If
epoch = 0,codenameMAY benull. - A codename MUST remain unchanged across patch, minor, and major increments within the same epoch.
- A codename MAY change only when
epochchanges.
12.3 Codename Scope in Multi-unit Repositories
Under the independent-version policy, codename values apply per versioned unit.
Under the shared-version policy, a codename value applies to all versioned units governed by the shared canonical version file.
12.4 Codename Semantics
The codename is metadata only. It is NOT part of version identity, encoded version formation, or version precedence.
13. Derived Encoded Version
The encoded version of a versioned unit MUST be derived from epoch-version.json as follows:
encodedMajor = epoch * 1000 + major
If preRelease is null:
version = encodedMajor.minor.patch
If preRelease is not null:
version = encodedMajor.minor.patch-preRelease
Example canonical file:
{
"epoch": 1,
"major": 3,
"minor": 2,
"patch": 0,
"preRelease": "beta.1",
"releasedAt": null,
"commitHash": null,
"codename": "obsidian"
}
Derived encoded version:
1003.2.0-beta.1
14. Pre-release Identifiers
14.1 General
This specification adopts standard SemVer pre-release syntax without modification.
14.2 Canonical Representation
If present, the pre-release identifier MUST be stored in the preRelease field of the canonical version file.
14.3 Constraints
The preRelease field:
- MUST be either
nullor a valid SemVer pre-release identifier string. - MUST NOT be treated as a fifth logical Epoch SemVer component.
- MUST affect encoded version precedence exactly as defined by Semantic Versioning.
15. Validation Requirements
An implementation conforms to this specification only if all of the following are true for every applicable canonical version file and derived encoded version:
epoch,major,minor, andpatchare integers greater than or equal to0.majoris less than or equal to999.preReleaseis eithernullor a valid SemVer pre-release identifier string.releasedAtis eithernullor an integer representing UTC Unix epoch milliseconds.commitHashis eithernullor a non-empty string.codenameis eithernullor a non-empty, non-whitespace-only string.- If
epoch > 0,codenameis non-null. - If the state is unreleased, both
releasedAtandcommitHasharenull. - If the state is released, both
releasedAtandcommitHashare non-null. - The encoded version is exactly
(epoch * 1000 + major).minor.patchwith an optional-preReleasesuffix. - The versioned unit has exactly one applicable canonical version file.
- The versioned unit's encoded version is derived from its applicable canonical version file.
- Any increment that creates a new logical version obeys the reset rules in Section 4.
- No two published releases for the same versioned unit share the same encoded version string.
16. Examples
16.1 Standard SemVer-compatible Version
{
"epoch": 0,
"major": 2,
"minor": 4,
"patch": 1,
"preRelease": null,
"releasedAt": 1743430920000,
"commitHash": "e1a72f0",
"codename": null
}
Derived encoded version:
2.4.1
16.2 First Epoch Release
{
"epoch": 1,
"major": 0,
"minor": 0,
"patch": 0,
"preRelease": null,
"releasedAt": 1743430920000,
"commitHash": "a3f9c12",
"codename": "obsidian"
}
Derived encoded version:
1000.0.0
16.3 Pre-release Example
{
"epoch": 1,
"major": 3,
"minor": 2,
"patch": 0,
"preRelease": "beta.1",
"releasedAt": null,
"commitHash": null,
"codename": "obsidian"
}
Derived encoded version:
1003.2.0-beta.1
16.4 Logical and Encoded Progression
Logical progression:
(0,1,0,0) -> (0,1,1,0) -> (0,2,0,0) -> (1,0,0,0) -> (1,1,0,0)
Encoded progression:
1.0.0 -> 1.1.0 -> 2.0.0 -> 1000.0.0 -> 1001.0.0
16.5 Independent-version Multi-unit Repository
packages/a/epoch-version.json
packages/b/epoch-version.json
services/api/epoch-version.json
Each versioned unit derives its encoded version from its own canonical version file.
16.6 Shared-version Multi-unit Repository
epoch-version.json
packages/a/package.json
packages/b/package.json
services/api/package.json
Each governed versioned unit derives its encoded version from the shared canonical version file.
17. Security and Operational Considerations
Epoch SemVer does not alter the security properties of Semantic Versioning itself.
However, implementations should consider the following operational risks:
- Manual editing of derived version fields may cause divergence from the canonical version file.
- In multi-unit repositories, ambiguous or undocumented version policy may cause tooling to derive the wrong encoded version.
- Inconsistent interpretation of
commitHashmay reduce traceability across releases. - Unsynchronized release workflows may result in duplicate or conflicting encoded versions.
Implementations SHOULD validate canonical version files in CI and SHOULD generate derived version fields automatically.
18. IANA Considerations
This document has no IANA actions.
19. Non-normative Summary
Epoch SemVer is SemVer-compatible on the outside and four-part logical versioning on the inside.
Its defining rule is:
encodedMajor = epoch * 1000 + major
This preserves ordinary tooling compatibility while providing a structured way to represent rare, high-signal generational shifts.