The server list block: specification
The open specification of the "keystone" block a server adds to its server list ping and of the manifest beside it - fields, format version, examples - for any launcher to read. The specification is open; Keystone's code is not.
On this page 5 parts
The server list block: specification
A server running Keystone can tell a launcher, before anybody joins, which modpack it runs, which game and loader, which Keystone, its resource pack and the client mods it recommends. It does that in the ordinary server list ping. This page is the specification of what it sends, so any launcher can read it. The specification is open; Keystone's code is All Rights Reserved. How an owner sets it up is on the server list.
Where it is
#The server list ping (status response) is a JSON object; Keystone adds one key to it, "keystone". Vanilla clients and other launchers read the reply leniently and skip a key they do not know, so the block is safe to send to everybody. The whole reply is one string the game caps at 32 767 characters, shared with the description and the icon; if the block would not fit, or anything about adding it fails, the reply goes out exactly as vanilla wrote it. The block stays well under a kilobyte.
The block, version 1
#"keystone": {
"v": 1,
"pack": "curseforge:123456@7654321",
"game": "1.21.1",
"loader": "neoforge",
"loaderVersion": "21.1.209",
"java": 21,
"heap": "6G",
"manifest": {"url": "https://example.com/manifest.json", "sha256": "…64 hex…"},
"keystoneVersion": "0.3.0",
"keystoneClient": "optional",
"recommend": [
{"mod": "modrinth:AANobbMI", "version": "mc1.21.1-0.6.0", "why": {"en": "Smoother frames", "cs": "Plynulejší obraz"}},
{"mod": "modrinth:sodium-extra", "atLeast": "0.6.0", "required": true, "why": {"en": "The HUD needs it"}},
{"mod": "modrinth:AANobbMI", "or": "curseforge:394468"},
{"preset": "my-quality"}
],
"resourcePack": {"url": "https://example.com/pack.zip", "sha1": "…40 hex…"}
}
| Field | Always | Meaning |
|---|---|---|
v |
yes | Format version, 1. A launcher that does not know the version ignores the block. |
game, loader |
yes | The Minecraft version and the loader (neoforge), as the server runs them. |
loaderVersion |
no | The loader's own version. |
pack |
no | The modpack: curseforge:<project id>[@<file id>] or modrinth:<project>[@<version>]. Ids only - letters, digits, _ and -, up to 64 each; CurseForge ids are numbers. |
java |
no | The Java version the pack wants (8-99). |
heap |
no | Suggested memory, 512M to 64G (6G, 6144M). |
manifest |
no | url (https, printable ASCII, no user, no #fragment, at most 512 characters) and the sha256 of the manifest as written. Given only together: a link nothing can check is not given. |
keystoneVersion |
no | The Keystone the server runs, without the build part (0.3.0, not 0.3.0+1.21.1-neoforge), so a launcher can offer the client half in the same version. |
keystoneClient |
with keystoneVersion |
optional today: a player without the client half joins all the same. required is reserved for a server whose features need it. |
recommend |
no | At most 12 entries and 3 000 characters. An entry is {"mod": "<portal>:<id>"} with version (exactly one: a Modrinth version id or number, a CurseForge file id) or atLeast (Modrinth, numbered versions), optionally or (the same mod on the other portal); or {"preset": "<id>"} for a launcher settings preset. required: true is for the player to read and blocks nobody. why holds a reason per language (en, cs), at most 120 characters each. |
resourcePack |
no | The server's own resource pack from server.properties (what vanilla hands every player who joins), only with an https url and its sha1. |
A server that announces nothing sends no block at all.
The manifest, format 1
#The file at manifest.url. The same mods give the same bytes - no timestamp, files in name order - so its hash changes only when something a player would notice did.
{
"format": 1,
"game": "1.21.1",
"loader": "neoforge",
"loaderVersion": "21.1.209",
"pack": "curseforge:123456@7654321",
"java": 21,
"heap": "6G",
"note": "Text for the player, at most 280 characters",
"mods": [
{"file": "create-1.21.1-6.0.6.jar", "ids": ["create"], "name": "Create", "version": "6.0.6",
"client": "required", "size": 12345678, "sha1": "…", "sha512": "…", "curseforge": 1234567890}
]
}
mods lists the files a player's game needs (client: required), or every file when the owner sets manifest.server-only. curseforge is CurseForge's own file fingerprint, sha1 and sha512 Modrinth's, so a launcher finds each file on a portal by its hash.
The rule: ids and hashes, never a download from the server
#Nothing in the block or the manifest is a file to download from the server or from an address the owner typed. A launcher looks the pack and every mod up on the portal by its id or hash and checks the portal's own hash; the manifest is checked against the sha256 in the ping. The one address besides the manifest is the resource pack, which vanilla already hands every joining player, and then only with its SHA-1. A launcher refuses the whole block over one field out of shape; Keystone checks every value against these shapes before sending and leaves out what does not fit, with the reason in its log.
What a server never says here
#Everything in the block is answered to anybody who pings, without a key. So it holds only what the owner typed on purpose and what the server already admits to (game, loader, Keystone version) - never the mod list (that is in the manifest, only when the owner publishes it), never a path, never a key.
Did this page help?
Opens the feedback panel with this page attached. It lands in the same queue as everything else.