The GitHub REST API returns a license object on every repo with a recognized LICENSE file. The spdx_id field inside it is what my ossfind.com ETL stores in Turso: r.license?.spdx_id ?? null. Simple enough.
The problem is that displaying that raw SPDX ID on a directory page — without mapping it first — either misrepresents the license or returns something meaningless to a developer evaluating the tool. Four specific values kept causing problems.
null — no license at all
r.license is null when the GitHub API can't find a LICENSE file in the repo. The ETL stores null. The display logic in decision-score.ts maps null to the unknown bucket: "No clear SPDX id — treat as all-rights-reserved until verified."
This is legally correct but surprising: a large fraction of repos on GitHub have no LICENSE file, often because the author never thought about it. Under copyright law, "all rights reserved" is the default in the absence of explicit terms. A repo with no license is technically proprietary, regardless of the author's intent.
For an OSS directory, null means I can't list the repo as "open source" without doing manual research. The ossfind ETL flags these with a "Verify license" note in the alternatives table rather than showing a green checkmark.
NOASSERTION — license file exists, can't be classified
NOASSERTION is GitHub's SPDX representation for "we found a LICENSE file but we cannot match it to any known SPDX identifier." This covers custom enterprise licenses, non-standard terms, and the various "fair-code" or "source-available" licenses that authors invented because they wanted something in between MIT and proprietary.
The canonical example I deal with: n8n has 188,900 GitHub stars and uses a Sustainable Use License. GitHub returns NOASSERTION. My licenseBucketKey() function maps NOASSERTION to "unknown" → commercial use risk "Unknown" → "treat as all-rights-reserved until verified." The page for the Zapier alternatives on ossfind shows a manual-review flag on n8n.
The challenge is that NOASSERTION is a spectrum. It covers n8n (explicitly non-commercial for hosted use), Elasticsearch pre-2021 (was Apache-2.0, then relicensed to a custom Elastic license), and small repos where someone pasted a README license note instead of using a proper LICENSE file. All three return NOASSERTION from the API and all three need different treatment.
BUSL-1.1 — classified SPDX but not OSI-approved open source
Business Source License 1.1 is in the SPDX license list, so GitHub returns BUSL-1.1 as the spdx_id. This makes it look like a standard recognized license alongside MIT and Apache-2.0. It is not.
BUSL-1.1 is a time-delayed commercial restriction: the software can be used freely for non-production use, but deploying it in production as a commercial service is prohibited until the "Change Date" specified in the license, at which point it converts to an OSI-approved open-source license. HashiCorp's Terraform used BUSL-1.1 starting in 2023 (then switched back under community pressure to MPL-2.0). Sentry currently uses a variant.
My ETL maps BUSL-1.1 to the restricted bucket: commercial use risk "High," note "Non-OSI terms restrict commercial or hosted use — read first." The page renders this clearly rather than showing "BUSL-1.1" next to MIT and Apache-2.0 as though they're equivalent.
One gotcha worth naming: BSL-1.0 is the Boost Software License — permissive, MIT-equivalent, no restrictions. BUSL-1.1 is the Business Source License — restricted, non-OSI. Both appear in the SPDX list with similar-looking identifiers. The decision-score.ts code has a comment specifically warning about this: if you pattern-match BSL before BUSL, you'll incorrectly classify Boost repos as restricted. The function tests for BUSL explicitly before the permissive catch-all.
SSPL-1.0 — AGPL variant with broader reach
Server Side Public License is SPDX-recognized (SSPL-1.0). MongoDB wrote it as a stricter alternative to AGPL-3.0 after concluding AGPL didn't go far enough for cloud providers. The key difference: AGPL requires releasing modifications to the software you deploy; SSPL-1.0 requires releasing the entire software stack you use to offer the software as a service — including your infrastructure tooling, deployment automation, and monitoring setup.
Redis used SSPL-1.0 before the community forked to Valkey under Apache-2.0. MongoDB still uses it.
For an OSS directory, the practical question is: can a developer use this to build their own product? Under SSPL, building a SaaS that runs the software as a service and doesn't release the entire stack is a license violation. My ETL groups SSPL-1.0 with AGPL in the network bucket — both are "network copyleft" licenses where even hosting triggers source-release obligations. The commercial use note: "Even a hosted/modified deployment can trigger source release."
The handling pattern
The full classification in decision-score.ts:
export function licenseBucketKey(lic: string | null | undefined): LicenseBucketKey {
const L = (lic ?? "").toUpperCase().trim();
if (!L || L === "NOASSERTION" || L === "OTHER") return "unknown";
const has = (re: RegExp) => re.test(L);
if (has(/\b(SSPL|AGPL)(?:[-\s]|$)/)) return "network";
if (has(/\b(GPL|EUPL)(?:[-\s]|$)/)) return "strong";
if (has(/\b(LGPL|MPL|EPL|CDDL)(?:[-\s]|$)/)) return "weak";
if (has(/\b(BUSL|ELASTIC|POLYFORM|COMMONS[-\s]?CLAUSE)(?:[-\s]|$)/)) return "restricted";
if (has(/\b(MIT|APACHE|BSD|0BSD|ISC|UNLICENSE|ZLIB|WTFPL|BSL-1)(?:[-\s.]|$)/)) return "permissive";
return "unknown";
}
The order matters: network before strong before weak before restricted before permissive. A compound license like "MIT WITH Commons-Clause" should map to restricted, not permissive. Testing for the restrictive cases first prevents permissive prefixes from short-circuiting the match.
The four cases here — null, NOASSERTION, BUSL-1.1, SSPL-1.0 — each land in unknown or restricted or network. All three map to "High" or "Unknown" commercial use risk on the directory page. A developer who glanced at a raw spdx_id field would not immediately know that any of these differ from MIT in a commercially relevant way. The classification layer is the part that makes the raw API data useful.
Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.
Top comments (0)