Skip to content

Commit 107b50c

Browse files
TMHSDigitalclaude
andcommitted
Licence guide covers every vocabulary value; validate keeps it that way
docs/licenses.md gains a permissive software table (MIT, ISC, BSD variants, Apache-2.0, MPL-2.0, CPL-1.0, CDLA-Permissive-2.0, Unlicense, public-domain) with what each asks you to ship, a GPL family table, a bare CC-BY row and a section on reading custom, varies and unknown entries. V32 fails validate when a licence value in site/license-vocabulary.json is not mentioned in the guide (21 were missing before). Closes #143 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 6402d7b commit 107b50c

4 files changed

Lines changed: 57 additions & 1 deletion

File tree

‎docs/licenses.md‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Quick reference for assets you might list or use. **Always re-check the source p
1414
| **CC-BY-SA** | Yes, **except Unreal** | Required | Share-alike applies to **derivatives of the asset**, not your whole game as code. Prefer shipping SA assets in extractable bundles (`.pck`, loose folders) rather than encrypted into the binary. The Unreal Engine EULA names CC-BY-SA (and GPL/LGPL) as licences you may not combine with the engine; see [unreal.md](unreal.md). |
1515
| **CC-BY-NC** | No | Required | Fine for prototypes / jam games; **reject for commercial catalogs** unless clearly marked non-commercial. |
1616
| **CC-BY-ND** | Yes | Required | No derivatives — **reject for games**. Interactive sync / remix is generally treated as a derivative; ND music and art are unsafe. |
17+
| **CC-BY** (no version) | Yes | Required | The source says CC-BY without a version. Credit as it asks; read the source before relying on terms that differ between 3.0 and 4.0. |
1718
| **ODbL** (OpenStreetMap) ([geodata.md](geodata.md)) | Yes* | Required | Game binary is usually a “Produced Work” (code not copylefted). If you redistribute a modified geodatabase, ODbL share obligations can trigger. Credit OSM. |
1819

1920
## Fonts
@@ -39,6 +40,38 @@ Full guidance, including what you still owe when `attribution_required` is false
3940
| **FMOD Studio (indie)** | Custom | Often free under revenue/funding caps — verify current thresholds. |
4041
| **Wwise free / evaluation** | Custom | Often asset-count or non-commercial caps — verify before shipping. |
4142

43+
## Permissive software licences
44+
45+
Most `tools/` entries and many libraries carry one of these. They all allow commercial use and closed-source games. What they ask for is a copy of the notice, not a credit on screen. The shortlist on the site collects these notices into one `CREDITS` file you can ship.
46+
47+
| License | Commercial use | What you ship | Notes |
48+
| --- | --- | --- | --- |
49+
| **MIT** | Yes | The copyright line and licence text, in a notices file | Applies when you redistribute the code or a build containing it. Using an MIT editor to make assets owes nothing. |
50+
| **ISC** | Yes | Same as MIT | Functionally the MIT licence in fewer words. |
51+
| **BSD-2-Clause** | Yes | Copyright line and licence text | The bare value **BSD** is used where a project says "BSD" without naming the clause count; read its LICENSE before shipping. |
52+
| **BSD-3-Clause** | Yes | Copyright line and licence text | Adds a no-endorsement clause: do not use the authors' names to promote your game. **BSD-3-Clause-Clear** is the same, but states that no patent rights are granted. |
53+
| **Apache-2.0** | Yes | Licence text, plus the project's `NOTICE` file if it has one | Includes a patent grant, which ends if you sue the authors over patents. Modified files must say they were changed. |
54+
| **MPL-2.0** | Yes | Licence text; source of the MPL files you changed | File-level copyleft: changes to MPL files stay MPL and must be offered as source; your own files and the rest of the game do not. |
55+
| **CPL-1.0** | Yes | Licence text; source of the CPL code you changed | An older weak-copyleft licence with a patent clause, close to MPL in effect. |
56+
| **CDLA-Permissive-2.0** | Yes | The licence text or a link to it | A data licence. Results of using the data, such as a trained model or a rendered scene, carry no obligation. |
57+
| **Unlicense** | Yes | Nothing | A public-domain dedication with a fallback licence, treated like CC0. |
58+
| **public-domain** | Yes | Nothing | Used where the source states public domain without a CC0 deed, such as US government works. Check the country: a work public in the US may not be elsewhere. |
59+
60+
## GPL family
61+
62+
| License | Commercial use | Notes |
63+
| --- | --- | --- |
64+
| **GPL-2.0-only**, **GPL-2.0-or-later**, **GPL-3.0-only**, **GPL-3.0-or-later** | Yes | Strong copyleft on the program. Linking GPL code into your game makes the game GPL. Using a GPL editor does not, as the section below explains. The bare **GPL-2.0** and **GPL-3.0** values mean the project did not say "only" or "or later". |
65+
| **LGPL-3.0** | Yes | You may link the library from closed code if users can replace it, which in practice means dynamic linking. Changes to the library itself stay LGPL. The Unreal Engine EULA forbids combining LGPL code with the engine; see [unreal.md](unreal.md). |
66+
67+
## How to read a `custom`, `varies` or `unknown` entry
68+
69+
**custom** means the source wrote its own terms. The entry's `## Evidence` section quotes the sentences that matter, with the date they were read, and `commercial` and `attribution_required` are the catalog's reading of that specific text. Read the quote, and the Notes for limits such as no redistribution, one project per purchase or engine locks, before you rely on the summary fields.
70+
71+
**varies** means the source hosts files under different licences, such as an aggregator or a community upload site. The entry describes the site, not any one file. Check each file's own licence before you use it, and expect `commercial: varies`.
72+
73+
**unknown** means the licence could not be established from the live source. Such entries are never `active`. Treat them as unusable until a dated quote settles the question.
74+
4275
## Copyleft tools vs generated output
4376

4477
GPL/AGPL on an **editor or generator** (Tiled, Blockbench, Pixelorama, etc.) usually does **not** copyleft the PNG/glTF/WAV you export — unless the maintainer says otherwise. Catalog `license` on `tools/` is the **software** license. Keep generated assets under whatever terms you assign; don’t ship the GPL editor source inside the game binary unless you mean to.

‎site/checks.mjs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,3 +851,18 @@ export function checkNonCommercialEvidence(rel, meta, body) {
851851
if (/commercial/i.test(ev)) return [];
852852
return [`${rel} is active with commercial: false but no Evidence line mentions the commercial restriction; quote the source or use needs-review`];
853853
}
854+
855+
/**
856+
* V32: docs/licenses.md explains every licence value the vocabulary allows,
857+
* so a value added to site/license-vocabulary.json cannot ship without a
858+
* row or paragraph telling readers what it means.
859+
*/
860+
export function checkLicenceGuide(vocab, guideText) {
861+
const text = String(guideText);
862+
const keys = Object.keys((vocab && vocab.licenses) || {});
863+
const missing = keys.filter((k) => {
864+
const esc = k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
865+
return !new RegExp(`(?<![\\w-])${esc}(?![\\w-])`).test(text);
866+
});
867+
return missing.map((k) => `docs/licenses.md does not mention the licence value "${k}" (add a row or a sentence for it)`);
868+
}

‎site/checks.test.mjs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
evidenceDates,
2626
markdownAnchors,
2727
checkLicenseVocabulary,
28+
checkLicenceGuide,
2829
checkLineEndings,
2930
checkMaintenance,
3031
checkNonCommercialEvidence,
@@ -807,10 +808,15 @@ accepts("V31 ignores needs-review", checkNonCommercialEvidence("ok.md", { status
807808
accepts("V31 accepts a quoted restriction", checkNonCommercialEvidence("ok.md", { status: "active", commercial: false }, "## Evidence\n\n- Live About (2026-01-01): \"as long as you don't use these commercially\"\n"));
808809
rejects("V31 rejects an unquoted restriction", checkNonCommercialEvidence("bad.md", { status: "active", commercial: false }, "## Evidence\n\n- Live page (2026-01-01): \"free download\"\n"), "bad.md is active with commercial: false but no Evidence line mentions the commercial restriction");
809810

811+
/* V32: the licence guide covers the vocabulary ---------------------------- */
812+
accepts("V32 accepts a guide naming every value", checkLicenceGuide({ licenses: { MIT: {}, "CC-BY": {} } }, "| **MIT** | yes |\n| **CC-BY** | yes |"));
813+
rejects("V32 rejects a missing value", checkLicenceGuide({ licenses: { MIT: {}, ISC: {} } }, "| **MIT** | yes |"), 'does not mention the licence value "ISC"');
814+
rejects("V32 a longer value does not count for its prefix", checkLicenceGuide({ licenses: { "CC-BY": {} } }, "| **CC-BY-4.0** | yes |"), 'does not mention the licence value "CC-BY"');
815+
810816
/* ----------------------------------------------------------------------- */
811817
if (failures.length) {
812818
console.error(`checks.test failed (${failures.length}):`);
813819
for (const f of failures) console.error(` - ${f}`);
814820
process.exit(1);
815821
}
816-
console.log(`checks.test ok: ${passed} assertions across 29 checks`);
822+
console.log(`checks.test ok: ${passed} assertions across 30 checks`);

‎site/validate.mjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
checkFieldTypes,
2323
checkFormatVocabulary,
2424
checkLicenseVocabulary,
25+
checkLicenceGuide,
2526
checkLineEndings,
2627
checkMaintenance,
2728
checkNonCommercialEvidence,
@@ -261,6 +262,7 @@ function main() {
261262
JSON.parse(fs.readFileSync(SPDX_ALLOWED_PATH, "utf8"))
262263
);
263264
const vocab = JSON.parse(fs.readFileSync(VOCAB_PATH, "utf8"));
265+
errors.push(...checkLicenceGuide(vocab, fs.readFileSync(path.join(ROOT, "docs", "licenses.md"), "utf8")));
264266
const aliases = JSON.parse(fs.readFileSync(ALIASES_PATH, "utf8"));
265267
const formatVocab = JSON.parse(fs.readFileSync(FORMATS_PATH, "utf8"));
266268
const categories = catalogCategories();

0 commit comments

Comments
 (0)