48,975 HF Models with Conflicting License Tags and Lineage: A License Audit Graph

A full-Hub lineage audit finds nearly 50,000 models with permissive tags but restricted upstream terms, plus a fail-closed tool, FinePrint.
The article builds a model lineage graph from a full Hugging Face snapshot to quantify the gap between licenses declared on model cards and the terms actually inherited. Of 242,170 models downstream of restricted-term models, 48,975 declare permissive licenses such as Apache-2.0 or MIT, with 5.88 million combined downloads, and 11,442 explicitly permit commercial use. The main source of wrong labels is Unsloth's save.py template, which unconditionally writes apache-2.0 and accounts for 54% of violating samples. The author uses FalkorDB to store lineage, datasets, licenses, and terms together, cutting full-Hub traversal from hours to milliseconds and enabling counterfactual queries. The resulting FinePrint returns ALLOW, CONDITIONS, or BLOCK and uses a fail-closed policy, misjudging restricted models as clean 0 times across 120 sealed test models with a median response of 9 ms.
A License Problem Amplified by a Template
When you open a model page on Hugging Face, the information you see usually has only one layer: the license the uploader filled in, plus an optional base model. But the legal terms that actually govern how the model can be used come from every ancestor along its entire derivative chain. If you fine-tune a model that was already fine-tuned on Llama 3.2, the Llama 3.2 agreement is inherited along with the weights—no matter what your model card says.
Someone built the lineage relationships of the entire Hub into a graph to quantify the problem: how often do the labels on model cards disagree with the terms they actually inherit? The conclusion: among 242,170 models sitting downstream of a model with restricted terms, 48,975 declare permissive licenses such as apache-2.0 or MIT—and these models were downloaded 5.88 million times in the 30 days before the snapshot. Of those, 11,442 explicitly say "commercial use permitted," while some ancestor of theirs does not.

Scale of the Data: 3.16 Million Models and 920,000 Lineage Edges
The analysis is based on daily snapshots from the public dataset cfahlgren1/hub-stats. Using the data from October 18, 2026 as an example, the Hub has 3,161,594 models. The author parsed the data with DuckDB, loaded it into the graph database FalkorDB, and built four types of nodes and relationships:
- One
Modelnode per model, carrying license, task type, model size, and download count; - 927,431 lineage edges, subdivided by
base_modeltag intoFINETUNE_OF,ADAPTER_OF,MERGE_OF, andQUANTIZED_FROMtypes; - 386,483
TRAINED_ONedges pointing to 66,243 datasets; - 47
Licensenodes, each linked to the specific terms it imposes, with original-text citations and source links.
There is an easily overlooked detail here: a large number of models on Hugging Face have only other in their license field, with the real name in license_name. 123,135 models fall into this category, and 43,164 of them use FLUX.1 [dev]—a non-commercial license. If an audit looks only at license tags, this entire batch would be missed.
Where the Wrong Labels Come From: A Chain Reaction from a Default Template
Among the 48,975 models that are "permissively tagged but restricted upstream," 54% carry exactly the same set of tags: unsloth, text-generation-inference, license:apache-2.0. This comes from the model card template in Unsloth's save.py—it unconditionally writes license: apache-2.0 on upload, regardless of the base model's license. When users fine-tune Llama or Qwen with Unsloth and click push, the card is automatically written as Apache.
This is not deliberate violation by anyone; it is a default value amplified by tens of thousands of uploads. The author wrote a patch for it: read the license from the base model (following Unsloth's own mirror all the way to the original model), and fall back to other when it cannot be determined. The patch covers both the library and Unsloth Studio, and Unsloth's own model card and export tests pass.
The graph can also locate the source of the wrong labels: 38,489 models declare a permissive license directly on top of a restricted parent model; beneath 2,438 of those are another 9,386 models that copied the wrong label from the parent rather than writing it themselves.
Unsloth is an open-source fine-tuning framework for consumer GPUs, known for significantly reducing memory usage and training time, and widely used in the Hugging Face community to do LoRA fine-tuning of models such as Llama and Qwen. Its workflow is usually "load base model → fine-tune → push to Hub," and the model card template in save.py is exactly the component invoked during the push step. The template's job is to auto-generate the YAML metadata in the README, including fields such as base_model, library_name, and license. The problem is that these fields are hard-coded to fixed values rather than inherited from the base model, so the more popular the template becomes, the larger the absolute number of wrong labels. This also explains why the wrong labels are so homogeneous: 54% of violating models share the same set of tags, reflecting the same code path rather than deliberate choices by the same group of users.
Why a Graph Database Instead of the API
The Hugging Face API can only list a model's direct children, 1,000 per page. Finding all 21,639 descendants of Llama-3.1-8B this way would require 21,653 calls, which at a measured 0.61 seconds each comes to about 3.7 hours. FalkorDB returns the same result in 70 milliseconds.
Running a Python BFS over an edge list in memory can also be fast. The real value of the graph is putting lineage, datasets, licenses, and terms in the same store: single-model checks and full-Hub audits share one copy of the data, and adding new edges requires no rebuild. The obligations a model inherits can be derived in a single query—traverse upward through ancestors, follow each ancestor's LICENSED_UNDER edge to its license, then collect the terms.
The author also uses algo.SPpaths to produce the three shortest paths by which a license reaches a given model, as presentable evidence. PageRank over the lineage shows that the most depended-upon model on the entire Hub is black-forest-labs/FLUX.1-dev—a non-commercial model. Another question only a graph can answer quickly is "what if": if Black Forest Labs changed FLUX.1-dev to Apache-2.0 tomorrow, FinePrint would use GRAPH.COPY to duplicate this family of 43,700 models, change the license on the copy, re-traverse, and delete the copy—about half a second per request. The result: 27,592 models would still retain a non-commercial tag, because their own cards copied FLUX's license and would need manual changes by each uploader.
FalkorDB is a graph database based on sparse matrices, compatible with the openCypher query language, and can be thought of as the graph storage module in the Redis ecosystem, supporting persistence of nodes, edges, and properties as well as graph algorithms (shortest path, PageRank, etc.). It suits this scenario because lineage queries are essentially multi-hop reachability computations: determining a model's license obligations requires repeatedly traversing upward along edges such as FINETUNE_OF and MERGE_OF, whereas a relational database or layer-by-layer API calls would require many round trips. algo.SPpaths is the shortest-path algorithm procedure provided by FalkorDB, which can enumerate several shortest paths between two nodes; the author uses it to explicitly output the inheritance chain of "how the license reaches this model" as evidence. GRAPH.COPY is used to duplicate an entire subgraph, so that counterfactual simulations can safely modify licenses and recompute on the copy without polluting production data.
FinePrint: Outputting ALLOW / CONDITIONS / BLOCK
These capabilities are packaged into a tool called FinePrint. Given a model id and an intended use (commercial or non-commercial), it returns ALLOW, CONDITIONS, or BLOCK, along with every inherited term, the license text containing that term, and the path in the model family tree.
Its design principle is fail closed: if any link in the chain is unknown, an ancestor's license is outside the recognized set, or a card declaration cannot be parsed, it will not return ALLOW.
For the special case of models that do not declare a base model, FinePrint introduces a constrained LLM agent: deepseek-ai/DeepSeek-R1-Distill-Llama-8B lists only MIT and no base model, so from tags alone the answer can only be UNKNOWN. The agent reads the model card, quotes the sentence "DeepSeekAd-R1-Distill-Llama-8B is derived from Llama3.1-8B-Base," finds meta-llama/Llama-3.1-8B in the graph, and writes a typed edge with source and citation. The rule engine can then see Llama 3.1's terms. The LLM does not write queries or make rulings; it only supplies evidence the rules can verify, and every claim must be quoted verbatim from the model card.
The tool forms cover scenarios developers commonly use: an MCP server lets coding agents check models, find clean alternatives with the same task and size, and draft declaration files; fineprint.guard() intercepts a banned model before from_pretrained downloads the weights; fineprint check works as a CLI that fails a CI build when a repository introduces a model that is banned for the current use.
fail closed is a default strategy in security engineering: when the system cannot confirm whether a condition is met, it defaults to denying or erroring rather than allowing. The opposite, fail open, tends to allow when uncertain, which looks like a better user experience but translates directly into compliance risk in license audits—an ancestor of unknown origin may be exactly the non-commercial model. That is why FinePrint uniformly returns UNKNOWN rather than ALLOW when the chain breaks, a license is not in the recognized set, or a card field cannot be parsed, accepting the loss of some cases it could have answered. This is the same design orientation as ranking "misjudging a restricted model as clean" as the primary metric in the evaluation: the cost of errors is highly asymmetric, and the cost of a missed violation far exceeds the cost of extra UNKNOWNs.
Evaluation: 0 Cases of Misjudging a Restricted Model as Clean
The author built a sealed test set containing 120 models in 7 groups (restricted terms two or more hops away, one or more hops away, clean models, FLUX derivatives, merged models, random samples, etc.). The gold standard comes from an independent script that traverses lineage through the online Hugging Face API, bypassing the author's data snapshot or graph, and the file was hashed before any system ran it.
The most critical metric is the number of times a restricted model is judged clean, because such errors directly lead to non-compliant releases. Looking only at the declared license, 33 of 69 were wrong; looking only at the direct parent model (that is, the information shown on the model page), 19 were wrong; having an LLM read the model card, 8 were wrong.
The strongest baseline was an LLM agent using the online Hub API as a tool, given 10 calls per model and instructed to trace every base model and check every license. On one tag revision it answered more correctly than FinePrint (92 vs. 80), mainly because FinePrint answers UNKNOWN for models that declare no license at all. But it still judged restricted models as clean 5 times, and all 5 were quantized copies in merge trees with restricted ancestors many hops above—it ran out of calls or hit gated repositories and could not get there. It needed a median of 11 seconds and about 4 Hub calls per model.
FinePrint judged restricted models as clean 0 times, found the restricted terms in all 19 cases where they were two or more hops away, and had a median response time of 9 milliseconds with no model API calls. It says UNKNOWN when it does not know, which loses some answers, but that is the right trade-off for this kind of tool.
This Is Not a Legal Conclusion
The author explicitly states: these are discrepancies between declared licenses and upstream terms, not legal rulings. Some models may have authorizations the author cannot see. Whether the terms of a non-commercial dataset propagate to weights trained on it is legally undecided, and FinePrint marks such cases as "disputed" rather than treating them as settled. The tool's goal is to surface the problem, along with the evidence, before release.
All numbers come from scripts listed in the repository's CLAIMS.md, and the entire project can be rebuilt with a single reproduce.sh. The online demo is at fineprint-plum.vercel.app, and the code is at github.com/kinnuworks/fineprint.
Key Points
- A single default template (Unsloth's
save.py) accounts for 54% of the wrong labels, with tens of thousands of uploads writing Apache-2.0 onto derivatives of restricted models. - The graph database cuts full-Hub lineage audits from hours to milliseconds and supports counterfactual queries such as "what if upstream changes its license."
- The correct posture for auditing is fail closed: answer UNKNOWN when you do not know, rather than giving a potentially non-compliant ALLOW.
Related articles

Cursor AI Programming in Practice: Build a Student Management System from Scratch in 30 Minutes
A Bilibili creator demonstrates Cursor AI programming: generating a student management system in 30 minutes with natural language. Details on Agent mode, Claude selection, prompt techniques, and thresholds.

Top 20 AI Coding Tools Ranking: From Rock-Solid to Lackluster, a Scored Matrix
Bilibili creator releases top 20 AI coding tools ranking, tiered T0–T4 from rock-solid to lackluster, with Claude Code and Codex tied at T0. This article details the scoring dimensions (capability, reliability, robustness, durability, cost, domestic availability) and tool performances to help developers choose the right AI coding assistant.

Vibe Coding Systematic Tutorial: Master AI-Assisted Programming from Zero
A three-month Vibe Coding tutorial from a Bilibili creator covering Codex, ClaudeCode, and Cursor, from core logic to commercial project practice for beginners.