Manifest specification
Schema versions 1 + 2 · revision 2026-05
The PackRelay manifest is a small JSON document that describes every file in a pack along with a cryptographic signature that proves the manifest came from a registered publisher and hasn't been tampered with. Launchers verify the signature before installing any file.
Two versions of this schema are defined. schemaVersion: 1is the original flat shape used by every release as of 2026-05; sections 1–7 below cover it. schemaVersion: 2 adds optional upstream-of-bytes provenance via a top-level sourcescatalog referenced by file entries — documented in section 8. Both versions sign identically (Ed25519 over canonical bytes); launchers accept either.
1.A complete example
The simplest way to read the spec is to start with a real manifest. The example below is shortened — a production manifest typically contains hundreds of file entries.
2.Canonicalization & signing
The signature is computed over a canonical JSON serialization of the manifest with the signature field removed. Canonical means:
- UTF-8 encoding.
- Object keys sorted lexicographically.
- No whitespace between tokens (no indentation, no trailing newlines).
- Integers serialized without a decimal point; floats are not used in the manifest schema.
The PackRelay CLI does this for you. If you implement manifest signing yourself, two manifests that differ only in key order or whitespace MUST produce the same signature input.
The algorithm is Ed25519. The signature is computed over the canonical bytes; value is the lowercase hex encoding of the 64-byte signature.
3.Root fields
| Field | Type | Description |
|---|---|---|
| schemaVersion* | integer | Manifest schema version. Currently 1. Bumped only for breaking changes to the manifest shape. |
| name* | string | Globally unique pack identifier. Lowercase, hyphen-separated, 3–50 characters. Used as the URL slug at /packs/<name>. |
| displayName* | string | Human-readable pack name shown in the launcher and on packrelay.cloud. 3–80 characters. |
| version* | string | Semver — MAJOR.MINOR.PATCH, optionally with a pre-release suffix. Bumping any segment forces a sync on connected players. |
| game* | string | Target game identifier. Currently only "7d2d" is recognized. |
| gameVersion* | string | The game build the pack targets. Launchers warn players when their installed game version doesn't match. |
| publisher* | string | Publisher identity that signed this manifest. Maps to a server-owner account on PackRelay.cloud. |
| publishedAt* | string (ISO 8601) | UTC timestamp of when this version was signed and submitted. The launcher refuses manifests dated in the future. |
| description | string | Long-form pack description in plain text or basic Markdown. Surfaced on the pack detail page. |
| tags | string[] | Up to 5 lowercase tags. Used for /browse filtering and for player discovery. |
| files* | FileEntry[] | Every file in the pack with its content hash and size. See the table below. |
| signature* | Signature | Cryptographic signature over the canonical-JSON form of every other field. |
* required
4.FileEntry
Each element of the files array.
| Field | Type | Description |
|---|---|---|
| path* | string | Path relative to the pack root. Forward slashes only. Must not contain .. or absolute prefixes. |
| sha256* | string (64-char hex) | SHA-256 hash of the file's contents. The launcher refuses any file whose computed hash doesn't match. |
| size* | integer (bytes) | File size in bytes. Used for progress reporting during sync. |
| executable | boolean | Default false. Set true for files that must be marked executable on POSIX targets (rare for 7DTD packs). |
* required
5.Signature
| Field | Type | Description |
|---|---|---|
| algo* | string | Signature algorithm. Currently always "ed25519". Future algorithm support will live behind new values here. |
| publicKeyId* | string | Identifier of the public key that should verify this signature. Format: <publisher>/<key-name>. The key must be registered on PackRelay.cloud and active. |
| value* | string (hex) | Detached signature over the canonical-JSON form of the manifest with the signature field omitted. |
* required
6.Versioning
Pack versions are semver. The launcher considers a version upgrade required when any segment changes — there is no skipping. A player on 1.4.1 joining a server attached to 1.4.2 will sync.
Pre-release suffixes (1.4.2-beta.1) are honored but participating launchers must opt in to pre-release channels via the upcoming public API.
schemaVersionis independent of the pack's version. It bumps only when this specification gets a breaking change to the manifest shape itself.
7.Verification rules
A launcher MUST reject a manifest if any of the following are true:
- The
signature.publicKeyIdis not registered on PackRelay.cloud, or has been revoked. - The Ed25519 verification fails.
publishedAtis in the future relative to a trusted clock source (allow a small skew).- Any
files[].pathescapes the pack root (contains..segments or absolute prefixes). - A downloaded file's computed SHA-256 does not match the manifest entry.
schemaVersionexceeds the version the launcher supports.
8.Schema version 2: source provenance
Schema version 2 is an additive evolution of v1. It adds optional fields that record where a pack's bytes originally came from (Nexus Mods, GitHub Releases, 7DaysToDieMods, or explicitly self-hosted) so a downloader can verify provenance and inherit any trust signals the upstream source provides (e.g. a malware-scan attestation).
Every v1 manifest is a valid v2 manifest minus the new fields. Conversely, a v2 manifest with no sources array and no sourceRef on any file behaves identically to v1. The differences:
schemaVersionchanges from1to2.- Optional top-level
sourcesarray — a normalized catalog of upstream binaries this pack bundles. Up to 500 entries. Many files can share one source entry (a 50-file mod from a single GitHub Release zip = 1 source, 50 files referencing it). - Optional
sourceRefon eachFileEntry— a pointer to asources[].id. Files without asourceRefare treated as self-hosted with no recorded upstream.
The signing contract is identical to v1: the signature covers the canonical-JSON serialization of the manifest minus its signature field. New fields just become part of the signed message.
Example
FileEntry — added fields in v2
| Field | Type | Description |
|---|---|---|
| sourceRef | string (slug) | Pointer to a sources[].id. When set, this file inherits the source's provenance. When omitted, the file is treated as legacy-blob equivalent (self-hosted, no recorded upstream). |
* required
Source — common fields
Every element of the sources array carries these fields. The source discriminator picks which per-variant fields apply (Nexus / GitHub / 7DTM / legacy-blob).
| Field | Type | Description |
|---|---|---|
| id* | string (slug) | Unique-within-this-manifest identifier. Lowercase, hyphen-separated. File entries reference this via sourceRef. |
| source* | "nexus" | "github" | "7dtm" | "legacy-blob" | Discriminator. Picks which other fields are valid on this source object. See the per-variant tables below. |
| discoveredVia | "7dtm" | Catalog through which this source was discovered. Currently only "7dtm" is recognized — recorded for trust signaling (e.g. a github source cataloged on 7DaysToDieMods inherits 7DTM's scan attestation). |
| scanAttestation | ScanAttestation | Malware-scan record from the catalog source that hosts this binary. See ScanAttestation below. |
* required
source: "nexus"
The upstream binary lives on Nexus Mods. Publishing requires the publisher's Premium account API key (Nexus only authorizes file downloads via Premium).
| Field | Type | Description |
|---|---|---|
| game* | string | Nexus game identifier (typically "7daystodie"). Nexus URLs include this segment. |
| modId* | integer | Nexus numeric mod ID. Visible in the Nexus mod page URL (/mods/<modId>). |
| fileId* | integer | Nexus numeric file ID for the specific uploaded archive used for this pack version. |
| version* | string | Author-declared version on the Nexus file entry. |
* required
source: "github"
The upstream binary is a GitHub Release asset. Public repos work without authentication; private repos need a Personal Access Token on the publisher account.
| Field | Type | Description |
|---|---|---|
| owner* | string | GitHub user or organization that owns the source repo. |
| repo* | string | Repository name (without the owner prefix). |
| releaseTag* | string | The release tag the asset was published under (e.g. "v2.4.0"). Pins to an immutable commit + asset combination. |
| assetName* | string | Filename of the release asset (e.g. "BackupMod_v2.4.0.zip"). The launcher never downloads from this URL — bytes come from the blob cache by sha256 — but the field lets a downloader cross-check provenance. |
* required
source: "7dtm"
The upstream binary is cataloged on 7DaysToDieMods. Many 7DTM-cataloged mods actually host their bytes on GitHub; in that case the publisher chooses either source: "github" with discoveredVia: "7dtm" (preferred, records the catalog reference) or source: "7dtm" (treats 7DTM as the primary source).
| Field | Type | Description |
|---|---|---|
| modSlug* | string | 7DaysToDieMods URL slug (the path segment under 7daystodiemods.com/). |
| version* | string | Author-declared version on the 7DTM mod page. |
| upstreamUrl* | string (URL) | The link 7DTM redirects to for the actual binary (typically a GitHub release URL). Captured at publish time for round-trip provenance display; never resolved by the launcher. |
* required
source: "legacy-blob"
No additional fields. Use when the bytes don't map to any of the recognized upstream sources — the publisher uploaded them directly to PackRelay's blob cache as the original source. Also the implicit behavior for v2 files that omit sourceRef.
ScanAttestation
Optional sub-object on any source variant. Records a malware-scan result from the catalog that hosts the binary. Surfaced in the launcher's pack-detail UI as a trust signal.
| Field | Type | Description |
|---|---|---|
| scannedAt* | string (ISO 8601) | Timestamp the catalog source recorded for the malware scan. |
| scanner* | string | Name of the scanner the catalog used (e.g. "Norton 360", "Windows Defender"). Surfaced in pack-detail UI. |
* required