attestplane 0.0.3a0__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.
- attestplane-0.0.3a0/.gitignore +47 -0
- attestplane-0.0.3a0/PKG-INFO +229 -0
- attestplane-0.0.3a0/README.md +184 -0
- attestplane-0.0.3a0/pyproject.toml +191 -0
- attestplane-0.0.3a0/src/attestplane/__init__.py +312 -0
- attestplane-0.0.3a0/src/attestplane/adapter_conformance.py +248 -0
- attestplane-0.0.3a0/src/attestplane/adapters/__init__.py +40 -0
- attestplane-0.0.3a0/src/attestplane/adapters/aios_spec.py +125 -0
- attestplane-0.0.3a0/src/attestplane/adapters/base.py +162 -0
- attestplane-0.0.3a0/src/attestplane/adapters/langfuse.py +247 -0
- attestplane-0.0.3a0/src/attestplane/adapters/langsmith.py +247 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/__init__.py +148 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/base.py +255 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/composite.py +106 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/eidas.py +164 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/http.py +337 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/mock.py +100 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/ocsp.py +239 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/rfc3161.py +395 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/sigstore.py +382 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/testing.py +690 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/verifier.py +428 -0
- attestplane-0.0.3a0/src/attestplane/anchoring/worker.py +282 -0
- attestplane-0.0.3a0/src/attestplane/canonical.py +177 -0
- attestplane-0.0.3a0/src/attestplane/canonical_text.py +124 -0
- attestplane-0.0.3a0/src/attestplane/cli/__init__.py +15 -0
- attestplane-0.0.3a0/src/attestplane/cli/main.py +348 -0
- attestplane-0.0.3a0/src/attestplane/event_payloads.py +581 -0
- attestplane-0.0.3a0/src/attestplane/event_types.py +88 -0
- attestplane-0.0.3a0/src/attestplane/hashchain.py +148 -0
- attestplane-0.0.3a0/src/attestplane/intoto.py +176 -0
- attestplane-0.0.3a0/src/attestplane/obligations/__init__.py +47 -0
- attestplane-0.0.3a0/src/attestplane/obligations/dora_article_8.json +106 -0
- attestplane-0.0.3a0/src/attestplane/obligations/eu_ai_act_article_12.json +161 -0
- attestplane-0.0.3a0/src/attestplane/obligations/registry.py +268 -0
- attestplane-0.0.3a0/src/attestplane/obligations/registry.schema.json +100 -0
- attestplane-0.0.3a0/src/attestplane/proof_bundle.py +432 -0
- attestplane-0.0.3a0/src/attestplane/py.typed +0 -0
- attestplane-0.0.3a0/src/attestplane/reason_codes.py +174 -0
- attestplane-0.0.3a0/src/attestplane/replay_verifier.py +196 -0
- attestplane-0.0.3a0/src/attestplane/settlement_verifier.py +214 -0
- attestplane-0.0.3a0/src/attestplane/signing/__init__.py +96 -0
- attestplane-0.0.3a0/src/attestplane/signing/base.py +316 -0
- attestplane-0.0.3a0/src/attestplane/signing/providers.py +253 -0
- attestplane-0.0.3a0/src/attestplane/signing/signer.py +437 -0
- attestplane-0.0.3a0/src/attestplane/signing/trust_roots.py +306 -0
- attestplane-0.0.3a0/src/attestplane/signing/verifier_ext.py +515 -0
- attestplane-0.0.3a0/src/attestplane/storage/__init__.py +34 -0
- attestplane-0.0.3a0/src/attestplane/storage/base.py +139 -0
- attestplane-0.0.3a0/src/attestplane/storage/jsonl.py +323 -0
- attestplane-0.0.3a0/src/attestplane/substrate.py +118 -0
- attestplane-0.0.3a0/src/attestplane/types.py +123 -0
- attestplane-0.0.3a0/src/attestplane/verifier.py +304 -0
- attestplane-0.0.3a0/tests/__init__.py +0 -0
- attestplane-0.0.3a0/tests/adapters/__init__.py +2 -0
- attestplane-0.0.3a0/tests/adapters/test_base.py +144 -0
- attestplane-0.0.3a0/tests/adapters/test_langfuse.py +223 -0
- attestplane-0.0.3a0/tests/adapters/test_langsmith.py +216 -0
- attestplane-0.0.3a0/tests/anchoring/__init__.py +2 -0
- attestplane-0.0.3a0/tests/anchoring/test_anchor_vectors.py +176 -0
- attestplane-0.0.3a0/tests/anchoring/test_anchoring.py +443 -0
- attestplane-0.0.3a0/tests/anchoring/test_eidas.py +227 -0
- attestplane-0.0.3a0/tests/anchoring/test_http.py +242 -0
- attestplane-0.0.3a0/tests/anchoring/test_multihop.py +249 -0
- attestplane-0.0.3a0/tests/anchoring/test_ocsp.py +161 -0
- attestplane-0.0.3a0/tests/anchoring/test_rfc3161.py +300 -0
- attestplane-0.0.3a0/tests/anchoring/test_sigstore.py +362 -0
- attestplane-0.0.3a0/tests/anchoring/test_worker.py +260 -0
- attestplane-0.0.3a0/tests/cli/__init__.py +2 -0
- attestplane-0.0.3a0/tests/cli/test_main.py +258 -0
- attestplane-0.0.3a0/tests/conformance/FIXTURE_HASHES.lock +26 -0
- attestplane-0.0.3a0/tests/conformance/__init__.py +0 -0
- attestplane-0.0.3a0/tests/conformance/anchor_vectors.json +55 -0
- attestplane-0.0.3a0/tests/conformance/generate_vectors.py +226 -0
- attestplane-0.0.3a0/tests/conformance/lease_lifecycle_event_vectors.json +170 -0
- attestplane-0.0.3a0/tests/conformance/negative/README.md +46 -0
- attestplane-0.0.3a0/tests/conformance/negative/broken_chain.json +71 -0
- attestplane-0.0.3a0/tests/conformance/negative/duplicate_event.json +72 -0
- attestplane-0.0.3a0/tests/conformance/negative/malformed_payload.json +71 -0
- attestplane-0.0.3a0/tests/conformance/negative/missing_event.json +50 -0
- attestplane-0.0.3a0/tests/conformance/negative/reordered_event.json +71 -0
- attestplane-0.0.3a0/tests/conformance/policy_check_event_vectors.json +209 -0
- attestplane-0.0.3a0/tests/conformance/proof_bundle_policy_trace_vectors.json +45 -0
- attestplane-0.0.3a0/tests/conformance/reason_codes_vectors.json +87 -0
- attestplane-0.0.3a0/tests/conformance/replay_event_vectors.json +330 -0
- attestplane-0.0.3a0/tests/conformance/settlement_precondition_vectors.json +197 -0
- attestplane-0.0.3a0/tests/conformance/signature_vectors.json +222 -0
- attestplane-0.0.3a0/tests/conformance/text_vectors.json +91 -0
- attestplane-0.0.3a0/tests/conformance/vectors.json +212 -0
- attestplane-0.0.3a0/tests/fault_injection/__init__.py +2 -0
- attestplane-0.0.3a0/tests/fault_injection/test_fault_matrix.py +350 -0
- attestplane-0.0.3a0/tests/fixtures/adapter_conformance/langfuse_v1.json +77 -0
- attestplane-0.0.3a0/tests/fixtures/adapter_conformance/langsmith_v1.json +78 -0
- attestplane-0.0.3a0/tests/obligations/__init__.py +2 -0
- attestplane-0.0.3a0/tests/obligations/test_registry.py +284 -0
- attestplane-0.0.3a0/tests/signing/__init__.py +2 -0
- attestplane-0.0.3a0/tests/signing/test_base.py +239 -0
- attestplane-0.0.3a0/tests/signing/test_proof_bundle_signatures.py +180 -0
- attestplane-0.0.3a0/tests/signing/test_providers.py +278 -0
- attestplane-0.0.3a0/tests/signing/test_signature_vectors.py +246 -0
- attestplane-0.0.3a0/tests/signing/test_signer.py +313 -0
- attestplane-0.0.3a0/tests/signing/test_trust_roots.py +244 -0
- attestplane-0.0.3a0/tests/signing/test_verifier_ext.py +431 -0
- attestplane-0.0.3a0/tests/storage/__init__.py +2 -0
- attestplane-0.0.3a0/tests/storage/test_jsonl.py +311 -0
- attestplane-0.0.3a0/tests/storage/test_storage_compatibility.py +138 -0
- attestplane-0.0.3a0/tests/test_adapter_conformance.py +182 -0
- attestplane-0.0.3a0/tests/test_canonical.py +136 -0
- attestplane-0.0.3a0/tests/test_canonical_text.py +156 -0
- attestplane-0.0.3a0/tests/test_conformance.py +100 -0
- attestplane-0.0.3a0/tests/test_conformance_negative.py +141 -0
- attestplane-0.0.3a0/tests/test_event_payloads.py +183 -0
- attestplane-0.0.3a0/tests/test_event_types.py +81 -0
- attestplane-0.0.3a0/tests/test_hashchain.py +158 -0
- attestplane-0.0.3a0/tests/test_import_surface.py +88 -0
- attestplane-0.0.3a0/tests/test_intoto.py +194 -0
- attestplane-0.0.3a0/tests/test_proof_bundle.py +335 -0
- attestplane-0.0.3a0/tests/test_proof_bundle_policy_trace.py +202 -0
- attestplane-0.0.3a0/tests/test_properties.py +217 -0
- attestplane-0.0.3a0/tests/test_public_api_manifest.py +130 -0
- attestplane-0.0.3a0/tests/test_reason_codes.py +117 -0
- attestplane-0.0.3a0/tests/test_release_claims.py +97 -0
- attestplane-0.0.3a0/tests/test_release_provenance_manifest.py +99 -0
- attestplane-0.0.3a0/tests/test_replay_event.py +189 -0
- attestplane-0.0.3a0/tests/test_schemas_v1.py +419 -0
- attestplane-0.0.3a0/tests/test_settlement_verifier.py +122 -0
- attestplane-0.0.3a0/tests/test_substrate.py +155 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# OS
|
|
2
|
+
.DS_Store
|
|
3
|
+
Thumbs.db
|
|
4
|
+
|
|
5
|
+
# Editor
|
|
6
|
+
.vscode/
|
|
7
|
+
.idea/
|
|
8
|
+
*.swp
|
|
9
|
+
*.swo
|
|
10
|
+
*~
|
|
11
|
+
|
|
12
|
+
# Environment / secrets
|
|
13
|
+
.env
|
|
14
|
+
.env.*
|
|
15
|
+
!.env.example
|
|
16
|
+
*.pem
|
|
17
|
+
*.key
|
|
18
|
+
secrets/
|
|
19
|
+
|
|
20
|
+
# Build / dependencies (placeholders for upcoming SDKs)
|
|
21
|
+
node_modules/
|
|
22
|
+
dist/
|
|
23
|
+
build/
|
|
24
|
+
target/
|
|
25
|
+
__pycache__/
|
|
26
|
+
*.pyc
|
|
27
|
+
.venv/
|
|
28
|
+
venv/
|
|
29
|
+
|
|
30
|
+
# Logs
|
|
31
|
+
*.log
|
|
32
|
+
|
|
33
|
+
# Test / coverage
|
|
34
|
+
coverage/
|
|
35
|
+
.coverage
|
|
36
|
+
.pytest_cache/
|
|
37
|
+
.mypy_cache/
|
|
38
|
+
.ruff_cache/
|
|
39
|
+
htmlcov/
|
|
40
|
+
*.egg-info/
|
|
41
|
+
|
|
42
|
+
# Local config
|
|
43
|
+
.local/
|
|
44
|
+
|
|
45
|
+
# Cross-SDK round-trip ephemeral artefacts (regenerated on every run)
|
|
46
|
+
tests/cross_sdk/py_emit.json
|
|
47
|
+
tests/cross_sdk/ts_reemit.json
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: attestplane
|
|
3
|
+
Version: 0.0.3a0
|
|
4
|
+
Summary: Apache-2.0 attestation and audit substrate for AI agent evidence chains.
|
|
5
|
+
Project-URL: Homepage, https://attestplane.io
|
|
6
|
+
Project-URL: Repository, https://github.com/attestplane/attestplane
|
|
7
|
+
Project-URL: Issues, https://github.com/attestplane/attestplane/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/attestplane/attestplane/releases
|
|
9
|
+
Project-URL: Documentation, https://github.com/attestplane/attestplane/tree/main/sdk/python
|
|
10
|
+
Author: The Attestplane Authors
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
|
+
Keywords: audit,compliance,dora,eu-ai-act,gdpr,hash-chain,provenance,transparency
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Legal Industry
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Security :: Cryptography
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: uuid-utils>=0.9
|
|
27
|
+
Provides-Extra: anchor
|
|
28
|
+
Requires-Dist: asn1crypto>=1.5; extra == 'anchor'
|
|
29
|
+
Requires-Dist: cryptography>=43; extra == 'anchor'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: asn1crypto>=1.5; extra == 'dev'
|
|
32
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: cryptography>=43; extra == 'dev'
|
|
34
|
+
Requires-Dist: cyclonedx-bom>=4.5; extra == 'dev'
|
|
35
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
36
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
39
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
41
|
+
Provides-Extra: signing
|
|
42
|
+
Requires-Dist: cryptography>=43; extra == 'signing'
|
|
43
|
+
Requires-Dist: pyyaml>=6.0; extra == 'signing'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# attestplane — Python SDK
|
|
47
|
+
|
|
48
|
+
Apache-2.0 attestation and audit substrate for AI agent evidence chains.
|
|
49
|
+
|
|
50
|
+
> **Status: alpha (v0.0.3a0).** APIs may change before v0.1.0. The canonical
|
|
51
|
+
> hash format and conformance vectors, however, are frozen — see [ADR-0002][adr2].
|
|
52
|
+
> The current CLI `attestplane verify` path is chain/report-oriented with
|
|
53
|
+
> ProofBundle metadata and `policy_trace_refs` closure checks. It does not
|
|
54
|
+
> perform full ProofBundle, signature, anchor, or compliance certification
|
|
55
|
+
> verification.
|
|
56
|
+
|
|
57
|
+
See the [project README][project-readme] for background, governance, and
|
|
58
|
+
trademark policy. The full design rationale for this SDK lives in [ADR-0002][adr2].
|
|
59
|
+
|
|
60
|
+
[project-readme]: https://github.com/attestplane/attestplane
|
|
61
|
+
[adr2]: https://github.com/attestplane/attestplane/blob/main/docs/adr/0002-substrate-data-model-and-hash-chain-v0.md
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install attestplane
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Requires Python ≥ 3.11.
|
|
70
|
+
|
|
71
|
+
Optional sidecar extras are deliberately separate from the core substrate:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install 'attestplane[anchor]' # RFC-3161/OCSP/HTTP anchoring helpers
|
|
75
|
+
pip install 'attestplane[signing]' # Ed25519 signing helpers + YAML trust roots
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`import attestplane` works without those extras. Symbols that require optional
|
|
79
|
+
dependencies are exported only when the corresponding extra is installed.
|
|
80
|
+
|
|
81
|
+
## Quickstart
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from attestplane import AttestSubstrate, EventDraft, SubjectRef
|
|
85
|
+
|
|
86
|
+
sub = AttestSubstrate()
|
|
87
|
+
|
|
88
|
+
sub.append(
|
|
89
|
+
EventDraft(
|
|
90
|
+
event_type="ai_decision",
|
|
91
|
+
actor="agent://recsys/v1",
|
|
92
|
+
payload={"outcome": "approved", "confidence_bp": 9120},
|
|
93
|
+
session_id="session-2026-05-17-abc",
|
|
94
|
+
subject_ref=SubjectRef(scheme="sha256_salted", value="2c1b...e9"),
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
print(sub.tip()) # ChainHead(seq=0, event_hash=...)
|
|
99
|
+
assert sub.verify().ok # True
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## What this SDK gives you
|
|
103
|
+
|
|
104
|
+
- An **append-only** audit log with cryptographic integrity (SHA-256 hash chain).
|
|
105
|
+
- Built-in fields designed toward **EU AI Act Art. 12(2)(a)** auditability from day one.
|
|
106
|
+
- A **deterministic canonical format** that the TypeScript SDK also verifies;
|
|
107
|
+
future SDKs must match the same conformance vector file shipped in this
|
|
108
|
+
package.
|
|
109
|
+
- Strong **GDPR pseudonymization typing** via `SubjectRef`.
|
|
110
|
+
|
|
111
|
+
## What this SDK does NOT give you (yet)
|
|
112
|
+
|
|
113
|
+
| Feature | Where |
|
|
114
|
+
|---|---|
|
|
115
|
+
| Production storage guarantees | JSONL exists as an alpha backend; validate durability and concurrency for your deployment |
|
|
116
|
+
| Multi-process concurrency | Bring your own locking/backend policy for alpha deployments |
|
|
117
|
+
| Full ProofBundle verification | Current CLI is chain/report-oriented only |
|
|
118
|
+
| Signed or anchored CLI verification | Signing/anchoring are sidecar primitives unless an explicit verifier path performs those checks |
|
|
119
|
+
| Retention / truncation policy | Out of scope — deployer responsibility |
|
|
120
|
+
|
|
121
|
+
## EU AI Act Article 12(2)(a) mapping
|
|
122
|
+
|
|
123
|
+
The four enumerated subitems of Art. 12(2)(a) are surfaced as `EventDraft`
|
|
124
|
+
fields. All remain optional in v0.0.3a0 — populate the ones your use case
|
|
125
|
+
requires.
|
|
126
|
+
|
|
127
|
+
| Art. 12(2)(a) language | `EventDraft` field | Type |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| "period of each use of the system" | `session_id` | `str \| None` |
|
|
130
|
+
| "the reference database against which input data has been checked by the system" | `reference_db_ref` | `str \| None` |
|
|
131
|
+
| "the input data for which the search has resulted in a match" | `matched_input_ref` | `str \| None` |
|
|
132
|
+
| "the identification of the natural persons involved in the verification of the results" | `human_verifier` | `SubjectRef \| None` |
|
|
133
|
+
|
|
134
|
+
These are **references**, not the data itself. A real reference database or
|
|
135
|
+
matched input must not be inlined into the audit log — that would violate
|
|
136
|
+
GDPR Art. 5(1)(c) data minimization. Store the data elsewhere (object storage,
|
|
137
|
+
hashed lookup table) and put a stable, content-addressed identifier here.
|
|
138
|
+
|
|
139
|
+
## Restricted JSON profile
|
|
140
|
+
|
|
141
|
+
To make Python, TypeScript, and Rust SDKs produce byte-identical hashes,
|
|
142
|
+
canonicalization is stricter than vanilla JCS (RFC 8785):
|
|
143
|
+
|
|
144
|
+
| Allowed in `payload` | Forbidden in `payload` |
|
|
145
|
+
|---|---|
|
|
146
|
+
| UTF-8 NFC strings | Other Unicode normalization forms |
|
|
147
|
+
| Signed-int64 integers | Floats, NaN, ±Inf |
|
|
148
|
+
| `True` / `False` / `None` | — |
|
|
149
|
+
| `dict` (string keys, sorted on emit) | Non-string keys; duplicate keys |
|
|
150
|
+
| `list` / `tuple` (order preserved) | — |
|
|
151
|
+
| `bytes` (emitted as base64url, no padding) | Raw bytes |
|
|
152
|
+
| `datetime` (RFC 3339 UTC microsecond, `Z` suffix) | Naive datetimes; non-UTC offsets |
|
|
153
|
+
|
|
154
|
+
Violations raise `CanonicalizationError` at append time.
|
|
155
|
+
|
|
156
|
+
If you need to record a real-valued measurement, multiply into a fixed-point
|
|
157
|
+
integer (e.g., basis points, microseconds, microcents) and document the unit.
|
|
158
|
+
|
|
159
|
+
## Conformance vectors
|
|
160
|
+
|
|
161
|
+
Ten golden `(EventDraft → event_hash hex)` pairs live in
|
|
162
|
+
[`tests/conformance/vectors.json`](tests/conformance/vectors.json). These
|
|
163
|
+
values are a **permanent external contract**:
|
|
164
|
+
|
|
165
|
+
- An auditor can re-implement canonicalization from ADR-0002 alone and verify
|
|
166
|
+
against this file without trusting any SDK code.
|
|
167
|
+
- TypeScript and Rust SDKs (M6, M7) MUST produce identical hex for the same
|
|
168
|
+
inputs, or the release is blocked.
|
|
169
|
+
- The file is frozen for `schema_version = 1`. Adding fields requires a new
|
|
170
|
+
`schema_version` and a new vector set under a separate filename; the
|
|
171
|
+
existing vectors stay valid forever.
|
|
172
|
+
|
|
173
|
+
The CI conformance test re-derives each hex on every run, so accidental
|
|
174
|
+
canonicalization drift fails the build immediately.
|
|
175
|
+
|
|
176
|
+
## Threading and processes
|
|
177
|
+
|
|
178
|
+
`AttestSubstrate` is safe for concurrent use from multiple threads of the same
|
|
179
|
+
process. It is **not** safe across process boundaries — it holds no durable
|
|
180
|
+
storage and uses a process-local lock. Multi-process / multi-machine backends
|
|
181
|
+
are deferred to ADR-0004 (anticipated M6).
|
|
182
|
+
|
|
183
|
+
## JSONL storage alpha semantics
|
|
184
|
+
|
|
185
|
+
`JsonlStorageBackend` is an opt-in alpha backend for one JSONL record per
|
|
186
|
+
`ChainedEvent`. Appends write newline-terminated rows, flush, and fsync by
|
|
187
|
+
default. Callers may disable fsync with `durable=False` for tests or explicit
|
|
188
|
+
best-effort storage.
|
|
189
|
+
|
|
190
|
+
Reads are fail-closed by default. `read_all()` raises on malformed JSON,
|
|
191
|
+
missing fields, invalid UTF-8, or a partial trailing line. `scan()` is a
|
|
192
|
+
read-only diagnostic path: it returns the valid prefix plus exact line/byte
|
|
193
|
+
offset issues, but it never repairs, truncates, or treats corrupted data as a
|
|
194
|
+
valid full chain.
|
|
195
|
+
|
|
196
|
+
Concurrency remains single-process. The backend uses a process-local lock only;
|
|
197
|
+
multi-writer safety, file locking, database semantics, ACID guarantees, and
|
|
198
|
+
production crash-safety are not claimed.
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
git clone https://github.com/attestplane/attestplane
|
|
204
|
+
cd attestplane/sdk/python
|
|
205
|
+
uv venv --python 3.12
|
|
206
|
+
. .venv/bin/activate
|
|
207
|
+
uv pip install -e '.[dev]'
|
|
208
|
+
|
|
209
|
+
pytest # unit tests + conformance vectors
|
|
210
|
+
mypy # strict type check
|
|
211
|
+
ruff check src tests # lint
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Coverage gate: ≥ 90 % (currently ~ 98 %).
|
|
215
|
+
|
|
216
|
+
Type check is `mypy --strict`. The public API is fully type-annotated; consumers
|
|
217
|
+
should expect type checks against this package to be precise.
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
Apache License 2.0. See [LICENSE](https://github.com/attestplane/attestplane/blob/main/LICENSE)
|
|
222
|
+
and [NOTICE](https://github.com/attestplane/attestplane/blob/main/NOTICE) at
|
|
223
|
+
the project root.
|
|
224
|
+
|
|
225
|
+
## Reporting issues
|
|
226
|
+
|
|
227
|
+
- **Bugs / feature requests:** https://github.com/attestplane/attestplane/issues
|
|
228
|
+
- **Security vulnerabilities:** see [SECURITY.md](https://github.com/attestplane/attestplane/blob/main/SECURITY.md) — do not open public issues.
|
|
229
|
+
- **Trademark questions:** see [TRADEMARK.md](https://github.com/attestplane/attestplane/blob/main/TRADEMARK.md).
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# attestplane — Python SDK
|
|
2
|
+
|
|
3
|
+
Apache-2.0 attestation and audit substrate for AI agent evidence chains.
|
|
4
|
+
|
|
5
|
+
> **Status: alpha (v0.0.3a0).** APIs may change before v0.1.0. The canonical
|
|
6
|
+
> hash format and conformance vectors, however, are frozen — see [ADR-0002][adr2].
|
|
7
|
+
> The current CLI `attestplane verify` path is chain/report-oriented with
|
|
8
|
+
> ProofBundle metadata and `policy_trace_refs` closure checks. It does not
|
|
9
|
+
> perform full ProofBundle, signature, anchor, or compliance certification
|
|
10
|
+
> verification.
|
|
11
|
+
|
|
12
|
+
See the [project README][project-readme] for background, governance, and
|
|
13
|
+
trademark policy. The full design rationale for this SDK lives in [ADR-0002][adr2].
|
|
14
|
+
|
|
15
|
+
[project-readme]: https://github.com/attestplane/attestplane
|
|
16
|
+
[adr2]: https://github.com/attestplane/attestplane/blob/main/docs/adr/0002-substrate-data-model-and-hash-chain-v0.md
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install attestplane
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires Python ≥ 3.11.
|
|
25
|
+
|
|
26
|
+
Optional sidecar extras are deliberately separate from the core substrate:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install 'attestplane[anchor]' # RFC-3161/OCSP/HTTP anchoring helpers
|
|
30
|
+
pip install 'attestplane[signing]' # Ed25519 signing helpers + YAML trust roots
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`import attestplane` works without those extras. Symbols that require optional
|
|
34
|
+
dependencies are exported only when the corresponding extra is installed.
|
|
35
|
+
|
|
36
|
+
## Quickstart
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from attestplane import AttestSubstrate, EventDraft, SubjectRef
|
|
40
|
+
|
|
41
|
+
sub = AttestSubstrate()
|
|
42
|
+
|
|
43
|
+
sub.append(
|
|
44
|
+
EventDraft(
|
|
45
|
+
event_type="ai_decision",
|
|
46
|
+
actor="agent://recsys/v1",
|
|
47
|
+
payload={"outcome": "approved", "confidence_bp": 9120},
|
|
48
|
+
session_id="session-2026-05-17-abc",
|
|
49
|
+
subject_ref=SubjectRef(scheme="sha256_salted", value="2c1b...e9"),
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
print(sub.tip()) # ChainHead(seq=0, event_hash=...)
|
|
54
|
+
assert sub.verify().ok # True
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## What this SDK gives you
|
|
58
|
+
|
|
59
|
+
- An **append-only** audit log with cryptographic integrity (SHA-256 hash chain).
|
|
60
|
+
- Built-in fields designed toward **EU AI Act Art. 12(2)(a)** auditability from day one.
|
|
61
|
+
- A **deterministic canonical format** that the TypeScript SDK also verifies;
|
|
62
|
+
future SDKs must match the same conformance vector file shipped in this
|
|
63
|
+
package.
|
|
64
|
+
- Strong **GDPR pseudonymization typing** via `SubjectRef`.
|
|
65
|
+
|
|
66
|
+
## What this SDK does NOT give you (yet)
|
|
67
|
+
|
|
68
|
+
| Feature | Where |
|
|
69
|
+
|---|---|
|
|
70
|
+
| Production storage guarantees | JSONL exists as an alpha backend; validate durability and concurrency for your deployment |
|
|
71
|
+
| Multi-process concurrency | Bring your own locking/backend policy for alpha deployments |
|
|
72
|
+
| Full ProofBundle verification | Current CLI is chain/report-oriented only |
|
|
73
|
+
| Signed or anchored CLI verification | Signing/anchoring are sidecar primitives unless an explicit verifier path performs those checks |
|
|
74
|
+
| Retention / truncation policy | Out of scope — deployer responsibility |
|
|
75
|
+
|
|
76
|
+
## EU AI Act Article 12(2)(a) mapping
|
|
77
|
+
|
|
78
|
+
The four enumerated subitems of Art. 12(2)(a) are surfaced as `EventDraft`
|
|
79
|
+
fields. All remain optional in v0.0.3a0 — populate the ones your use case
|
|
80
|
+
requires.
|
|
81
|
+
|
|
82
|
+
| Art. 12(2)(a) language | `EventDraft` field | Type |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| "period of each use of the system" | `session_id` | `str \| None` |
|
|
85
|
+
| "the reference database against which input data has been checked by the system" | `reference_db_ref` | `str \| None` |
|
|
86
|
+
| "the input data for which the search has resulted in a match" | `matched_input_ref` | `str \| None` |
|
|
87
|
+
| "the identification of the natural persons involved in the verification of the results" | `human_verifier` | `SubjectRef \| None` |
|
|
88
|
+
|
|
89
|
+
These are **references**, not the data itself. A real reference database or
|
|
90
|
+
matched input must not be inlined into the audit log — that would violate
|
|
91
|
+
GDPR Art. 5(1)(c) data minimization. Store the data elsewhere (object storage,
|
|
92
|
+
hashed lookup table) and put a stable, content-addressed identifier here.
|
|
93
|
+
|
|
94
|
+
## Restricted JSON profile
|
|
95
|
+
|
|
96
|
+
To make Python, TypeScript, and Rust SDKs produce byte-identical hashes,
|
|
97
|
+
canonicalization is stricter than vanilla JCS (RFC 8785):
|
|
98
|
+
|
|
99
|
+
| Allowed in `payload` | Forbidden in `payload` |
|
|
100
|
+
|---|---|
|
|
101
|
+
| UTF-8 NFC strings | Other Unicode normalization forms |
|
|
102
|
+
| Signed-int64 integers | Floats, NaN, ±Inf |
|
|
103
|
+
| `True` / `False` / `None` | — |
|
|
104
|
+
| `dict` (string keys, sorted on emit) | Non-string keys; duplicate keys |
|
|
105
|
+
| `list` / `tuple` (order preserved) | — |
|
|
106
|
+
| `bytes` (emitted as base64url, no padding) | Raw bytes |
|
|
107
|
+
| `datetime` (RFC 3339 UTC microsecond, `Z` suffix) | Naive datetimes; non-UTC offsets |
|
|
108
|
+
|
|
109
|
+
Violations raise `CanonicalizationError` at append time.
|
|
110
|
+
|
|
111
|
+
If you need to record a real-valued measurement, multiply into a fixed-point
|
|
112
|
+
integer (e.g., basis points, microseconds, microcents) and document the unit.
|
|
113
|
+
|
|
114
|
+
## Conformance vectors
|
|
115
|
+
|
|
116
|
+
Ten golden `(EventDraft → event_hash hex)` pairs live in
|
|
117
|
+
[`tests/conformance/vectors.json`](tests/conformance/vectors.json). These
|
|
118
|
+
values are a **permanent external contract**:
|
|
119
|
+
|
|
120
|
+
- An auditor can re-implement canonicalization from ADR-0002 alone and verify
|
|
121
|
+
against this file without trusting any SDK code.
|
|
122
|
+
- TypeScript and Rust SDKs (M6, M7) MUST produce identical hex for the same
|
|
123
|
+
inputs, or the release is blocked.
|
|
124
|
+
- The file is frozen for `schema_version = 1`. Adding fields requires a new
|
|
125
|
+
`schema_version` and a new vector set under a separate filename; the
|
|
126
|
+
existing vectors stay valid forever.
|
|
127
|
+
|
|
128
|
+
The CI conformance test re-derives each hex on every run, so accidental
|
|
129
|
+
canonicalization drift fails the build immediately.
|
|
130
|
+
|
|
131
|
+
## Threading and processes
|
|
132
|
+
|
|
133
|
+
`AttestSubstrate` is safe for concurrent use from multiple threads of the same
|
|
134
|
+
process. It is **not** safe across process boundaries — it holds no durable
|
|
135
|
+
storage and uses a process-local lock. Multi-process / multi-machine backends
|
|
136
|
+
are deferred to ADR-0004 (anticipated M6).
|
|
137
|
+
|
|
138
|
+
## JSONL storage alpha semantics
|
|
139
|
+
|
|
140
|
+
`JsonlStorageBackend` is an opt-in alpha backend for one JSONL record per
|
|
141
|
+
`ChainedEvent`. Appends write newline-terminated rows, flush, and fsync by
|
|
142
|
+
default. Callers may disable fsync with `durable=False` for tests or explicit
|
|
143
|
+
best-effort storage.
|
|
144
|
+
|
|
145
|
+
Reads are fail-closed by default. `read_all()` raises on malformed JSON,
|
|
146
|
+
missing fields, invalid UTF-8, or a partial trailing line. `scan()` is a
|
|
147
|
+
read-only diagnostic path: it returns the valid prefix plus exact line/byte
|
|
148
|
+
offset issues, but it never repairs, truncates, or treats corrupted data as a
|
|
149
|
+
valid full chain.
|
|
150
|
+
|
|
151
|
+
Concurrency remains single-process. The backend uses a process-local lock only;
|
|
152
|
+
multi-writer safety, file locking, database semantics, ACID guarantees, and
|
|
153
|
+
production crash-safety are not claimed.
|
|
154
|
+
|
|
155
|
+
## Development
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
git clone https://github.com/attestplane/attestplane
|
|
159
|
+
cd attestplane/sdk/python
|
|
160
|
+
uv venv --python 3.12
|
|
161
|
+
. .venv/bin/activate
|
|
162
|
+
uv pip install -e '.[dev]'
|
|
163
|
+
|
|
164
|
+
pytest # unit tests + conformance vectors
|
|
165
|
+
mypy # strict type check
|
|
166
|
+
ruff check src tests # lint
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Coverage gate: ≥ 90 % (currently ~ 98 %).
|
|
170
|
+
|
|
171
|
+
Type check is `mypy --strict`. The public API is fully type-annotated; consumers
|
|
172
|
+
should expect type checks against this package to be precise.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
Apache License 2.0. See [LICENSE](https://github.com/attestplane/attestplane/blob/main/LICENSE)
|
|
177
|
+
and [NOTICE](https://github.com/attestplane/attestplane/blob/main/NOTICE) at
|
|
178
|
+
the project root.
|
|
179
|
+
|
|
180
|
+
## Reporting issues
|
|
181
|
+
|
|
182
|
+
- **Bugs / feature requests:** https://github.com/attestplane/attestplane/issues
|
|
183
|
+
- **Security vulnerabilities:** see [SECURITY.md](https://github.com/attestplane/attestplane/blob/main/SECURITY.md) — do not open public issues.
|
|
184
|
+
- **Trademark questions:** see [TRADEMARK.md](https://github.com/attestplane/attestplane/blob/main/TRADEMARK.md).
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.21"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "attestplane"
|
|
7
|
+
version = "0.0.3a0"
|
|
8
|
+
description = "Apache-2.0 attestation and audit substrate for AI agent evidence chains."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
license-files = ["LICENSE", "NOTICE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "The Attestplane Authors" },
|
|
15
|
+
]
|
|
16
|
+
keywords = [
|
|
17
|
+
"audit",
|
|
18
|
+
"compliance",
|
|
19
|
+
"eu-ai-act",
|
|
20
|
+
"dora",
|
|
21
|
+
"gdpr",
|
|
22
|
+
"hash-chain",
|
|
23
|
+
"provenance",
|
|
24
|
+
"transparency",
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Intended Audience :: Legal Industry",
|
|
30
|
+
"License :: OSI Approved :: Apache Software License",
|
|
31
|
+
"Operating System :: OS Independent",
|
|
32
|
+
"Programming Language :: Python :: 3",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Topic :: Security :: Cryptography",
|
|
37
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
38
|
+
"Typing :: Typed",
|
|
39
|
+
]
|
|
40
|
+
dependencies = [
|
|
41
|
+
"uuid-utils>=0.9",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
anchor = [
|
|
46
|
+
"cryptography>=43",
|
|
47
|
+
"asn1crypto>=1.5",
|
|
48
|
+
]
|
|
49
|
+
signing = [
|
|
50
|
+
"cryptography>=43",
|
|
51
|
+
"pyyaml>=6.0",
|
|
52
|
+
]
|
|
53
|
+
dev = [
|
|
54
|
+
"pytest>=8",
|
|
55
|
+
"pytest-cov>=5",
|
|
56
|
+
"mypy>=1.11",
|
|
57
|
+
"ruff>=0.6",
|
|
58
|
+
"hypothesis>=6.100",
|
|
59
|
+
"cyclonedx-bom>=4.5",
|
|
60
|
+
"build>=1.2",
|
|
61
|
+
"cryptography>=43",
|
|
62
|
+
"asn1crypto>=1.5",
|
|
63
|
+
"pyyaml>=6.0",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[project.scripts]
|
|
67
|
+
attestplane = "attestplane.cli:main"
|
|
68
|
+
|
|
69
|
+
[project.urls]
|
|
70
|
+
Homepage = "https://attestplane.io"
|
|
71
|
+
Repository = "https://github.com/attestplane/attestplane"
|
|
72
|
+
Issues = "https://github.com/attestplane/attestplane/issues"
|
|
73
|
+
Changelog = "https://github.com/attestplane/attestplane/releases"
|
|
74
|
+
Documentation = "https://github.com/attestplane/attestplane/tree/main/sdk/python"
|
|
75
|
+
|
|
76
|
+
[tool.hatch.build.targets.wheel]
|
|
77
|
+
packages = ["src/attestplane"]
|
|
78
|
+
|
|
79
|
+
[tool.hatch.build.targets.sdist]
|
|
80
|
+
include = [
|
|
81
|
+
"src/attestplane",
|
|
82
|
+
"tests",
|
|
83
|
+
"README.md",
|
|
84
|
+
"LICENSE",
|
|
85
|
+
"NOTICE",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.ruff]
|
|
89
|
+
# 120 covers ~all existing long lines that are doc-strings or composite f-strings.
|
|
90
|
+
# Hard-wrapping below that yielded noise without readability gain.
|
|
91
|
+
line-length = 120
|
|
92
|
+
target-version = "py311"
|
|
93
|
+
|
|
94
|
+
[tool.ruff.lint]
|
|
95
|
+
select = [
|
|
96
|
+
"E", "F", "W", "I", "B", "UP", "S", "RUF", "PL", "PT", "SIM", "TID",
|
|
97
|
+
]
|
|
98
|
+
ignore = [
|
|
99
|
+
# ----- Pylint refactoring suggestions (advisory only; cosmetic) -----
|
|
100
|
+
"PLR0911", # too many return statements (verifier paths legitimately fan out)
|
|
101
|
+
"PLR0912", # too many branches (canonicalize / verify_chain are unavoidably branchy)
|
|
102
|
+
"PLR0913", # too many arguments (some dataclasses have many fields by design)
|
|
103
|
+
"PLR0915", # too many statements (composed verifier/builder routines)
|
|
104
|
+
"PLR2004", # magic-value comparison (test code + protocol-version checks)
|
|
105
|
+
"PLC0415", # `import` should be top-level — we deliberately lazy-import yaml /
|
|
106
|
+
# cryptography / hashlib in functions to keep optional extras truly optional.
|
|
107
|
+
# ----- Test assertion noise -----
|
|
108
|
+
"S101", # `assert` used (pytest tests deliberately use bare assert)
|
|
109
|
+
# ----- Security-rule false positives in this codebase -----
|
|
110
|
+
# S105/S107/S110/S112/S314/S324: domain-specific reasons documented at use sites.
|
|
111
|
+
# Selectively re-enable in future when each call site is reviewed; for now they
|
|
112
|
+
# are advisory not blocking.
|
|
113
|
+
"S105", # "Possible hardcoded password" — false-positive on identifier names
|
|
114
|
+
# like `secret = ...` in test fixtures.
|
|
115
|
+
"S107", # function-argument password — same false-positive on kw names.
|
|
116
|
+
"S110", # `try-except-pass` — used intentionally in defensive importable
|
|
117
|
+
# adapters.
|
|
118
|
+
"S112", # `try-except-continue` — same.
|
|
119
|
+
"S314", # `xml.etree` use — `intoto.py` deserialises trusted DSSE payloads only.
|
|
120
|
+
"S324", # `hashlib.sha1` — used for non-cryptographic identifiers (e.g.
|
|
121
|
+
# AnchorRecord opaque tag deduplication), never as a security
|
|
122
|
+
# primitive.
|
|
123
|
+
# ----- RUF suggestions that don't apply here -----
|
|
124
|
+
"RUF002", # ambiguous-unicode-character in docstring (legitimate full-width
|
|
125
|
+
# punctuation in CJK examples).
|
|
126
|
+
"RUF005", # iterable-unpacking suggestion (cosmetic).
|
|
127
|
+
"RUF022", # `__all__` is not sorted — keep current grouping (alpha by category).
|
|
128
|
+
"RUF059", # unpacked-variable-not-used — used intentionally to document tuple shape.
|
|
129
|
+
"RUF100", # unused-noqa — stale noqa entries from earlier ruff versions; benign.
|
|
130
|
+
# ----- pytest style suggestions (advisory, not bugs) -----
|
|
131
|
+
"PT006", # parametrize argument-name type (cosmetic).
|
|
132
|
+
"PT011", # `pytest.raises(ValueError)` too broad — domain code raises
|
|
133
|
+
# `ValueError` widely; match parameter is per-call decision.
|
|
134
|
+
"PT012", # raises-block-multiple-statements — fixture setup inside raises.
|
|
135
|
+
"PT018", # composite-assertion — used for clarity in test expectations.
|
|
136
|
+
# ----- SIM style suggestions (cosmetic refactors) -----
|
|
137
|
+
"SIM102", # nested-if combinable into single if.
|
|
138
|
+
"SIM108", # ternary suggestion.
|
|
139
|
+
"SIM115", # context-manager suggestion (test-only file-handle patterns).
|
|
140
|
+
# ----- Other low-signal rules -----
|
|
141
|
+
"B007", # unused-loop-control-variable (idiomatic in some test patterns).
|
|
142
|
+
"F841", # unused-local — intentional reservation in fixture wiring.
|
|
143
|
+
"PLC0206", # extracting-value-without-items() — dict iteration style choice.
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
[tool.ruff.lint.per-file-ignores]
|
|
147
|
+
"tests/**" = [
|
|
148
|
+
"S101",
|
|
149
|
+
"PLR2004",
|
|
150
|
+
"PLC0415", # tests legitimately import-then-use inside test functions
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
[tool.mypy]
|
|
154
|
+
python_version = "3.11"
|
|
155
|
+
strict = true
|
|
156
|
+
warn_unreachable = true
|
|
157
|
+
warn_unused_ignores = true
|
|
158
|
+
disallow_any_explicit = false
|
|
159
|
+
files = ["src/attestplane"]
|
|
160
|
+
|
|
161
|
+
[[tool.mypy.overrides]]
|
|
162
|
+
module = ["asn1crypto", "asn1crypto.*"]
|
|
163
|
+
ignore_missing_imports = true
|
|
164
|
+
|
|
165
|
+
[[tool.mypy.overrides]]
|
|
166
|
+
module = ["yaml"]
|
|
167
|
+
ignore_missing_imports = true
|
|
168
|
+
|
|
169
|
+
[tool.pytest.ini_options]
|
|
170
|
+
testpaths = ["tests"]
|
|
171
|
+
addopts = "-ra --strict-markers --strict-config"
|
|
172
|
+
xfail_strict = true
|
|
173
|
+
|
|
174
|
+
[tool.coverage.run]
|
|
175
|
+
source = ["src/attestplane"]
|
|
176
|
+
branch = true
|
|
177
|
+
|
|
178
|
+
[tool.coverage.report]
|
|
179
|
+
fail_under = 90
|
|
180
|
+
show_missing = true
|
|
181
|
+
skip_covered = false
|
|
182
|
+
|
|
183
|
+
# Mutation testing (Gap G6, advisory only, nightly).
|
|
184
|
+
# Narrow scope to small core modules so mutmut completes in the nightly
|
|
185
|
+
# budget: canonical_text.py (cross-SDK byte contract) + reason_codes.py
|
|
186
|
+
# (ADR-0010 verification semantics). Other modules will be added as
|
|
187
|
+
# mutmut budget permits.
|
|
188
|
+
[tool.mutmut]
|
|
189
|
+
paths_to_mutate = "src/attestplane/canonical_text.py:src/attestplane/reason_codes.py"
|
|
190
|
+
runner = "python -m pytest -x -q --no-cov"
|
|
191
|
+
tests_dir = "tests"
|