knowyourai 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: knowyourai
3
+ Version: 0.1.0
4
+ Summary: Check that the open-weight models on your machine are what they claim to be.
5
+ Keywords: ai-security,supply-chain,gguf,safetensors,llm,provenance,lockfile
6
+ Author: Pradyoth P.
7
+ License-Expression: Apache-2.0
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Security
13
+ Requires-Python: >=3.11
14
+ Project-URL: Homepage, https://github.com/ppradyoth/knowyourai
15
+ Project-URL: Issues, https://github.com/ppradyoth/knowyourai/issues
16
+ Description-Content-Type: text/markdown
17
+
18
+ # knowyourai
19
+
20
+ **Is the model on your machine the model it claims to be?**
21
+
22
+ You pulled a 5 GB file from a stranger's repo. The model card says which model it is, what licence
23
+ it has, and that nothing odd happens when you chat with it. `knowyourai` checks those claims
24
+ against the bytes, and tells you when something changes after you approved it.
25
+
26
+ ```bash
27
+ uvx knowyourai scan
28
+ ```
29
+
30
+ ```text
31
+ STATUS COMPONENT REVISION FORMAT SIZE
32
+ review hf:someone/Model-GGUF b17cb02dd882 gguf Q4_K 4.4 GB
33
+ consistent hf:org/embedding-model 1110a243fdf4 safetensors F32 87.3 MB
34
+
35
+ hf:someone/Model-GGUF
36
+ [high] TMPL002 Chat template changes behaviour when a message contains specific text
37
+ evidence: model.gguf#chat_template: branches on 'wire the funds'
38
+ fix: Read the branch. A template has no reason to react to what a user says.
39
+ ```
40
+
41
+ - **No setup.** One command finds every model in the Hugging Face cache, Ollama and LM Studio.
42
+ - **Never runs a model.** It reads headers and metadata. No torch, no GPU, no dependencies.
43
+ - **Offline by default.** No telemetry. `--online` talks only to the model's own registry.
44
+ - **Check before you pull.** `scan hf:org/name --online` inspects a repo on the Hub by reading file
45
+ headers over range requests. A 471 GB repo takes about 9 seconds and downloads nothing.
46
+ - **A lockfile for models.** `lock` pins what you approved; `check` fails CI when it moves.
47
+
48
+ ## Why
49
+
50
+ In the 200 most-downloaded GGUF repos on Hugging Face (October 2026), 128 ship a chat template
51
+ that can be compared with their declared base model's. **61 of them, 48%, differ.** Most of those
52
+ are deliberate fixes by the quantizer. The point is that you are often not running the template
53
+ the original publisher wrote, and a chat template is a program that runs on every prompt.
54
+
55
+ Model scanners look for malware in a file. This tool asks a different question: is this the model
56
+ the label says it is, and has it changed since you approved it?
57
+
58
+ Status: alpha.
59
+
60
+ ## Tested on real models
61
+
62
+ Run against real repos on the Hugging Face Hub, with nothing downloaded:
63
+
64
+ | Repo | Listed size | Time | Result |
65
+ |---|---|---|---|
66
+ | `unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF` | 471.6 GB | 7.4 s | template differs from the base model's (a labelled fix) |
67
+ | `Qwen/Qwen3-4B-GGUF` | 14.7 GB | 6.5 s | template differs from the publisher's own safetensors repo |
68
+ | `google/gemma-4-E2B-it-qat-q4_0-gguf` | 4.0 GB | 14.6 s | template identical, structure matches |
69
+ | `jinaai/jina-embeddings-v3` | 5.4 GB | 9.8 s | config loads model code from a second repository |
70
+ | `microsoft/Phi-3-mini-4k-instruct` | 7.1 GB | 5.4 s | config maps to Python code shipped in the repo |
71
+
72
+ Fourteen remote scans, a local run with digest verification, the 200-repo study with a
73
+ per-publisher breakdown, 21 attack fixtures, the three bugs real data exposed in this tool, and
74
+ what has not been tested yet are all in **[FIELD-TESTS.md](FIELD-TESTS.md)**.
75
+
76
+ ## Use it
77
+
78
+ ```bash
79
+ uvx knowyourai scan # everything on this machine
80
+ uvx knowyourai scan ./models # a directory
81
+ uvx knowyourai scan hf:org/name --online # a Hub repo, without downloading it
82
+ uvx knowyourai scan --online # compare local models with their publishers
83
+ uvx knowyourai lock hf:org/name # write ai.lock
84
+ uvx knowyourai check # exit 1 if anything locked has changed
85
+ ```
86
+
87
+ | Command | What it does |
88
+ |---|---|
89
+ | `scan [targets]` | Inventory and inspect. `--fail-on high` makes it a CI gate. |
90
+ | `lock [targets]` | Write `ai.lock`: revisions, file digests and chat-template digests. |
91
+ | `check` | Exit 1 if anything in `ai.lock` has changed. `--strict` also fails on missing or unlocked components. |
92
+
93
+ Targets are paths, `hf:org/name[@revision]` or `ollama:name:tag`. `kyai` is a short alias.
94
+
95
+ ### In CI
96
+
97
+ ```yaml
98
+ - uses: ppradyoth/knowyourai@v0.1.0
99
+ with:
100
+ command: check
101
+ ```
102
+
103
+ ### As a pre-commit hook
104
+
105
+ ```yaml
106
+ repos:
107
+ - repo: https://github.com/ppradyoth/knowyourai
108
+ rev: v0.1.0
109
+ hooks:
110
+ - id: knowyourai-check
111
+ ```
112
+
113
+ ## What it checks
114
+
115
+ Offline:
116
+
117
+ | Rule | Finding |
118
+ |---|---|
119
+ | `FMT001` | File content is not the format its extension claims |
120
+ | `FMT002` | safetensors header is invalid, or the file has unreferenced bytes or overlapping tensors |
121
+ | `FMT004` | GGUF header could not be parsed within safe limits |
122
+ | `EXEC001` | Weights are a pickle (low if every import is a tensor rebuild function, medium otherwise) |
123
+ | `EXEC002` | Pickle imports a dangerous callable, has an unresolvable import, or is malformed |
124
+ | `EXEC003` | Config `auto_map` points to Python code (high if the code lives in another repo) |
125
+ | `EXEC004` | Model ships Python files |
126
+ | `EXEC005` | Config sets `_attn_implementation_internal`, a reported indicator of CVE-2026-4372 |
127
+ | `TMPL001` | Chat template reaches into Python internals |
128
+ | `TMPL002` | Chat template branches on the text of a message |
129
+ | `TMPL003` | Chat template contains a hard-coded URL |
130
+ | `INTEG001` | With `--rehash`: a file no longer matches the digest it is stored under |
131
+
132
+ With `--online`, against the publisher:
133
+
134
+ | Rule | Finding |
135
+ |---|---|
136
+ | `DRIFT001` | The Hub's `main` has moved since the local copy was downloaded |
137
+ | `DRIFT002` | The Ollama tag now points to different layers |
138
+ | `CLAIM002` | Licence differs from the declared base model's (high if it is more permissive) |
139
+ | `CLAIM003` | Layer count or hidden size does not match the declared base model |
140
+ | `CLAIM004` | No licence is declared anywhere |
141
+ | `TMPL010` | Chat template differs from the base model's |
142
+
143
+ When a template differs from the base model's, `TMPL002` and `TMPL003` hits that the base model
144
+ does not have are raised to high. Hits the base model also has are lowered to low.
145
+
146
+ ## Statuses
147
+
148
+ | Status | Meaning |
149
+ |---|---|
150
+ | `violation` | At least one high finding |
151
+ | `review` | At least one medium finding |
152
+ | `unverifiable` | Something could not be inspected, for example an ONNX file |
153
+ | `consistent` | Nothing contradicted the model's claims in the checks that ran |
154
+
155
+ `consistent` is not a safety verdict. Offline, nothing is compared against the publisher.
156
+
157
+ ## Network use
158
+
159
+ Without `--online` the tool makes no network requests. With it, requests go only to
160
+ `huggingface.co` (and the CDN its downloads redirect to) and `registry.ollama.ai`. There is no
161
+ telemetry. `HF_TOKEN`, if set, is sent to `huggingface.co` only, for gated models.
162
+
163
+ ## Limits
164
+
165
+ - It does not judge behaviour. A backdoor trained into the weights is invisible to it.
166
+ - Pickle, ONNX, Keras and TensorFlow files are not deeply analysed. Use ModelAudit or fickling.
167
+ - Licence and base-model checks compare declared metadata, not the weights themselves.
168
+ - Template rules are heuristics. A finding means "read this", not "this is malicious".
169
+
170
+ ## Measuring the Hub
171
+
172
+ `study/gguf_study.py` measures the most-downloaded GGUF repos using Hub metadata only. For each
173
+ repo it compares the chat template and licence with the declared base model's. It downloads
174
+ nothing, resumes if interrupted, and writes one JSON line per repo.
175
+
176
+ ```bash
177
+ uv run python study/gguf_study.py --limit 200 --out study/out/gguf.jsonl
178
+ ```
179
+
180
+ The template it reads is the one the Hub parsed from the repo, and the base model's template is
181
+ whatever its `main` holds today. A difference means the two are not the same now, not that the
182
+ quantizer changed anything.
183
+
184
+ ## Development
185
+
186
+ ```bash
187
+ uv sync --all-groups
188
+ uv run pytest
189
+ uv run ruff check
190
+ ```
@@ -0,0 +1,173 @@
1
+ # knowyourai
2
+
3
+ **Is the model on your machine the model it claims to be?**
4
+
5
+ You pulled a 5 GB file from a stranger's repo. The model card says which model it is, what licence
6
+ it has, and that nothing odd happens when you chat with it. `knowyourai` checks those claims
7
+ against the bytes, and tells you when something changes after you approved it.
8
+
9
+ ```bash
10
+ uvx knowyourai scan
11
+ ```
12
+
13
+ ```text
14
+ STATUS COMPONENT REVISION FORMAT SIZE
15
+ review hf:someone/Model-GGUF b17cb02dd882 gguf Q4_K 4.4 GB
16
+ consistent hf:org/embedding-model 1110a243fdf4 safetensors F32 87.3 MB
17
+
18
+ hf:someone/Model-GGUF
19
+ [high] TMPL002 Chat template changes behaviour when a message contains specific text
20
+ evidence: model.gguf#chat_template: branches on 'wire the funds'
21
+ fix: Read the branch. A template has no reason to react to what a user says.
22
+ ```
23
+
24
+ - **No setup.** One command finds every model in the Hugging Face cache, Ollama and LM Studio.
25
+ - **Never runs a model.** It reads headers and metadata. No torch, no GPU, no dependencies.
26
+ - **Offline by default.** No telemetry. `--online` talks only to the model's own registry.
27
+ - **Check before you pull.** `scan hf:org/name --online` inspects a repo on the Hub by reading file
28
+ headers over range requests. A 471 GB repo takes about 9 seconds and downloads nothing.
29
+ - **A lockfile for models.** `lock` pins what you approved; `check` fails CI when it moves.
30
+
31
+ ## Why
32
+
33
+ In the 200 most-downloaded GGUF repos on Hugging Face (October 2026), 128 ship a chat template
34
+ that can be compared with their declared base model's. **61 of them, 48%, differ.** Most of those
35
+ are deliberate fixes by the quantizer. The point is that you are often not running the template
36
+ the original publisher wrote, and a chat template is a program that runs on every prompt.
37
+
38
+ Model scanners look for malware in a file. This tool asks a different question: is this the model
39
+ the label says it is, and has it changed since you approved it?
40
+
41
+ Status: alpha.
42
+
43
+ ## Tested on real models
44
+
45
+ Run against real repos on the Hugging Face Hub, with nothing downloaded:
46
+
47
+ | Repo | Listed size | Time | Result |
48
+ |---|---|---|---|
49
+ | `unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF` | 471.6 GB | 7.4 s | template differs from the base model's (a labelled fix) |
50
+ | `Qwen/Qwen3-4B-GGUF` | 14.7 GB | 6.5 s | template differs from the publisher's own safetensors repo |
51
+ | `google/gemma-4-E2B-it-qat-q4_0-gguf` | 4.0 GB | 14.6 s | template identical, structure matches |
52
+ | `jinaai/jina-embeddings-v3` | 5.4 GB | 9.8 s | config loads model code from a second repository |
53
+ | `microsoft/Phi-3-mini-4k-instruct` | 7.1 GB | 5.4 s | config maps to Python code shipped in the repo |
54
+
55
+ Fourteen remote scans, a local run with digest verification, the 200-repo study with a
56
+ per-publisher breakdown, 21 attack fixtures, the three bugs real data exposed in this tool, and
57
+ what has not been tested yet are all in **[FIELD-TESTS.md](FIELD-TESTS.md)**.
58
+
59
+ ## Use it
60
+
61
+ ```bash
62
+ uvx knowyourai scan # everything on this machine
63
+ uvx knowyourai scan ./models # a directory
64
+ uvx knowyourai scan hf:org/name --online # a Hub repo, without downloading it
65
+ uvx knowyourai scan --online # compare local models with their publishers
66
+ uvx knowyourai lock hf:org/name # write ai.lock
67
+ uvx knowyourai check # exit 1 if anything locked has changed
68
+ ```
69
+
70
+ | Command | What it does |
71
+ |---|---|
72
+ | `scan [targets]` | Inventory and inspect. `--fail-on high` makes it a CI gate. |
73
+ | `lock [targets]` | Write `ai.lock`: revisions, file digests and chat-template digests. |
74
+ | `check` | Exit 1 if anything in `ai.lock` has changed. `--strict` also fails on missing or unlocked components. |
75
+
76
+ Targets are paths, `hf:org/name[@revision]` or `ollama:name:tag`. `kyai` is a short alias.
77
+
78
+ ### In CI
79
+
80
+ ```yaml
81
+ - uses: ppradyoth/knowyourai@v0.1.0
82
+ with:
83
+ command: check
84
+ ```
85
+
86
+ ### As a pre-commit hook
87
+
88
+ ```yaml
89
+ repos:
90
+ - repo: https://github.com/ppradyoth/knowyourai
91
+ rev: v0.1.0
92
+ hooks:
93
+ - id: knowyourai-check
94
+ ```
95
+
96
+ ## What it checks
97
+
98
+ Offline:
99
+
100
+ | Rule | Finding |
101
+ |---|---|
102
+ | `FMT001` | File content is not the format its extension claims |
103
+ | `FMT002` | safetensors header is invalid, or the file has unreferenced bytes or overlapping tensors |
104
+ | `FMT004` | GGUF header could not be parsed within safe limits |
105
+ | `EXEC001` | Weights are a pickle (low if every import is a tensor rebuild function, medium otherwise) |
106
+ | `EXEC002` | Pickle imports a dangerous callable, has an unresolvable import, or is malformed |
107
+ | `EXEC003` | Config `auto_map` points to Python code (high if the code lives in another repo) |
108
+ | `EXEC004` | Model ships Python files |
109
+ | `EXEC005` | Config sets `_attn_implementation_internal`, a reported indicator of CVE-2026-4372 |
110
+ | `TMPL001` | Chat template reaches into Python internals |
111
+ | `TMPL002` | Chat template branches on the text of a message |
112
+ | `TMPL003` | Chat template contains a hard-coded URL |
113
+ | `INTEG001` | With `--rehash`: a file no longer matches the digest it is stored under |
114
+
115
+ With `--online`, against the publisher:
116
+
117
+ | Rule | Finding |
118
+ |---|---|
119
+ | `DRIFT001` | The Hub's `main` has moved since the local copy was downloaded |
120
+ | `DRIFT002` | The Ollama tag now points to different layers |
121
+ | `CLAIM002` | Licence differs from the declared base model's (high if it is more permissive) |
122
+ | `CLAIM003` | Layer count or hidden size does not match the declared base model |
123
+ | `CLAIM004` | No licence is declared anywhere |
124
+ | `TMPL010` | Chat template differs from the base model's |
125
+
126
+ When a template differs from the base model's, `TMPL002` and `TMPL003` hits that the base model
127
+ does not have are raised to high. Hits the base model also has are lowered to low.
128
+
129
+ ## Statuses
130
+
131
+ | Status | Meaning |
132
+ |---|---|
133
+ | `violation` | At least one high finding |
134
+ | `review` | At least one medium finding |
135
+ | `unverifiable` | Something could not be inspected, for example an ONNX file |
136
+ | `consistent` | Nothing contradicted the model's claims in the checks that ran |
137
+
138
+ `consistent` is not a safety verdict. Offline, nothing is compared against the publisher.
139
+
140
+ ## Network use
141
+
142
+ Without `--online` the tool makes no network requests. With it, requests go only to
143
+ `huggingface.co` (and the CDN its downloads redirect to) and `registry.ollama.ai`. There is no
144
+ telemetry. `HF_TOKEN`, if set, is sent to `huggingface.co` only, for gated models.
145
+
146
+ ## Limits
147
+
148
+ - It does not judge behaviour. A backdoor trained into the weights is invisible to it.
149
+ - Pickle, ONNX, Keras and TensorFlow files are not deeply analysed. Use ModelAudit or fickling.
150
+ - Licence and base-model checks compare declared metadata, not the weights themselves.
151
+ - Template rules are heuristics. A finding means "read this", not "this is malicious".
152
+
153
+ ## Measuring the Hub
154
+
155
+ `study/gguf_study.py` measures the most-downloaded GGUF repos using Hub metadata only. For each
156
+ repo it compares the chat template and licence with the declared base model's. It downloads
157
+ nothing, resumes if interrupted, and writes one JSON line per repo.
158
+
159
+ ```bash
160
+ uv run python study/gguf_study.py --limit 200 --out study/out/gguf.jsonl
161
+ ```
162
+
163
+ The template it reads is the one the Hub parsed from the repo, and the base model's template is
164
+ whatever its `main` holds today. A difference means the two are not the same now, not that the
165
+ quantizer changed anything.
166
+
167
+ ## Development
168
+
169
+ ```bash
170
+ uv sync --all-groups
171
+ uv run pytest
172
+ uv run ruff check
173
+ ```
@@ -0,0 +1,100 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.11,<0.12"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "knowyourai"
7
+ version = "0.1.0"
8
+ description = "Check that the open-weight models on your machine are what they claim to be."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "Apache-2.0"
12
+ keywords = [
13
+ "ai-security",
14
+ "supply-chain",
15
+ "gguf",
16
+ "safetensors",
17
+ "llm",
18
+ "provenance",
19
+ "lockfile",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Environment :: Console",
24
+ "Intended Audience :: Developers",
25
+ "Programming Language :: Python :: 3",
26
+ "Topic :: Security",
27
+ ]
28
+ dependencies = []
29
+
30
+ [[project.authors]]
31
+ name = "Pradyoth P."
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/ppradyoth/knowyourai"
35
+ Issues = "https://github.com/ppradyoth/knowyourai/issues"
36
+
37
+ [project.scripts]
38
+ knowyourai = "knowyourai.cli:main"
39
+ kyai = "knowyourai.cli:main"
40
+
41
+ [dependency-groups]
42
+ lint = [
43
+ "ruff",
44
+ "ty",
45
+ ]
46
+ test = ["pytest"]
47
+
48
+ [[dependency-groups.dev]]
49
+ include-group = "lint"
50
+
51
+ [[dependency-groups.dev]]
52
+ include-group = "test"
53
+
54
+ [tool.ruff]
55
+ line-length = 100
56
+ target-version = "py311"
57
+
58
+ [tool.ruff.lint]
59
+ select = ["ALL"]
60
+ ignore = [
61
+ "D",
62
+ "COM812",
63
+ "ISC001",
64
+ "ANN401",
65
+ "PLR2004",
66
+ "C901",
67
+ "PLR0911",
68
+ "PLR0912",
69
+ "PLR0913",
70
+ "PLR0915",
71
+ "PLR0917",
72
+ "CPY",
73
+ "SIM905",
74
+ "TRY301",
75
+ ]
76
+
77
+ [tool.ruff.lint.per-file-ignores]
78
+ "tests/*" = [
79
+ "S101",
80
+ "ANN",
81
+ "INP001",
82
+ "SLF001",
83
+ "ARG001",
84
+ "TRY003",
85
+ "EM101",
86
+ "TC003",
87
+ ]
88
+ "study/*" = [
89
+ "INP001",
90
+ "T201",
91
+ ]
92
+ "src/knowyourai/report.py" = ["T201"]
93
+ "src/knowyourai/cli.py" = ["T201"]
94
+
95
+ [tool.pytest.ini_options]
96
+ testpaths = ["tests"]
97
+ pythonpath = ["tests"]
98
+
99
+ [tool.ty.environment]
100
+ python-version = "3.11"
@@ -0,0 +1,70 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.11,<0.12"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "knowyourai"
7
+ version = "0.1.0"
8
+ description = "Check that the open-weight models on your machine are what they claim to be."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "Apache-2.0"
12
+ authors = [{ name = "Pradyoth P." }]
13
+ keywords = ["ai-security", "supply-chain", "gguf", "safetensors", "llm", "provenance", "lockfile"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Security",
20
+ ]
21
+ dependencies = []
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/ppradyoth/knowyourai"
25
+ Issues = "https://github.com/ppradyoth/knowyourai/issues"
26
+
27
+ [project.scripts]
28
+ knowyourai = "knowyourai.cli:main"
29
+ kyai = "knowyourai.cli:main"
30
+
31
+ [dependency-groups]
32
+ dev = [{ include-group = "lint" }, { include-group = "test" }]
33
+ lint = ["ruff", "ty"]
34
+ test = ["pytest"]
35
+
36
+ [tool.ruff]
37
+ line-length = 100
38
+ target-version = "py311"
39
+
40
+ [tool.ruff.lint]
41
+ select = ["ALL"]
42
+ ignore = [
43
+ "D",
44
+ "COM812",
45
+ "ISC001",
46
+ "ANN401",
47
+ "PLR2004",
48
+ "C901",
49
+ "PLR0911",
50
+ "PLR0912",
51
+ "PLR0913",
52
+ "PLR0915",
53
+ "PLR0917",
54
+ "CPY",
55
+ "SIM905",
56
+ "TRY301",
57
+ ]
58
+
59
+ [tool.ruff.lint.per-file-ignores]
60
+ "tests/*" = ["S101", "ANN", "INP001", "SLF001", "ARG001", "TRY003", "EM101", "TC003"]
61
+ "study/*" = ["INP001", "T201"]
62
+ "src/knowyourai/report.py" = ["T201"]
63
+ "src/knowyourai/cli.py" = ["T201"]
64
+
65
+ [tool.pytest.ini_options]
66
+ testpaths = ["tests"]
67
+ pythonpath = ["tests"]
68
+
69
+ [tool.ty.environment]
70
+ python-version = "3.11"
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from knowyourai.cli import main
4
+
5
+ sys.exit(main())