# Publishing

Getting an addon to players through GitHub releases, and why not Modrinth

Source: https://socketsvc.vercel.app/zh-TW/addons/publishing
Addons go to **GitHub releases**. Not Modrinth: that project page is the core mod — client, server and proxy — and a page carrying a dozen optional extras makes the thing somebody came to install harder to find.

That is enforced, not remembered: `scripts/modrinth_release.py` refuses outright to upload anything named `socketsvc-addon-*`.

## Declaring what a release carries

A release becomes index entries by carrying an `addons.json` asset beside the jars:

```json title="addons.json"
{
  "addons": [
    {
      "id": "cinema",
      "name": "Cinema",
      "version": "1.2.0",
      "kind": "mod",
      "description": "Screens that play a stream",
      "file": "socketsvc-addon-cinema-1.2.0+mc1.21.11.jar",
      "minecraft": "1.21.11",
      "loader": "fabric",
      "side": "client"
    }
  ]
}
```

| Field | Meaning |
| --- | --- |
| `id` | `[a-z0-9][a-z0-9_-]{0,63}` — a folder name and a config key as well as a name |
| `kind` | `addon` to hot-load, `mod` for an ordinary loader mod |
| `file` | the asset **in this release** carrying the jar |
| `minecraft` / `loader` | omit for something that fits every game, which a pure addon usually does |
| `side` | `client` (the default) or `server`; the client manager only offers client ones |

The release also needs the **`checksums.txt`** that `scripts/publish_manifest.py` already writes. An entry whose jar has no checksum is **dropped from the index rather than served** — the client refuses anything it cannot verify, so serving it would only produce an install button that cannot work. The same goes for an entry naming a jar the release does not carry.

## What the index does with it

The [update service](/client/updates) reads every release, keeps the newest version of each id, and serves them at `/v1/addons` and `/addons.json`. A malformed entry is skipped and named; one bad row costs that addon and nothing else.

Players point at an index with `addonIndexEndpoint`, which defaults to SocketSVC's own service — the same place, and the same decision, as the updater.

## Why the index never links to GitHub directly

Every entry's `url` is the service's own `/v1/download/<tag>/<asset>` route, which redirects to GitHub for a public repository and streams the bytes for a private one.

That keeps the client's rule intact: **an index may only serve from its own host**. Combined with a server being able to name only an id, there is no path by which a server chooses where a player's game fetches code from.

<Callout title="The checksum is what makes the redirect safe">
  The host check governs what an index may *declare*, not where bytes finally come from — a redirect can go anywhere. The mandatory `sha256`, checked before a single class is loaded, is the guard that actually holds.
</Callout>