isongraph-ext 0.3.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.
- isongraph_ext-0.3.0/LICENSE +21 -0
- isongraph_ext-0.3.0/PKG-INFO +144 -0
- isongraph_ext-0.3.0/README.md +114 -0
- isongraph_ext-0.3.0/pyproject.toml +47 -0
- isongraph_ext-0.3.0/setup.cfg +4 -0
- isongraph_ext-0.3.0/src/isongraph_ext/__init__.py +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/REGISTRY.ison +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/MANIFEST.ison +22 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/domains.ison +8 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/receipts/cpp.ison +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/receipts/cs.ison +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/receipts/js.ison +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/receipts/python.ison +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/receipts/rust.ison +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/baselines/ce9d3b78afd79cbdffdd67546bcb5278dc38be70/receipts/ts.ison +3 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igdm/__init__.py +13 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igdm/__main__.py +92 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igdm/bind.py +49 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igdm/errors.py +12 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igdm/payload.py +478 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igdm/records.py +121 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igdm/verify.py +118 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/__init__.py +14 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/__main__.py +186 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/bind.py +52 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/errors.py +14 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/lineage.py +100 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/locators.py +51 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/payload.py +511 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/records.py +134 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/resolve.py +63 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igpv/verify.py +178 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/__init__.py +15 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/__main__.py +86 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/admit.py +95 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/baselines.py +133 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/bind.py +57 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/binding.py +241 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/digest.py +33 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/errors.py +14 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/receipt_rules.py +34 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/registry.py +10 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/source.py +84 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/verify.py +141 -0
- isongraph_ext-0.3.0/src/isongraph_ext/igx/versions.py +60 -0
- isongraph_ext-0.3.0/src/isongraph_ext/py.typed +0 -0
- isongraph_ext-0.3.0/src/isongraph_ext.egg-info/PKG-INFO +144 -0
- isongraph_ext-0.3.0/src/isongraph_ext.egg-info/SOURCES.txt +79 -0
- isongraph_ext-0.3.0/src/isongraph_ext.egg-info/dependency_links.txt +1 -0
- isongraph_ext-0.3.0/src/isongraph_ext.egg-info/requires.txt +6 -0
- isongraph_ext-0.3.0/src/isongraph_ext.egg-info/top_level.txt +1 -0
- isongraph_ext-0.3.0/tests/test_admit.py +114 -0
- isongraph_ext-0.3.0/tests/test_baselines.py +131 -0
- isongraph_ext-0.3.0/tests/test_bind.py +93 -0
- isongraph_ext-0.3.0/tests/test_binding.py +169 -0
- isongraph_ext-0.3.0/tests/test_cli.py +97 -0
- isongraph_ext-0.3.0/tests/test_digest.py +34 -0
- isongraph_ext-0.3.0/tests/test_examples.py +184 -0
- isongraph_ext-0.3.0/tests/test_igdm_cli.py +52 -0
- isongraph_ext-0.3.0/tests/test_igdm_payload.py +115 -0
- isongraph_ext-0.3.0/tests/test_igdm_records.py +27 -0
- isongraph_ext-0.3.0/tests/test_igdm_rules.py +139 -0
- isongraph_ext-0.3.0/tests/test_igdm_verify.py +136 -0
- isongraph_ext-0.3.0/tests/test_igpv_bind.py +62 -0
- isongraph_ext-0.3.0/tests/test_igpv_cli.py +112 -0
- isongraph_ext-0.3.0/tests/test_igpv_lineage.py +87 -0
- isongraph_ext-0.3.0/tests/test_igpv_locators.py +48 -0
- isongraph_ext-0.3.0/tests/test_igpv_payload.py +128 -0
- isongraph_ext-0.3.0/tests/test_igpv_records.py +32 -0
- isongraph_ext-0.3.0/tests/test_igpv_resolve.py +73 -0
- isongraph_ext-0.3.0/tests/test_igpv_rules.py +92 -0
- isongraph_ext-0.3.0/tests/test_igpv_verify.py +176 -0
- isongraph_ext-0.3.0/tests/test_package.py +38 -0
- isongraph_ext-0.3.0/tests/test_packaging.py +54 -0
- isongraph_ext-0.3.0/tests/test_registry.py +41 -0
- isongraph_ext-0.3.0/tests/test_source.py +91 -0
- isongraph_ext-0.3.0/tests/test_vectors.py +138 -0
- isongraph_ext-0.3.0/tests/test_vectors_igdm.py +103 -0
- isongraph_ext-0.3.0/tests/test_vectors_igpv.py +167 -0
- isongraph_ext-0.3.0/tests/test_verify.py +176 -0
- isongraph_ext-0.3.0/tests/test_versions.py +38 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mahesh Vaikri
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: isongraph-ext
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: ISONGraphExt - IGX/1 extension bindings, IGPV/1 provenance and IGDM/1 datamatics with verifiable identity claims over ISONGraph Core
|
|
5
|
+
Author: Mahesh Vaikri
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/ISON-format/isongraph-ext
|
|
8
|
+
Project-URL: Issues, https://github.com/ISON-format/isongraph-ext/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/ISON-format/isongraph-ext/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Specifications, https://github.com/ISON-format/isongraph-ext/tree/main/docs/blueprint
|
|
11
|
+
Project-URL: ISONGraph Core, https://github.com/ISON-format/isongraph
|
|
12
|
+
Keywords: isongraph,ison,graph,provenance,lineage,integrity,verification,conformance
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: ison-py==1.2.0
|
|
25
|
+
Requires-Dist: ison-graph==1.4.1
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<img src="https://raw.githubusercontent.com/ISON-format/isongraph-ext/main/assets/isongraph-banner.png" alt="ISONGraph" width="720">
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<h1 align="center">isongraph-ext</h1>
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<strong>Prove what a graph is, where it came from, and how to show it — without changing the graph.</strong>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<a href="https://github.com/ISON-format/isongraph-ext/actions/workflows/ci.yml"><img src="https://github.com/ISON-format/isongraph-ext/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
43
|
+
<img src="https://img.shields.io/badge/license-MIT-yellow.svg" alt="MIT License">
|
|
44
|
+
<img src="https://img.shields.io/badge/python-3.12%2B-blue.svg" alt="Python 3.12+">
|
|
45
|
+
<img src="https://img.shields.io/badge/status-alpha-orange.svg" alt="Alpha">
|
|
46
|
+
</p>
|
|
47
|
+
|
|
48
|
+
[ISONGraph](https://github.com/ISON-format/isongraph) stores a graph as a compact, token-efficient file. That file deliberately says nothing about **who made it**, **where its facts came from**, or **how a viewer should draw it**. `isongraph-ext` answers those three questions with small **companion files that sit next to the graph** and are tied to its exact bytes by SHA-256. The graph itself is never modified, and everything works offline.
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
graph.ison the ordinary ISONGraph; Core reads it exactly as before
|
|
52
|
+
|-- graph.igx.ison IGX/1 binding "is this the graph I was promised?"
|
|
53
|
+
|-- graph.igpv.ison IGPV/1 provenance "where did this come from?"
|
|
54
|
+
`-- graph.igdm.ison IGDM/1 datamatics "how should a viewer present it?"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Each companion is optional and independent. A consumer that knows none of them still has the whole graph.
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install isongraph-ext
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Python 3.12+. Pure Python. The only dependencies are `ison-graph==1.4.1` and `ison-py==1.2.0`, pinned exactly, because the package verifies against the Core baseline it was measured with.
|
|
66
|
+
|
|
67
|
+
## Try it
|
|
68
|
+
|
|
69
|
+
Bind a graph to its bytes, verify it, then change one byte:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from ison_graph import ISONGraph
|
|
73
|
+
from isongraph_ext import igx
|
|
74
|
+
|
|
75
|
+
g = ISONGraph()
|
|
76
|
+
g.add_node("person", "ada", name="Ada")
|
|
77
|
+
g.add_node("person", "grace", name="Grace")
|
|
78
|
+
g.add_edge("knows", ("person", "ada"), ("person", "grace"))
|
|
79
|
+
data = g.to_ison().encode("utf-8")
|
|
80
|
+
|
|
81
|
+
binding = igx.bind(data, serialization_profile="ison-v1", producer_port="python", identity_claim="artifact")
|
|
82
|
+
result, decision = igx.verify_and_admit(binding, data)
|
|
83
|
+
print("admitted:", decision.admitted, "| steps:", ", ".join(result.steps_passed))
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
igx.verify(binding, data + b"\n# edited")
|
|
87
|
+
except igx.IGXError as e:
|
|
88
|
+
print("tampered:", e.code, "at", e.step)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
admitted: True | steps: V0, V1, V2, V3, V4, V5
|
|
93
|
+
tampered: SOURCE_BINDING_MISMATCH at V4
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Verification is a fixed sequence of numbered steps, and every failure names the step and a stable error code.
|
|
97
|
+
|
|
98
|
+
## Command line
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
python -m isongraph_ext.igx verify graph.ison graph.igx.ison # exit 0 ok, 1 invalid, 64 usage
|
|
102
|
+
python -m isongraph_ext.igx admit graph.ison graph.igx.ison # exit 0 admitted, 2 denied
|
|
103
|
+
python -m isongraph_ext.igpv verify graph.ison graph.igx.ison graph.igpv.ison [--source SHA256=PATH]
|
|
104
|
+
python -m isongraph_ext.igpv origins <graph-sha256> graph.igpv.ison --node person ada
|
|
105
|
+
python -m isongraph_ext.igdm verify graph.ison graph.igx.ison graph.igdm.ison
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Output is itself ISON, so receipts can quote it verbatim.
|
|
109
|
+
|
|
110
|
+
## What you get
|
|
111
|
+
|
|
112
|
+
| Capability | What it does |
|
|
113
|
+
|---|---|
|
|
114
|
+
| **IGX/1** binding | Pins the exact bytes, the Core version they were written with and an identity claim (`none`, `artifact`, `producer_bound`). Unknown capabilities are reported or refused, never silently ignored. |
|
|
115
|
+
| **IGPV/1** provenance | Attributes nodes and edges to source documents, with byte or line spans checked against the real bytes, and records derivation lineage: *this node stands for those nodes in an earlier graph*. |
|
|
116
|
+
| **IGDM/1** datamatics | Renderer-neutral presentation **intent**: semantic roles, emphasis, badges, groups, layout hints and accessibility text. Never colours, fonts or coordinates. |
|
|
117
|
+
|
|
118
|
+
## Built to be checked
|
|
119
|
+
|
|
120
|
+
- **Byte-exact.** Companion files are canonical by construction: one graph, one companion, one set of bytes, one SHA-256.
|
|
121
|
+
- **Specified before coded.** Frozen specifications for IGX/1, IGPV/1 and IGDM/1 live in the [repository](https://github.com/ISON-format/isongraph-ext/tree/main/docs/blueprint); the code follows them, never the reverse.
|
|
122
|
+
- **Conformance vectors.** 156 byte-exact vectors and about 450 tests are replayed in CI on every change.
|
|
123
|
+
- **Pinned to a measured Core.** The package embeds the Core baseline it was verified against, plus Core's conformance receipts, so identity claims are checkable rather than asserted.
|
|
124
|
+
- **Hostile-input safe.** Parsing is bounded by explicit size and row budgets; verification reports the first failing step instead of guessing.
|
|
125
|
+
|
|
126
|
+
## Examples
|
|
127
|
+
|
|
128
|
+
Four runnable demonstrations (a non-software provenance graph, a supply-chain presentation graph, a minimal generic SVG adapter and a Core-only consumer) are in the [examples folder](https://github.com/ISON-format/isongraph-ext/tree/main/examples) of the repository.
|
|
129
|
+
|
|
130
|
+
## Status and limits
|
|
131
|
+
|
|
132
|
+
This is an **early-stage (alpha)** release. Know what it does not do:
|
|
133
|
+
|
|
134
|
+
- A verified binding proves **reproducibility and consistency, not authorship**; `producer_port` is a checked claim, not authentication.
|
|
135
|
+
- Provenance proves origin and lineage, **not that an extracted statement is true**.
|
|
136
|
+
- The cross-port identity claim and lossy producer-bound admission are reserved. Revision, delta, projection and DAG capabilities are not implemented.
|
|
137
|
+
- IGDM/1 carries presentation **intent only** and renders nothing.
|
|
138
|
+
- Python reference implementation only. Verification is an offline cost: roughly 5 seconds for a 10,000-node graph with a capability.
|
|
139
|
+
|
|
140
|
+
## Links
|
|
141
|
+
|
|
142
|
+
[Source and issues](https://github.com/ISON-format/isongraph-ext) · [Changelog](https://github.com/ISON-format/isongraph-ext/blob/main/CHANGELOG.md) · [Security policy](https://github.com/ISON-format/isongraph-ext/blob/main/SECURITY.md) · [ISONGraph Core](https://github.com/ISON-format/isongraph)
|
|
143
|
+
|
|
144
|
+
MIT — Copyright (c) 2026 Mahesh Vaikri.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/ISON-format/isongraph-ext/main/assets/isongraph-banner.png" alt="ISONGraph" width="720">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">isongraph-ext</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Prove what a graph is, where it came from, and how to show it — without changing the graph.</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://github.com/ISON-format/isongraph-ext/actions/workflows/ci.yml"><img src="https://github.com/ISON-format/isongraph-ext/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
13
|
+
<img src="https://img.shields.io/badge/license-MIT-yellow.svg" alt="MIT License">
|
|
14
|
+
<img src="https://img.shields.io/badge/python-3.12%2B-blue.svg" alt="Python 3.12+">
|
|
15
|
+
<img src="https://img.shields.io/badge/status-alpha-orange.svg" alt="Alpha">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
[ISONGraph](https://github.com/ISON-format/isongraph) stores a graph as a compact, token-efficient file. That file deliberately says nothing about **who made it**, **where its facts came from**, or **how a viewer should draw it**. `isongraph-ext` answers those three questions with small **companion files that sit next to the graph** and are tied to its exact bytes by SHA-256. The graph itself is never modified, and everything works offline.
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
graph.ison the ordinary ISONGraph; Core reads it exactly as before
|
|
22
|
+
|-- graph.igx.ison IGX/1 binding "is this the graph I was promised?"
|
|
23
|
+
|-- graph.igpv.ison IGPV/1 provenance "where did this come from?"
|
|
24
|
+
`-- graph.igdm.ison IGDM/1 datamatics "how should a viewer present it?"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Each companion is optional and independent. A consumer that knows none of them still has the whole graph.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install isongraph-ext
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Python 3.12+. Pure Python. The only dependencies are `ison-graph==1.4.1` and `ison-py==1.2.0`, pinned exactly, because the package verifies against the Core baseline it was measured with.
|
|
36
|
+
|
|
37
|
+
## Try it
|
|
38
|
+
|
|
39
|
+
Bind a graph to its bytes, verify it, then change one byte:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ison_graph import ISONGraph
|
|
43
|
+
from isongraph_ext import igx
|
|
44
|
+
|
|
45
|
+
g = ISONGraph()
|
|
46
|
+
g.add_node("person", "ada", name="Ada")
|
|
47
|
+
g.add_node("person", "grace", name="Grace")
|
|
48
|
+
g.add_edge("knows", ("person", "ada"), ("person", "grace"))
|
|
49
|
+
data = g.to_ison().encode("utf-8")
|
|
50
|
+
|
|
51
|
+
binding = igx.bind(data, serialization_profile="ison-v1", producer_port="python", identity_claim="artifact")
|
|
52
|
+
result, decision = igx.verify_and_admit(binding, data)
|
|
53
|
+
print("admitted:", decision.admitted, "| steps:", ", ".join(result.steps_passed))
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
igx.verify(binding, data + b"\n# edited")
|
|
57
|
+
except igx.IGXError as e:
|
|
58
|
+
print("tampered:", e.code, "at", e.step)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
admitted: True | steps: V0, V1, V2, V3, V4, V5
|
|
63
|
+
tampered: SOURCE_BINDING_MISMATCH at V4
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Verification is a fixed sequence of numbered steps, and every failure names the step and a stable error code.
|
|
67
|
+
|
|
68
|
+
## Command line
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python -m isongraph_ext.igx verify graph.ison graph.igx.ison # exit 0 ok, 1 invalid, 64 usage
|
|
72
|
+
python -m isongraph_ext.igx admit graph.ison graph.igx.ison # exit 0 admitted, 2 denied
|
|
73
|
+
python -m isongraph_ext.igpv verify graph.ison graph.igx.ison graph.igpv.ison [--source SHA256=PATH]
|
|
74
|
+
python -m isongraph_ext.igpv origins <graph-sha256> graph.igpv.ison --node person ada
|
|
75
|
+
python -m isongraph_ext.igdm verify graph.ison graph.igx.ison graph.igdm.ison
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Output is itself ISON, so receipts can quote it verbatim.
|
|
79
|
+
|
|
80
|
+
## What you get
|
|
81
|
+
|
|
82
|
+
| Capability | What it does |
|
|
83
|
+
|---|---|
|
|
84
|
+
| **IGX/1** binding | Pins the exact bytes, the Core version they were written with and an identity claim (`none`, `artifact`, `producer_bound`). Unknown capabilities are reported or refused, never silently ignored. |
|
|
85
|
+
| **IGPV/1** provenance | Attributes nodes and edges to source documents, with byte or line spans checked against the real bytes, and records derivation lineage: *this node stands for those nodes in an earlier graph*. |
|
|
86
|
+
| **IGDM/1** datamatics | Renderer-neutral presentation **intent**: semantic roles, emphasis, badges, groups, layout hints and accessibility text. Never colours, fonts or coordinates. |
|
|
87
|
+
|
|
88
|
+
## Built to be checked
|
|
89
|
+
|
|
90
|
+
- **Byte-exact.** Companion files are canonical by construction: one graph, one companion, one set of bytes, one SHA-256.
|
|
91
|
+
- **Specified before coded.** Frozen specifications for IGX/1, IGPV/1 and IGDM/1 live in the [repository](https://github.com/ISON-format/isongraph-ext/tree/main/docs/blueprint); the code follows them, never the reverse.
|
|
92
|
+
- **Conformance vectors.** 156 byte-exact vectors and about 450 tests are replayed in CI on every change.
|
|
93
|
+
- **Pinned to a measured Core.** The package embeds the Core baseline it was verified against, plus Core's conformance receipts, so identity claims are checkable rather than asserted.
|
|
94
|
+
- **Hostile-input safe.** Parsing is bounded by explicit size and row budgets; verification reports the first failing step instead of guessing.
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
Four runnable demonstrations (a non-software provenance graph, a supply-chain presentation graph, a minimal generic SVG adapter and a Core-only consumer) are in the [examples folder](https://github.com/ISON-format/isongraph-ext/tree/main/examples) of the repository.
|
|
99
|
+
|
|
100
|
+
## Status and limits
|
|
101
|
+
|
|
102
|
+
This is an **early-stage (alpha)** release. Know what it does not do:
|
|
103
|
+
|
|
104
|
+
- A verified binding proves **reproducibility and consistency, not authorship**; `producer_port` is a checked claim, not authentication.
|
|
105
|
+
- Provenance proves origin and lineage, **not that an extracted statement is true**.
|
|
106
|
+
- The cross-port identity claim and lossy producer-bound admission are reserved. Revision, delta, projection and DAG capabilities are not implemented.
|
|
107
|
+
- IGDM/1 carries presentation **intent only** and renders nothing.
|
|
108
|
+
- Python reference implementation only. Verification is an offline cost: roughly 5 seconds for a 10,000-node graph with a capability.
|
|
109
|
+
|
|
110
|
+
## Links
|
|
111
|
+
|
|
112
|
+
[Source and issues](https://github.com/ISON-format/isongraph-ext) · [Changelog](https://github.com/ISON-format/isongraph-ext/blob/main/CHANGELOG.md) · [Security policy](https://github.com/ISON-format/isongraph-ext/blob/main/SECURITY.md) · [ISONGraph Core](https://github.com/ISON-format/isongraph)
|
|
113
|
+
|
|
114
|
+
MIT — Copyright (c) 2026 Mahesh Vaikri.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "isongraph-ext"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "ISONGraphExt - IGX/1 extension bindings, IGPV/1 provenance and IGDM/1 datamatics with verifiable identity claims over ISONGraph Core"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{name = "Mahesh Vaikri"}]
|
|
13
|
+
requires-python = ">=3.12"
|
|
14
|
+
# Exact pins: the reference runtime IS the implementation the Core baseline was measured with (17 §3, §4 V0).
|
|
15
|
+
dependencies = ["ison-py==1.2.0", "ison-graph==1.4.1"]
|
|
16
|
+
keywords = ["isongraph", "ison", "graph", "provenance", "lineage", "integrity", "verification", "conformance"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = ["pytest>=8", "build>=1.2"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Repository = "https://github.com/ISON-format/isongraph-ext"
|
|
33
|
+
Issues = "https://github.com/ISON-format/isongraph-ext/issues"
|
|
34
|
+
Changelog = "https://github.com/ISON-format/isongraph-ext/blob/main/CHANGELOG.md"
|
|
35
|
+
Specifications = "https://github.com/ISON-format/isongraph-ext/tree/main/docs/blueprint"
|
|
36
|
+
"ISONGraph Core" = "https://github.com/ISON-format/isongraph"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.packages.find]
|
|
39
|
+
where = ["src"]
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.package-data]
|
|
42
|
+
isongraph_ext = ["py.typed", "baselines/REGISTRY.ison", "baselines/*/MANIFEST.ison", "baselines/*/domains.ison", "baselines/*/receipts/*.ison"]
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
testpaths = ["tests"]
|
|
46
|
+
addopts = "-q --tb=short"
|
|
47
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
meta.baseline
|
|
2
|
+
canonical_writer_dist canonical_writer_dist_version core_commit core_format core_runtime_dist core_runtime_dist_version core_semantic_spec core_vector_corpus_commit core_vector_corpus_digest core_vector_corpus_version core_version
|
|
3
|
+
ison-py 1.2.0 ce9d3b78afd79cbdffdd67546bcb5278dc38be70 isongraph ison-graph 1.4.1 ISONGraph-Semantic/2-draft f88af8bd29680e7b6e7716585be936d5738eb55f 1d1a6648abeeadece24b43a163949d02241911706866236cc32bd6b61745bcb0 1 1.4.1
|
|
4
|
+
|
|
5
|
+
table.core_runtime
|
|
6
|
+
module sha256
|
|
7
|
+
ison_graph/__init__.py 2a13a36d63d85e766df961aae2d52550c2e9c52f60a5ee46560ddce4a301ece4
|
|
8
|
+
ison_graph/multigraph.py 6d4e874a75f43d8914422bf2006f995a6334bfe4fe9eb5552c0f2f31fb4ff3e6
|
|
9
|
+
ison_graph/query.py d0c34fa8649de066049a1670ccc00f8af28029c2ed7374256b9fb9a978423cd4
|
|
10
|
+
ison_graph/schema.py 06f82dc13eb02794b11d0386c8f8090b0f9a5d2f48be839e828703e2e5e2d1b4
|
|
11
|
+
ison_graph/structural.py ffa53c3edd5dc76e6e7db20b74473f59bb6b329b09479445699eeba59e2232c7
|
|
12
|
+
ison_graph/viz.py e62949eb91c9a3fe4be3972de94baddf7f41f034f5c61db51ceb8bcb4ca9b6d6
|
|
13
|
+
|
|
14
|
+
table.files
|
|
15
|
+
path sha256
|
|
16
|
+
"domains.ison" 6d9db34ceedd5c1309060bc2dfeddcdd302b6664737bfe904c14391870254897
|
|
17
|
+
receipts/cpp.ison b330dc23e4d882c71942d035d1848f03841f932345c823f93b560c86f40e83a8
|
|
18
|
+
receipts/cs.ison f37d41566cf69c3e6b933483946bc8559c8ceb160c2d6d4ea36caab8f94ccfc7
|
|
19
|
+
receipts/js.ison acbe2104736148bf19c0d93649781b9c142066bec4fe1ab0b6fdc116abc39ed6
|
|
20
|
+
receipts/python.ison cece1259ac61172a1d519b5ad31cef8b29ff3e466f629f4cb72b4f34fdda9ac0
|
|
21
|
+
receipts/rust.ison 6dd9c2dcf5407f598233f6c3f21bf831a02f141a509ff45b112729ae35690040
|
|
22
|
+
receipts/ts.ison 393ba7e64e6c2be3606f2e8b03124dd2ff0cef33d069f38c6cb0b075dec268c4
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
meta.core_conformance_receipt
|
|
2
|
+
canonicalized core_commit core_format core_version corpus_commit corpus_digest corpus_version defect lossy pass port port_version posture rejected_as_expected semantic_spec todo total
|
|
3
|
+
0 ce9d3b78afd79cbdffdd67546bcb5278dc38be70 isongraph 1.4.1 f88af8bd29680e7b6e7716585be936d5738eb55f 1d1a6648abeeadece24b43a163949d02241911706866236cc32bd6b61745bcb0 1 0 0 0 cpp 1.4.1 PARTIAL 0 ISONGraph-Semantic/2-draft 32 32
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
meta.core_conformance_receipt
|
|
2
|
+
canonicalized core_commit core_format core_version corpus_commit corpus_digest corpus_version defect lossy pass port port_version posture rejected_as_expected semantic_spec todo total
|
|
3
|
+
0 ce9d3b78afd79cbdffdd67546bcb5278dc38be70 isongraph 1.4.1 f88af8bd29680e7b6e7716585be936d5738eb55f 1d1a6648abeeadece24b43a163949d02241911706866236cc32bd6b61745bcb0 1 0 0 0 cs 1.4.1 PARTIAL 0 ISONGraph-Semantic/2-draft 32 32
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
meta.core_conformance_receipt
|
|
2
|
+
canonicalized core_commit core_format core_version corpus_commit corpus_digest corpus_version defect lossy pass port port_version posture rejected_as_expected semantic_spec todo total
|
|
3
|
+
6 ce9d3b78afd79cbdffdd67546bcb5278dc38be70 isongraph 1.4.1 f88af8bd29680e7b6e7716585be936d5738eb55f 1d1a6648abeeadece24b43a163949d02241911706866236cc32bd6b61745bcb0 1 1 4 16 js 1.4.1 KNOWN_DEFECT 5 ISONGraph-Semantic/2-draft 0 32
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
meta.core_conformance_receipt
|
|
2
|
+
canonicalized core_commit core_format core_version corpus_commit corpus_digest corpus_version defect lossy pass port port_version posture rejected_as_expected semantic_spec todo total
|
|
3
|
+
7 ce9d3b78afd79cbdffdd67546bcb5278dc38be70 isongraph 1.4.1 f88af8bd29680e7b6e7716585be936d5738eb55f 1d1a6648abeeadece24b43a163949d02241911706866236cc32bd6b61745bcb0 1 0 0 19 python 1.4.1 FULL 6 ISONGraph-Semantic/2-draft 0 32
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
meta.core_conformance_receipt
|
|
2
|
+
canonicalized core_commit core_format core_version corpus_commit corpus_digest corpus_version defect lossy pass port port_version posture rejected_as_expected semantic_spec todo total
|
|
3
|
+
0 ce9d3b78afd79cbdffdd67546bcb5278dc38be70 isongraph 1.4.1 f88af8bd29680e7b6e7716585be936d5738eb55f 1d1a6648abeeadece24b43a163949d02241911706866236cc32bd6b61745bcb0 1 0 0 0 rust 1.4.1 PARTIAL 0 ISONGraph-Semantic/2-draft 32 32
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
meta.core_conformance_receipt
|
|
2
|
+
canonicalized core_commit core_format core_version corpus_commit corpus_digest corpus_version defect lossy pass port port_version posture rejected_as_expected semantic_spec todo total
|
|
3
|
+
6 ce9d3b78afd79cbdffdd67546bcb5278dc38be70 isongraph 1.4.1 f88af8bd29680e7b6e7716585be936d5738eb55f 1d1a6648abeeadece24b43a163949d02241911706866236cc32bd6b61745bcb0 1 1 4 16 ts 1.4.1 KNOWN_DEFECT 5 ISONGraph-Semantic/2-draft 0 32
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""IGDM/1 Datamatics (19-IGDM1-DESIGN.md)."""
|
|
2
|
+
|
|
3
|
+
from ..igpv.records import EdgeAddress, NodeRef
|
|
4
|
+
from ..igx.registry import DEFAULT_REGISTRY
|
|
5
|
+
from .bind import bind, verify_and_admit
|
|
6
|
+
from .errors import IGDMError
|
|
7
|
+
from .payload import DatamaticsPayload, check_references, unrecognized_roles
|
|
8
|
+
from .records import DEFAULT_LIMITS, EdgeHints, Group, LayoutHints, Limits, Member, NodeHints
|
|
9
|
+
from .verify import DatamaticsVerification, verify
|
|
10
|
+
|
|
11
|
+
__all__ = ["IGDMError", "DEFAULT_LIMITS", "EdgeHints", "Group", "LayoutHints", "Limits", "Member", "NodeHints", "EdgeAddress", "NodeRef",
|
|
12
|
+
"DEFAULT_REGISTRY", "DatamaticsPayload", "check_references", "unrecognized_roles", "DatamaticsVerification", "verify",
|
|
13
|
+
"bind", "verify_and_admit"]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""CLI (19 §4): verify -> 0/1/2, 64 on usage; one canonical ISON document per response."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from ison_parser import Block, Document, dumps_canonical
|
|
11
|
+
|
|
12
|
+
from ..igx.admit import AdmissionPolicy
|
|
13
|
+
from ..igx.binding import ExtensionBinding
|
|
14
|
+
from ..igx.errors import IGXError
|
|
15
|
+
from .bind import verify_and_admit
|
|
16
|
+
from .errors import IGDMError
|
|
17
|
+
|
|
18
|
+
EX_USAGE = 64 # BSD sysexits: never collides with 2 (admission denied) or 1 (invalid binding/payload)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _Parser(argparse.ArgumentParser):
|
|
22
|
+
def error(self, message: str) -> None: # type: ignore[override]
|
|
23
|
+
self.print_usage(sys.stderr)
|
|
24
|
+
sys.stderr.write(f"error: {message}\n")
|
|
25
|
+
raise SystemExit(EX_USAGE)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _utf8_streams() -> None:
|
|
29
|
+
"""Output is UTF-8, LF-terminated ISON regardless of console locale or platform (same rule as the igx CLI)."""
|
|
30
|
+
for stream in (sys.stdout, sys.stderr):
|
|
31
|
+
reconfigure = getattr(stream, "reconfigure", None)
|
|
32
|
+
if reconfigure is not None:
|
|
33
|
+
reconfigure(encoding="utf-8", errors="strict", newline="\n")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _meta(name: str, row: dict) -> Block:
|
|
37
|
+
return Block("meta", name, list(row), [row])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _emit(blocks: list[Block]) -> None:
|
|
41
|
+
sys.stdout.write(dumps_canonical(Document(blocks=blocks)) + "\n")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _joined(items) -> str | None:
|
|
45
|
+
items = list(items)
|
|
46
|
+
return ",".join(items) if items else None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _kv(m: Mapping) -> str | None:
|
|
50
|
+
return _joined(f"{k}={v}" for k, v in m.items())
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main(argv: list[str]) -> int:
|
|
54
|
+
_utf8_streams()
|
|
55
|
+
ap = _Parser(prog="python -m isongraph_ext.igdm")
|
|
56
|
+
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
57
|
+
v = sub.add_parser("verify")
|
|
58
|
+
v.add_argument("graph", type=Path)
|
|
59
|
+
v.add_argument("binding", type=Path)
|
|
60
|
+
v.add_argument("payload", type=Path)
|
|
61
|
+
v.add_argument("--allow-retired", action="store_true")
|
|
62
|
+
a = ap.parse_args(argv)
|
|
63
|
+
try:
|
|
64
|
+
graph, binding_bytes, payload_bytes = a.graph.read_bytes(), a.binding.read_bytes(), a.payload.read_bytes()
|
|
65
|
+
except OSError as e:
|
|
66
|
+
ap.print_usage(sys.stderr)
|
|
67
|
+
sys.stderr.write(f"error: {e}\n")
|
|
68
|
+
return EX_USAGE
|
|
69
|
+
try:
|
|
70
|
+
binding = ExtensionBinding.from_ison(binding_bytes)
|
|
71
|
+
result, dm, decision = verify_and_admit(payload_bytes, binding, graph, policy=AdmissionPolicy(allow_retired_baselines=a.allow_retired))
|
|
72
|
+
except (IGXError, IGDMError) as e:
|
|
73
|
+
_emit([_meta("error", {"code": e.code, "field": e.field, "detail": e.detail, "step": e.step})])
|
|
74
|
+
return 1
|
|
75
|
+
_emit([
|
|
76
|
+
_meta("verification", {
|
|
77
|
+
"binding_digest": result.binding_digest, "semantic_verification": result.semantic_verification,
|
|
78
|
+
"source_canonical": result.source_canonical, "source_semantic_eligibility": result.source_semantic_eligibility,
|
|
79
|
+
"source_semantic_reason": result.source_semantic_reason, "steps_passed": _joined(result.steps_passed),
|
|
80
|
+
"unrecognized_optional": _joined(c.extension for c in result.unrecognized_optional)}),
|
|
81
|
+
_meta("datamatics", {
|
|
82
|
+
"payload_sha256": dm.payload_sha256, "binding_digest": dm.binding_digest, "target_artifact_sha256": dm.target_artifact_sha256,
|
|
83
|
+
"object_resolution": dm.object_resolution, "unrecognized_roles": _joined(dm.unrecognized_roles),
|
|
84
|
+
"counts": _kv(dm.counts), "steps_passed": _joined(dm.steps_passed)}),
|
|
85
|
+
_meta("admission", {"admitted": decision.admitted, "code": decision.code, "step": decision.step,
|
|
86
|
+
"receipt_posture": decision.receipt_posture, "reasons": _joined(decision.reasons)}),
|
|
87
|
+
])
|
|
88
|
+
return 0 if decision.admitted else 2
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Producer path and trustworthy consumer path (19 §4), mirroring igpv.bind."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping, Sequence
|
|
6
|
+
|
|
7
|
+
from ..igx.admit import DEFAULT_POLICY, AdmissionPolicy, AdmissionResult, admit
|
|
8
|
+
from ..igx.baselines import BASELINES, DEFAULT_BASELINE, Baseline
|
|
9
|
+
from ..igx.bind import bind as igx_bind
|
|
10
|
+
from ..igx.binding import Capability, Consumer, ExtensionBinding
|
|
11
|
+
from ..igx.digest import sha256_hex
|
|
12
|
+
from ..igx.registry import DEFAULT_REGISTRY, CapabilityRegistry
|
|
13
|
+
from ..igx.source import as_bytes
|
|
14
|
+
from ..igx.verify import VerificationResult
|
|
15
|
+
from . import records as R
|
|
16
|
+
from .errors import IGDMError
|
|
17
|
+
from .payload import DatamaticsPayload
|
|
18
|
+
from .verify import DatamaticsVerification, verify
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def bind(target: str | bytes, payload: DatamaticsPayload, *, requirement: str = "REQUIRED", serialization_profile: str,
|
|
22
|
+
producer_port: str, identity_claim: str, capabilities: Sequence[Capability] = (), consumer: Consumer | None = None,
|
|
23
|
+
baseline: Baseline = DEFAULT_BASELINE, registry: CapabilityRegistry = DEFAULT_REGISTRY,
|
|
24
|
+
policy: AdmissionPolicy = DEFAULT_POLICY, limits: R.Limits = R.DEFAULT_LIMITS) -> ExtensionBinding:
|
|
25
|
+
if requirement not in ("REQUIRED", "OPTIONAL"):
|
|
26
|
+
raise IGDMError("MALFORMED_DATAMATICS", "table.capabilities.igdm.requirement", "REQUIRED or OPTIONAL", "D1")
|
|
27
|
+
if any(c.extension == R.CAPABILITY for c in capabilities):
|
|
28
|
+
raise IGDMError("MALFORMED_DATAMATICS", "table.capabilities.igdm", "bind() adds the igdm row itself; do not pass one", "D1")
|
|
29
|
+
payload.check()
|
|
30
|
+
data = as_bytes(target)
|
|
31
|
+
actual = sha256_hex(data)
|
|
32
|
+
if payload.target_artifact_sha256 != actual:
|
|
33
|
+
raise IGDMError("TARGET_BINDING_MISMATCH", "meta.igdm.target_artifact_sha256", f"target bytes hash to {actual}", "D2")
|
|
34
|
+
if payload.target_serialization_profile != serialization_profile:
|
|
35
|
+
raise IGDMError("TARGET_BINDING_MISMATCH", "meta.igdm.target_serialization_profile",
|
|
36
|
+
f"{payload.target_serialization_profile} != {serialization_profile}", "D2")
|
|
37
|
+
caps = tuple(capabilities) + (Capability(R.CAPABILITY, R.CAPABILITY_RANGE, requirement, payload.digest),)
|
|
38
|
+
binding = igx_bind(data, serialization_profile=serialization_profile, producer_port=producer_port, identity_claim=identity_claim,
|
|
39
|
+
capabilities=caps, consumer=consumer, baseline=baseline, registry=registry, policy=policy)
|
|
40
|
+
verify(payload, binding, data, limits=limits, baselines={**BASELINES, baseline.core_commit: baseline}, registry=registry)
|
|
41
|
+
return binding
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def verify_and_admit(payload: str | bytes | DatamaticsPayload, binding: ExtensionBinding, target: str | bytes, *,
|
|
45
|
+
limits: R.Limits = R.DEFAULT_LIMITS, baselines: Mapping[str, Baseline] = BASELINES,
|
|
46
|
+
registry: CapabilityRegistry = DEFAULT_REGISTRY, policy: AdmissionPolicy = DEFAULT_POLICY
|
|
47
|
+
) -> tuple[VerificationResult, DatamaticsVerification, AdmissionResult]:
|
|
48
|
+
result, dm = verify(payload, binding, target, limits=limits, baselines=baselines, registry=registry)
|
|
49
|
+
return result, dm, admit(binding, result, policy=policy, baselines=baselines)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""The one exception type IGDM/1 raises (19 §3.1). Admission denials are the binding's results, not exceptions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IGDMError(Exception):
|
|
7
|
+
def __init__(self, code: str, field: str, detail: str, step: str) -> None:
|
|
8
|
+
super().__init__(f"{step} {code} {field}: {detail}")
|
|
9
|
+
self.code = code
|
|
10
|
+
self.field = field
|
|
11
|
+
self.detail = detail
|
|
12
|
+
self.step = step
|