bindable 0.1.0__tar.gz → 0.1.1__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.
Files changed (33) hide show
  1. {bindable-0.1.0 → bindable-0.1.1}/PKG-INFO +2 -2
  2. {bindable-0.1.0 → bindable-0.1.1}/bindable/_canonical_record.py +4 -4
  3. bindable-0.1.1/bindable/_version.py +1 -0
  4. {bindable-0.1.0 → bindable-0.1.1}/pyproject.toml +1 -1
  5. {bindable-0.1.0 → bindable-0.1.1}/tests/crypto/vectors.json +1 -1
  6. bindable-0.1.1/tests/fixtures/KEY_PROVENANCE.md +1 -0
  7. bindable-0.1.1/tests/fixtures/bindable-public-2026-07.pem +4 -0
  8. bindable-0.1.1/tests/fixtures/receipt_public_blocked_rc3.json +1 -0
  9. bindable-0.1.1/tests/fixtures/receipt_public_released_rc3.json +1 -0
  10. {bindable-0.1.0 → bindable-0.1.1}/tests/test_canonical_vectors.py +4 -4
  11. {bindable-0.1.0 → bindable-0.1.1}/tests/test_client.py +3 -1
  12. {bindable-0.1.0 → bindable-0.1.1}/tests/test_verify_receipt.py +70 -56
  13. {bindable-0.1.0 → bindable-0.1.1}/tests/test_version.py +1 -1
  14. bindable-0.1.0/bindable/_version.py +0 -1
  15. bindable-0.1.0/tests/fixtures/receipt_public_key.pem +0 -4
  16. bindable-0.1.0/tests/fixtures/receipt_released.json +0 -30
  17. {bindable-0.1.0 → bindable-0.1.1}/.gitignore +0 -0
  18. {bindable-0.1.0 → bindable-0.1.1}/LICENSE +0 -0
  19. {bindable-0.1.0 → bindable-0.1.1}/NOTICE +0 -0
  20. {bindable-0.1.0 → bindable-0.1.1}/README.md +0 -0
  21. {bindable-0.1.0 → bindable-0.1.1}/bindable/__init__.py +0 -0
  22. {bindable-0.1.0 → bindable-0.1.1}/bindable/client.py +0 -0
  23. {bindable-0.1.0 → bindable-0.1.1}/bindable/crypto/__init__.py +0 -0
  24. {bindable-0.1.0 → bindable-0.1.1}/bindable/crypto/canonical.py +0 -0
  25. {bindable-0.1.0 → bindable-0.1.1}/bindable/crypto/hash.py +0 -0
  26. {bindable-0.1.0 → bindable-0.1.1}/bindable/crypto/keys.py +0 -0
  27. {bindable-0.1.0 → bindable-0.1.1}/bindable/crypto/sign.py +0 -0
  28. {bindable-0.1.0 → bindable-0.1.1}/bindable/errors.py +0 -0
  29. {bindable-0.1.0 → bindable-0.1.1}/bindable/py.typed +0 -0
  30. {bindable-0.1.0 → bindable-0.1.1}/bindable/verify.py +0 -0
  31. {bindable-0.1.0 → bindable-0.1.1}/tests/__init__.py +0 -0
  32. {bindable-0.1.0 → bindable-0.1.1}/tests/crypto/sign_vectors.json +0 -0
  33. {bindable-0.1.0 → bindable-0.1.1}/tests/test_signature_verify.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: bindable
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Python client for the hosted Bindable Action Gate, with offline Receipt verification
5
5
  Author: Bindable AI LLC
6
6
  License-Expression: Apache-2.0
@@ -38,7 +38,7 @@ def _canonical_hash_input(receipt: Mapping[str, Any]) -> dict[str, Any]:
38
38
  """Return the canonical hash input dict per MVP spec §12.3.
39
39
 
40
40
  Included fields (canonical_json sorts alphabetically):
41
- action_type, claim_payload, contract_id, contract_version,
41
+ action_type, proposal_payload, contract_id, contract_version,
42
42
  counterparty_identity, failed_rule, nonce,
43
43
  persistent_agent_id, previous_hash, proposed_action_id,
44
44
  referenced_evidence, sponsor_identity,
@@ -46,8 +46,8 @@ def _canonical_hash_input(receipt: Mapping[str, Any]) -> dict[str, Any]:
46
46
 
47
47
  Excluded per §12.3:
48
48
  receipt_id, signature, signing_key_id, signed_at,
49
- chain_hash (self-reference), reason_detail, claim_digest
50
- (derivable from canonical claim_payload).
49
+ chain_hash (self-reference), reason_detail, proposal_digest
50
+ (derivable from canonical proposal_payload).
51
51
 
52
52
  Args:
53
53
  receipt: A receipt as a plain mapping (e.g. parsed JSON from
@@ -61,7 +61,7 @@ def _canonical_hash_input(receipt: Mapping[str, Any]) -> dict[str, Any]:
61
61
  """
62
62
  return {
63
63
  "action_type": receipt["action_type"],
64
- "claim_payload": receipt["claim_payload"],
64
+ "proposal_payload": receipt["proposal_payload"],
65
65
  "contract_id": receipt["contract_id"],
66
66
  "contract_version": receipt["contract_version"],
67
67
  "counterparty_identity": receipt["counterparty_identity"],
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bindable"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  requires-python = ">=3.10"
5
5
  description = "Python client for the hosted Bindable Action Gate, with offline Receipt verification"
6
6
  readme = "README.md"
@@ -72,7 +72,7 @@
72
72
  },
73
73
  {
74
74
  "name": "production_shape_receipt",
75
- "description": "Approximates the receipt record shape from Interlock MVP Spec v0.1.2 §6.2.",
75
+ "description": "LEGACY SHAPE (pre-rc3 claim_* wire). Approximates the receipt record shape from Interlock MVP Spec v0.1.2 §6.2. Pins canonicalization math only; the rc3 wire uses proposal_* vocabulary.",
76
76
  "record": {
77
77
  "claim_payload": {
78
78
  "po_number": "PO-44821",
@@ -0,0 +1 @@
1
+ Key provenance: bindable-public-2026-07.pem was recovered via ECDSA public-key recovery from the two production receipts in this directory and cross-validated against the hosted POST /v1/verify. Authoritative distribution of landscape verification keys will move to a read-only platform endpoint.
@@ -0,0 +1,4 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgAiZnfLmwECLMvbzCsaKKn+zMZ3W
3
+ sJHGfdbwH8M7fk2hkNVI+RffcCwZFu2wG3MejwmvW7+b9GC4EgOEkdlkXw==
4
+ -----END PUBLIC KEY-----
@@ -0,0 +1 @@
1
+ {"receipt_id": "f92558c09edc44e7a141ea5c1b5cc855", "proposal_digest": "6b317920863fdbebaeda9e824daf5aeb781d87b2307f209f1ae25188fce2fe62", "proposal_payload": {"matter_id": "MER-2026-014", "call_cost_usd": 126, "records_requested": 400, "purpose": "full-market data sweep"}, "contract_id": "demo-diligence-spend-public-founding", "contract_version": "1.0.0", "sponsor_identity": "did:web:bindbl.ai:sponsor:b4047dfe7c4244dda32a3c8b4a02fc8e", "sponsor_identity_context": "ctx:public", "counterparty_identity": null, "persistent_agent_id": "did:web:bindbl.ai:agent:9dd08b5846564a1fbe674b1834fb887d", "action_type": "paid_data_call", "proposed_action_id": "pf-blocked-public-founding", "verification_outcome": "failed", "failed_rule": "r-2", "reason_detail": "126 lte 58 not satisfied", "referenced_evidence": ["demo-budget-public-founding"], "verified_at": "2026-08-13T14:09:07.031Z", "nonce": "d61480f760d63f14d1350c21573d1a8b", "chain_hash": "63b052eef0f449d7330dfd897891385e48ef372d8c69e9ddd05eaa84247ea036", "previous_hash": null, "signature": "30460221009273d5a94dbca61570dff9f7729d7576e852c0a681a396998db5da1e27885ed4022100ada78e5200865db572d78663a07b871cfd6013f84550c34bb3242cad07c44e9e", "signing_key_id": "bindable-public-2026-07", "signed_at": "2026-08-13T14:09:07.031Z", "intervention_artifacts": null}
@@ -0,0 +1 @@
1
+ {"receipt_id": "9b6e8bca643444e09dfe0ef4015244d1", "proposal_digest": "9fe07610a2f335a96454d710259145e5ed1ef546234e21c279f4d6cff79f570d", "proposal_payload": {"matter_id": "MER-2026-014", "call_cost_usd": 12, "records_requested": 40, "purpose": "comparable-transactions pull"}, "contract_id": "demo-diligence-spend-public-founding", "contract_version": "1.0.0", "sponsor_identity": "did:web:bindbl.ai:sponsor:b4047dfe7c4244dda32a3c8b4a02fc8e", "sponsor_identity_context": "ctx:public", "counterparty_identity": null, "persistent_agent_id": "did:web:bindbl.ai:agent:9dd08b5846564a1fbe674b1834fb887d", "action_type": "paid_data_call", "proposed_action_id": "pf-released-public-founding", "verification_outcome": "passed", "failed_rule": null, "reason_detail": null, "referenced_evidence": ["demo-budget-public-founding"], "verified_at": "2026-08-13T14:08:59.607Z", "nonce": "b38265567729c89b020e6afe8a420f70", "chain_hash": "61374b353d242e9ddc523b09b537506a16f84852a1352d4bbf03643e486fe99d", "previous_hash": null, "signature": "3045022100d4dc8ff4ce26b24d0725016b7243b41e96ed2bc9074f21ba40c618ca459e0b01022027065e8d3e342393d70a270d43886145b2373418f2dc6ef7fc12b7b66860a12b", "signing_key_id": "bindable-public-2026-07", "signed_at": "2026-08-13T14:08:59.621Z", "intervention_artifacts": null}
@@ -55,7 +55,7 @@ def test_canonical_json_rejects_nested_float() -> None:
55
55
  _FULL_RECEIPT: dict[str, Any] = {
56
56
  # 15 included fields
57
57
  "action_type": "release_payment",
58
- "claim_payload": {"po_number": "PO-44821", "invoice_amount_cents": 12500000},
58
+ "proposal_payload": {"po_number": "PO-44821", "invoice_amount_cents": 12500000},
59
59
  "contract_id": "contract-acme-xyz-invoice",
60
60
  "contract_version": "1.0.0",
61
61
  "counterparty_identity": "acme-vendor",
@@ -76,12 +76,12 @@ _FULL_RECEIPT: dict[str, Any] = {
76
76
  "signed_at": "2026-05-19T14:00:01.000Z",
77
77
  "chain_hash": "0" * 64,
78
78
  "reason_detail": "all good",
79
- "claim_digest": "f" * 64,
79
+ "proposal_digest": "f" * 64,
80
80
  }
81
81
 
82
82
  _EXPECTED_KEYS = {
83
83
  "action_type",
84
- "claim_payload",
84
+ "proposal_payload",
85
85
  "contract_id",
86
86
  "contract_version",
87
87
  "counterparty_identity",
@@ -115,7 +115,7 @@ def test_canonical_hash_input_excludes_signature_and_chain_fields() -> None:
115
115
  "signed_at",
116
116
  "chain_hash",
117
117
  "reason_detail",
118
- "claim_digest",
118
+ "proposal_digest",
119
119
  ):
120
120
  assert excluded not in result
121
121
 
@@ -29,7 +29,9 @@ _OUTCOME_RELEASED: dict[str, Any] = {
29
29
  }
30
30
 
31
31
  _FIXTURE_RECEIPT: dict[str, Any] = json.loads(
32
- (Path(__file__).parent / "fixtures" / "receipt_released.json").read_text(encoding="utf-8")
32
+ (Path(__file__).parent / "fixtures" / "receipt_public_released_rc3.json").read_text(
33
+ encoding="utf-8"
34
+ )
33
35
  )
34
36
 
35
37
 
@@ -1,10 +1,13 @@
1
- """verify_receipt — end-to-end gate against a real engine-minted receipt.
2
-
3
- The fixture (tests/fixtures/receipt_released.json + receipt_public_key.pem) was
4
- minted FROM the engine @ 5f129a6 via its own Interlock construction path. The
5
- chain-hash gate below proves the de-pydantic thin-client builder reproduces the
6
- engine's chain_hash byte-for-byte. If it fails, the builder diverged — STOP and
7
- report; never edit the fixture.
1
+ """verify_receipt — end-to-end gate against real engine-minted receipts.
2
+
3
+ The fixtures (tests/fixtures/receipt_public_released_rc3.json and
4
+ receipt_public_blocked_rc3.json) are REAL production receipts minted by the
5
+ engine @ 0.1.0rc3 on the Public landscape, ECDSA-signed by key
6
+ bindable-public-2026-07 (public half at tests/fixtures/
7
+ bindable-public-2026-07.pem). The chain-hash gates below prove the
8
+ de-pydantic thin-client builder reproduces the engine's chain_hash
9
+ byte-for-byte on the proposal_* wire. If one fails, the builder diverged —
10
+ STOP and report; never edit the fixtures.
8
11
  """
9
12
 
10
13
  from __future__ import annotations
@@ -14,120 +17,135 @@ import json
14
17
  from pathlib import Path
15
18
  from typing import Any
16
19
 
20
+ import pytest
21
+
17
22
  from bindable import ReceiptVerification, load_public_key, verify_receipt
18
23
  from bindable._canonical_record import _canonical_hash_input
19
24
  from bindable.crypto import canonical_sha256
20
25
 
21
26
  _FIX = Path(__file__).parent / "fixtures"
22
- _RECEIPT: dict[str, Any] = json.loads(
23
- (_FIX / "receipt_released.json").read_text(encoding="utf-8")
27
+ _RELEASED: dict[str, Any] = json.loads(
28
+ (_FIX / "receipt_public_released_rc3.json").read_text(encoding="utf-8")
29
+ )
30
+ _BLOCKED: dict[str, Any] = json.loads(
31
+ (_FIX / "receipt_public_blocked_rc3.json").read_text(encoding="utf-8")
24
32
  )
25
- _PUBLIC_KEY_PEM_BYTES = (_FIX / "receipt_public_key.pem").read_bytes()
33
+ _PUBLIC_KEY_PEM_BYTES = (_FIX / "bindable-public-2026-07.pem").read_bytes()
26
34
  _PUBLIC_KEY_PEM_STR = _PUBLIC_KEY_PEM_BYTES.decode("utf-8")
27
35
 
28
- # A valid P-256 key that did NOT sign this receipt (from the sign vectors).
36
+ # A valid P-256 key that did NOT sign these receipts (from the sign vectors).
29
37
  _WRONG_KEY_PEM = json.loads(
30
38
  (Path(__file__).parent / "crypto" / "sign_vectors.json").read_text(encoding="utf-8")
31
39
  )[0]["public_key_pem"]
32
40
 
41
+ _BOTH = pytest.mark.parametrize(
42
+ "fixture", [_RELEASED, _BLOCKED], ids=["released", "blocked"]
43
+ )
44
+
33
45
 
34
- def _receipt() -> dict[str, Any]:
35
- return copy.deepcopy(_RECEIPT)
46
+ def _receipt(fixture: dict[str, Any]) -> dict[str, Any]:
47
+ return copy.deepcopy(fixture)
36
48
 
37
49
 
38
50
  # ─── GATE: builder reproduces the engine chain_hash ────────────────────
39
51
 
40
52
 
41
- def test_chain_hash_gate_builder_matches_fixture() -> None:
53
+ @_BOTH
54
+ def test_chain_hash_gate_builder_matches_fixture(fixture: dict[str, Any]) -> None:
42
55
  """canonical_sha256(_canonical_hash_input(receipt)) == receipt['chain_hash']."""
43
- recomputed = canonical_sha256(_canonical_hash_input(_receipt()))
44
- assert recomputed == _RECEIPT["chain_hash"], (
56
+ recomputed = canonical_sha256(_canonical_hash_input(_receipt(fixture)))
57
+ assert recomputed == fixture["chain_hash"], (
45
58
  f"Builder diverged from the engine: recomputed {recomputed!r} != pinned "
46
- f"{_RECEIPT['chain_hash']!r}. STOP — do NOT edit the fixture."
59
+ f"{fixture['chain_hash']!r}. STOP — do NOT edit the fixture."
47
60
  )
48
61
 
49
62
 
50
63
  # ─── Happy path (key as PEM str and as loaded object) ──────────────────
51
64
 
52
65
 
53
- def test_valid_with_pem_string() -> None:
54
- result = verify_receipt(_receipt(), public_key=_PUBLIC_KEY_PEM_STR)
55
- assert isinstance(result, ReceiptVerification)
66
+ @_BOTH
67
+ def test_valid_with_pem_string(fixture: dict[str, Any]) -> None:
68
+ result = verify_receipt(_receipt(fixture), public_key=_PUBLIC_KEY_PEM_STR)
56
69
  assert result.valid is True
57
70
  assert result.chain_hash_valid is True
58
71
  assert result.signature_valid is True
59
72
  assert result.reason is None
60
73
 
61
74
 
62
- def test_valid_with_loaded_public_key_object() -> None:
75
+ @_BOTH
76
+ def test_valid_with_loaded_public_key_object(fixture: dict[str, Any]) -> None:
63
77
  pub = load_public_key(_PUBLIC_KEY_PEM_BYTES)
64
- result = verify_receipt(_receipt(), public_key=pub)
78
+ result = verify_receipt(_receipt(fixture), public_key=pub)
65
79
  assert result.valid is True
80
+ assert result.chain_hash_valid is True
66
81
  assert result.signature_valid is True
82
+ assert result.reason is None
67
83
 
68
84
 
69
- # ─── Tamper detection ──────────────────────────────────────────────────
85
+ # ─── Tamper and wrong-key paths ────────────────────────────────────────
70
86
 
71
87
 
72
- def test_tampered_canonical_field_breaks_chain() -> None:
73
- r = _receipt()
74
- r["claim_payload"] = {"po_number": "PO-EVIL", "amount": 999_999}
88
+ @_BOTH
89
+ def test_tampered_canonical_field_breaks_chain(fixture: dict[str, Any]) -> None:
90
+ r = _receipt(fixture)
91
+ r["proposal_payload"] = {"matter_id": "MER-EVIL", "call_cost_usd": 999_999}
75
92
  result = verify_receipt(r, public_key=_PUBLIC_KEY_PEM_STR)
76
- assert result.chain_hash_valid is False
77
93
  assert result.valid is False
78
- assert "chain_hash" in (result.reason or "")
94
+ assert result.chain_hash_valid is False
95
+ assert result.reason == "chain_hash mismatch"
79
96
 
80
97
 
81
98
  def test_tampered_signature_fails_signature_check() -> None:
82
- r = _receipt()
83
- sig = bytearray(bytes.fromhex(r["signature"]))
84
- sig[-1] ^= 0x01
85
- r["signature"] = bytes(sig).hex()
99
+ r = _receipt(_RELEASED)
100
+ # Flip the last hex digit of the DER signature.
101
+ last = r["signature"][-1]
102
+ r["signature"] = r["signature"][:-1] + ("0" if last != "0" else "1")
86
103
  result = verify_receipt(r, public_key=_PUBLIC_KEY_PEM_STR)
104
+ assert result.valid is False
87
105
  assert result.chain_hash_valid is True
88
106
  assert result.signature_valid is False
89
- assert result.valid is False
90
107
 
91
108
 
92
109
  def test_wrong_key_fails_signature_check() -> None:
93
- result = verify_receipt(_receipt(), public_key=_WRONG_KEY_PEM)
110
+ result = verify_receipt(_receipt(_RELEASED), public_key=_WRONG_KEY_PEM)
111
+ assert result.valid is False
94
112
  assert result.chain_hash_valid is True
95
113
  assert result.signature_valid is False
96
- assert result.valid is False
97
114
  assert result.reason == "signature invalid"
98
115
 
99
116
 
100
- # ─── 4-state matrix corners ────────────────────────────────────────────
117
+ # ─── Signature-optional matrix ─────────────────────────────────────────
101
118
 
102
119
 
103
- def test_no_key_skips_signature() -> None:
104
- result = verify_receipt(_receipt()) # public_key=None
105
- assert result.signature_valid is None
106
- assert result.chain_hash_valid is True
120
+ @_BOTH
121
+ def test_no_key_skips_signature(fixture: dict[str, Any]) -> None:
122
+ result = verify_receipt(_receipt(fixture)) # public_key=None
107
123
  assert result.valid is True
124
+ assert result.chain_hash_valid is True
125
+ assert result.signature_valid is None
108
126
  assert result.reason is None
109
127
 
110
128
 
111
129
  def test_key_set_but_no_signature_field() -> None:
112
- r = _receipt()
113
- r.pop("signature")
130
+ r = _receipt(_RELEASED)
131
+ r["signature"] = None
114
132
  result = verify_receipt(r, public_key=_PUBLIC_KEY_PEM_STR)
115
- assert result.signature_valid is False
116
- assert result.chain_hash_valid is True
117
133
  assert result.valid is False
118
- assert result.reason == "receipt has no signature to verify"
134
+ assert result.chain_hash_valid is True
135
+ assert result.signature_valid is False
119
136
 
120
137
 
121
138
  def test_invalid_hex_signature_is_handled() -> None:
122
- r = _receipt()
123
- r["signature"] = "zzz_not_hex_garbage_zzz"
139
+ r = _receipt(_RELEASED)
140
+ r["signature"] = "zz-not-hex"
124
141
  result = verify_receipt(r, public_key=_PUBLIC_KEY_PEM_STR)
125
142
  assert result.valid is False
143
+ assert result.chain_hash_valid is True
126
144
  assert result.signature_valid is False
127
145
  assert "error" in (result.reason or "").lower()
128
146
 
129
147
 
130
- # ─── Never-raises contract ─────────────────────────────────────────────
148
+ # ─── Robustness: verify_receipt never raises ───────────────────────────
131
149
 
132
150
 
133
151
  def test_empty_dict_never_raises() -> None:
@@ -138,14 +156,14 @@ def test_empty_dict_never_raises() -> None:
138
156
 
139
157
 
140
158
  def test_garbage_dict_never_raises() -> None:
141
- result = verify_receipt({"chain_hash": 123, "claim_payload": object()}) # type: ignore[dict-item]
159
+ result = verify_receipt({"chain_hash": 123, "proposal_payload": object()}) # type: ignore[dict-item]
142
160
  assert isinstance(result, ReceiptVerification)
143
161
  assert result.valid is False
144
162
  assert result.reason is not None
145
163
 
146
164
 
147
165
  def test_missing_chain_hash_never_raises() -> None:
148
- r = _receipt()
166
+ r = _receipt(_RELEASED)
149
167
  r.pop("chain_hash")
150
168
  result = verify_receipt(r)
151
169
  assert result.valid is False
@@ -154,12 +172,8 @@ def test_missing_chain_hash_never_raises() -> None:
154
172
 
155
173
 
156
174
  def test_receipt_verification_is_frozen() -> None:
157
- rv = verify_receipt(_receipt())
175
+ rv = verify_receipt(_receipt(_RELEASED))
158
176
  import dataclasses
159
177
 
160
- try:
178
+ with pytest.raises(dataclasses.FrozenInstanceError):
161
179
  rv.valid = False # type: ignore[misc]
162
- except dataclasses.FrozenInstanceError:
163
- pass
164
- else: # pragma: no cover - frozen dataclass must reject assignment
165
- raise AssertionError("ReceiptVerification must be frozen")
@@ -2,4 +2,4 @@ import bindable
2
2
 
3
3
 
4
4
  def test_version() -> None:
5
- assert bindable.__version__ == "0.1.0"
5
+ assert bindable.__version__ == "0.1.1"
@@ -1 +0,0 @@
1
- __version__ = "0.1.0"
@@ -1,4 +0,0 @@
1
- -----BEGIN PUBLIC KEY-----
2
- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEa5xwHbysJvcU00yrqTfM7jGYk/87
3
- cypa/O3b/j6Iy6n+rpJRwDT2VTrbli1n4xoWZ3fXszM/pUzWRVIQt56fTg==
4
- -----END PUBLIC KEY-----
@@ -1,30 +0,0 @@
1
- {
2
- "receipt_id": "rcpt_test_001",
3
- "claim_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
4
- "claim_payload": {
5
- "po_number": "PO-001",
6
- "amount": 1000
7
- },
8
- "contract_id": "ctr_001",
9
- "contract_version": "1.0.0",
10
- "sponsor_identity": "testco",
11
- "sponsor_identity_context": "ctx:testco:procurement",
12
- "counterparty_identity": "supplier-a",
13
- "persistent_agent_id": "agent_testco",
14
- "action_type": "send_invoice",
15
- "proposed_action_id": "claim_001",
16
- "verification_outcome": "passed",
17
- "failed_rule": null,
18
- "reason_detail": null,
19
- "referenced_evidence": [
20
- "evidence-001"
21
- ],
22
- "verified_at": "2026-05-25T14:22:05.123Z",
23
- "nonce": "abc123def456",
24
- "chain_hash": "fa37feb6c264ae089c44a2667eddfa91492712b425c2e5c60091797051e90ca6",
25
- "previous_hash": null,
26
- "signature": "304502202ff0c4527fa3be3208732971e75ac939edb486da248107ffab6f10faa7ef0baa02210096dec776e158633c9e1f88dd5f7e184d0461a7574d6772a8eae89661ea1921d2",
27
- "signing_key_id": "test_signer",
28
- "signed_at": null,
29
- "intervention_artifacts": null
30
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes