# Stockfish — GPL-3.0 compliance

**Not legal advice.** This is the reasoning behind how the engine is shipped, so
the decision is written down rather than assumed.

## What is bundled

| | |
|---|---|
| package | `stockfish.js` 18 — https://github.com/nmrugg/stockfish.js |
| upstream | Stockfish — https://github.com/official-stockfish/Stockfish |
| licence | **GPL-3.0-or-later** |
| build | `stockfish-18-lite-single` (single-threaded, so no SharedArrayBuffer and no COOP/COEP headers) |
| NNUE | `nn-9067e33176e` |

The licence notice is preserved in the header of
`stockfish-18-lite-single.js` and must not be stripped.

## Why obligations apply today

Serving the `.wasm` to a browser **is conveying** under GPL-3.0. The Cloudflare
Pages deploy does exactly that, so the distribution obligations are live right
now: preserve the notices, ship the licence text, and offer the corresponding
source of the exact build being distributed.

## Does it make Chessynth itself GPL?

The argument that it does not: Chessynth talks to the engine **at arm's length**.
Stockfish runs in its own Web Worker — a separate thread and a separate JS realm —
and the only traffic between them is UCI text over `postMessage`:

    position startpos moves e2e4 e7e5
    go depth 12
    -> info depth 12 score cp 37 ...
    -> bestmove e2e4

No linking, no shared address space, no Stockfish code called directly. This is
the same relationship every desktop chess GUI has with the engines it ships, and
it is the FSF's own distinction between a combined work and two programs that
communicate.

That argument is reasonable but it is not a guarantee, and the Stockfish
maintainers enforce actively — they have pursued commercial products and had
apps removed from stores. Do not treat this as settled.

## The important asymmetry: GPL, not AGPL

Stockfish is GPL-3.0, **not** AGPL. Running it on a server and returning results
over a network is **not** distribution, so none of the obligations trigger.

That matters for two planned things at once:

1. **A mobile app with paid packs.** GPL-3.0 conflicts with the Apple App Store
   terms (the anti-tivoisation and DRM clauses). Stockfish-bundling apps have
   been pulled. Bundling the engine into an iOS build is a real problem, not a
   theoretical one.
2. **Server-side rendering**, which the deterministic-replay design already
   wants for published tracks.

Moving the engine server-side solves the store problem and the licence problem
with one change — and it is a change the architecture was heading toward anyway.

## Current position

- **Web prototype (now):** keep the engine client-side and comply. Notices
  preserved, licence text shipped, corresponding source offered below,
  attribution shown in the app.
- **Mobile / commercial (later):** move the engine server-side. No conveying,
  no obligations, no store conflict.
- **Do not** strip the licence header, and do not link Stockfish code directly
  into the app bundle — the worker boundary is what the separation argument
  rests on.

## Corresponding source

GPL-3.0 §6(d) lets object code be conveyed from a network server with the
Corresponding Source hosted elsewhere, provided there are **clear directions
next to the object code** saying where to find it. Directions are only clear if
they identify *this* build rather than whatever upstream happens to be today, so
the build is pinned by content:

| | |
|---|---|
| package | `stockfish.js` 18 — https://github.com/nmrugg/stockfish.js |
| upstream | Stockfish — https://github.com/official-stockfish/Stockfish |
| build | `stockfish-18-lite-single` (single-threaded) |
| NNUE net | `nn-9067e33176e` |
| `stockfish-18-lite-single.js` | sha256 `5243fd9b276cab7dfe3ad1d43ab9ead73568fac76468c614242977a210c4a391` |
| `stockfish-18-lite-single.wasm` | sha256 `a8fbc05ec6920b56d7485826dcb02c5ffd2826bcbf751cf973046f237a9096f1` |

A checksum is a stronger pin than a tag: it identifies the artefact itself, so a
recipient can confirm the source they fetch corresponds to the binary they were
served. `tools/build-dist.sh` fails the build if any shipped copy stops matching
`vendor/`, because the moment they diverge the offer above stops being true.

The licence header inside `stockfish-18-lite-single.js` is preserved verbatim
and must not be stripped by any minifier.

## Two defects found on the live deploy, 2026-09-09

Recorded because both were silent, and silent compliance failures are the ones
that persist.

1. **The licence link served the application.** The footer links to
   `vendor/COPYING.txt` *relatively*, and the app is served from `/chess-synth/`
   as well as the site root. Nothing copied `vendor/` into
   `dist/chess-synth/`, so that path did not exist and Cloudflare Pages answered
   with the SPA fallback: **HTTP 200, 340KB of Chessynth HTML**, not the GPL. A
   200 is not proof a path exists. `tools/build-dist.sh` now ships the licence
   under both roots and asserts the served file really is the GPL text.

2. **The corresponding-source offer was not pinned**, which this file had itself
   flagged as required "before any public release" while the deploy was already
   public. Fixed by the table above.
