PACKRELAY.cloud
DOCS · MANIFEST SPEC

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.

manifest.json
{
  "schemaVersion": 1,
  "name": "the-veiled-eclipse",
  "displayName": "The Veiled Eclipse",
  "version": "1.4.2",
  "game": "7d2d",
  "gameVersion": "1.0.0",
  "publisher": "darkrising.gg",
  "publishedAt": "2026-05-10T18:42:00Z",
  "files": [
    {
      "path": "Mods/EclipseLighting/ModInfo.xml",
      "sha256": "9f1ce8a3...ab4e",
      "size": 1842
    },
    {
      "path": "Mods/EclipseLighting/Config/lights.xml",
      "sha256": "5e2a1f88...d091",
      "size": 5310
    }
  ],
  "signature": {
    "algo": "ed25519",
    "publicKeyId": "darkrising.gg/main",
    "value": "f0d3a1...7c81"
  }
}

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

FieldTypeDescription
schemaVersion*integerManifest schema version. Currently 1. Bumped only for breaking changes to the manifest shape.
name*stringGlobally unique pack identifier. Lowercase, hyphen-separated, 3–50 characters. Used as the URL slug at /packs/<name>.
displayName*stringHuman-readable pack name shown in the launcher and on packrelay.cloud. 3–80 characters.
version*stringSemver — MAJOR.MINOR.PATCH, optionally with a pre-release suffix. Bumping any segment forces a sync on connected players.
game*stringTarget game identifier. Currently only "7d2d" is recognized.
gameVersion*stringThe game build the pack targets. Launchers warn players when their installed game version doesn't match.
publisher*stringPublisher 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.
descriptionstringLong-form pack description in plain text or basic Markdown. Surfaced on the pack detail page.
tagsstring[]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*SignatureCryptographic signature over the canonical-JSON form of every other field.

* required

4.FileEntry

Each element of the files array.

FieldTypeDescription
path*stringPath 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.
executablebooleanDefault false. Set true for files that must be marked executable on POSIX targets (rare for 7DTD packs).

* required

5.Signature

FieldTypeDescription
algo*stringSignature algorithm. Currently always "ed25519". Future algorithm support will live behind new values here.
publicKeyId*stringIdentifier 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.publicKeyId is not registered on PackRelay.cloud, or has been revoked.
  • The Ed25519 verification fails.
  • publishedAt is in the future relative to a trusted clock source (allow a small skew).
  • Any files[].path escapes the pack root (contains.. segments or absolute prefixes).
  • A downloaded file's computed SHA-256 does not match the manifest entry.
  • schemaVersion exceeds 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:

  • schemaVersion changes from 1 to 2.
  • Optional top-level sources array — 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 sourceRef on each FileEntry — a pointer to a sources[].id. Files without a sourceRef are 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

manifest.json
{
  "schemaVersion": 2,
  "name": "the-veiled-eclipse",
  "displayName": "The Veiled Eclipse",
  "version": "1.4.2",
  "game": "7d2d",
  "gameVersion": "1.0.0",
  "publisher": "darkrising.gg",
  "publishedAt": "2026-05-10T18:42:00Z",
  "sources": [
    {
      "id": "eclipse-lighting-v142",
      "source": "github",
      "owner": "darkrising-gg",
      "repo": "EclipseLighting",
      "releaseTag": "v1.4.2",
      "assetName": "EclipseLighting_v1.4.2.zip",
      "discoveredVia": "7dtm",
      "scanAttestation": {
        "scannedAt": "2026-04-29T00:00:00Z",
        "scanner": "Norton 360"
      }
    }
  ],
  "files": [
    {
      "path": "Mods/EclipseLighting/ModInfo.xml",
      "sha256": "9f1ce8a3...ab4e",
      "size": 1842,
      "sourceRef": "eclipse-lighting-v142"
    },
    {
      "path": "Mods/EclipseLighting/Config/lights.xml",
      "sha256": "5e2a1f88...d091",
      "size": 5310,
      "sourceRef": "eclipse-lighting-v142"
    }
  ],
  "signature": {
    "algo": "ed25519",
    "publicKeyId": "darkrising.gg/main",
    "value": "f0d3a1...7c81"
  }
}

FileEntry — added fields in v2

FieldTypeDescription
sourceRefstring (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).

FieldTypeDescription
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).
scanAttestationScanAttestationMalware-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).

FieldTypeDescription
game*stringNexus game identifier (typically "7daystodie"). Nexus URLs include this segment.
modId*integerNexus numeric mod ID. Visible in the Nexus mod page URL (/mods/<modId>).
fileId*integerNexus numeric file ID for the specific uploaded archive used for this pack version.
version*stringAuthor-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.

FieldTypeDescription
owner*stringGitHub user or organization that owns the source repo.
repo*stringRepository name (without the owner prefix).
releaseTag*stringThe release tag the asset was published under (e.g. "v2.4.0"). Pins to an immutable commit + asset combination.
assetName*stringFilename 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).

FieldTypeDescription
modSlug*string7DaysToDieMods URL slug (the path segment under 7daystodiemods.com/).
version*stringAuthor-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.

FieldTypeDescription
scannedAt*string (ISO 8601)Timestamp the catalog source recorded for the malware scan.
scanner*stringName of the scanner the catalog used (e.g. "Norton 360", "Windows Defender"). Surfaced in pack-detail UI.

* required