rio-receipt-protocol 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: rio-receipt-protocol
3
+ Version: 2.2.0
4
+ Summary: Cryptographic proof for AI actions. Generate tamper-evident receipts, maintain hash-chained ledgers, and verify AI action audit trails. Zero dependencies.
5
+ Author: RIO Protocol Contributors
6
+ License: MIT OR Apache-2.0
7
+ Project-URL: Homepage, https://rioprotocol-q9cry3ny.manus.space
8
+ Project-URL: Repository, https://github.com/bkr1297-RIO/rio-receipt-protocol
9
+ Project-URL: Issues, https://github.com/bkr1297-RIO/rio-receipt-protocol/issues
10
+ Project-URL: Changelog, https://github.com/bkr1297-RIO/rio-receipt-protocol/blob/main/CHANGELOG.md
11
+ Keywords: ai-governance,ai-receipts,cryptographic-proof,tamper-evident,hash-chain,ledger,ai-audit,ai-safety,sha256,ed25519,openai,anthropic,langchain,ai-agent,compliance,audit-trail
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Security :: Cryptography
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ Provides-Extra: signing
27
+ Requires-Dist: pynacl>=1.5.0; extra == "signing"
28
+
29
+ # rio-receipt-protocol
30
+
31
+ Cryptographic proof for AI actions. Open standard. **Zero required dependencies.**
32
+
33
+ Generate tamper-evident receipts, maintain hash-chained ledgers, and verify AI action audit trails — all with nothing more than the Python standard library.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install rio-receipt-protocol
39
+ ```
40
+
41
+ For Ed25519 signature support (optional):
42
+
43
+ ```bash
44
+ pip install rio-receipt-protocol[signing]
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```python
50
+ from rio_receipt_protocol import (
51
+ hash_intent, hash_execution, generate_receipt, verify_receipt, create_ledger
52
+ )
53
+
54
+ # 1. Hash the intent
55
+ intent_hash = hash_intent(
56
+ intent_id="i-001",
57
+ action="send_email",
58
+ agent_id="agent-1",
59
+ parameters={"to": "user@example.com", "subject": "Hello"},
60
+ timestamp="2026-04-01T00:00:00.000Z",
61
+ )
62
+
63
+ # 2. Hash the execution
64
+ execution_hash = hash_execution(
65
+ intent_id="i-001",
66
+ action="send_email",
67
+ result="sent",
68
+ connector="smtp",
69
+ timestamp="2026-04-01T00:00:01.000Z",
70
+ )
71
+
72
+ # 3. Generate a receipt
73
+ receipt = generate_receipt(
74
+ intent_hash=intent_hash,
75
+ execution_hash=execution_hash,
76
+ intent_id="i-001",
77
+ action="send_email",
78
+ agent_id="agent-1",
79
+ )
80
+
81
+ # 4. Verify it
82
+ result = verify_receipt(receipt)
83
+ assert result["valid"] is True
84
+
85
+ # 5. Append to a tamper-evident ledger
86
+ ledger = create_ledger()
87
+ entry = ledger.append(
88
+ intent_id="i-001",
89
+ action="send_email",
90
+ agent_id="agent-1",
91
+ status="executed",
92
+ detail="Email sent",
93
+ receipt_hash=receipt["hash_chain"]["receipt_hash"],
94
+ )
95
+
96
+ # 6. Verify the chain
97
+ chain = ledger.verify_chain()
98
+ assert chain["valid"] is True
99
+ ```
100
+
101
+ ## API Reference
102
+
103
+ The Python package mirrors the Node.js API exactly:
104
+
105
+ | Function | Description |
106
+ |---|---|
107
+ | `sha256(data)` | SHA-256 hash of a string |
108
+ | `hash_intent(...)` | Hash an intent object |
109
+ | `hash_execution(...)` | Hash an execution record |
110
+ | `hash_governance(...)` | Hash a governance decision (optional) |
111
+ | `hash_authorization(...)` | Hash an authorization record (optional) |
112
+ | `generate_receipt(...)` | Generate a v2.2 receipt |
113
+ | `verify_receipt(receipt)` | Verify a receipt's hash chain |
114
+ | `create_ledger(file_path=None)` | Create a tamper-evident ledger |
115
+ | `verify_receipt_standalone(receipt)` | Independent receipt verification |
116
+ | `verify_chain(entries)` | Verify a ledger hash chain |
117
+ | `verify_receipt_batch(receipts)` | Batch-verify multiple receipts |
118
+
119
+ ## License
120
+
121
+ Dual-licensed under MIT and Apache 2.0.
@@ -0,0 +1,93 @@
1
+ # rio-receipt-protocol
2
+
3
+ Cryptographic proof for AI actions. Open standard. **Zero required dependencies.**
4
+
5
+ Generate tamper-evident receipts, maintain hash-chained ledgers, and verify AI action audit trails — all with nothing more than the Python standard library.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install rio-receipt-protocol
11
+ ```
12
+
13
+ For Ed25519 signature support (optional):
14
+
15
+ ```bash
16
+ pip install rio-receipt-protocol[signing]
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```python
22
+ from rio_receipt_protocol import (
23
+ hash_intent, hash_execution, generate_receipt, verify_receipt, create_ledger
24
+ )
25
+
26
+ # 1. Hash the intent
27
+ intent_hash = hash_intent(
28
+ intent_id="i-001",
29
+ action="send_email",
30
+ agent_id="agent-1",
31
+ parameters={"to": "user@example.com", "subject": "Hello"},
32
+ timestamp="2026-04-01T00:00:00.000Z",
33
+ )
34
+
35
+ # 2. Hash the execution
36
+ execution_hash = hash_execution(
37
+ intent_id="i-001",
38
+ action="send_email",
39
+ result="sent",
40
+ connector="smtp",
41
+ timestamp="2026-04-01T00:00:01.000Z",
42
+ )
43
+
44
+ # 3. Generate a receipt
45
+ receipt = generate_receipt(
46
+ intent_hash=intent_hash,
47
+ execution_hash=execution_hash,
48
+ intent_id="i-001",
49
+ action="send_email",
50
+ agent_id="agent-1",
51
+ )
52
+
53
+ # 4. Verify it
54
+ result = verify_receipt(receipt)
55
+ assert result["valid"] is True
56
+
57
+ # 5. Append to a tamper-evident ledger
58
+ ledger = create_ledger()
59
+ entry = ledger.append(
60
+ intent_id="i-001",
61
+ action="send_email",
62
+ agent_id="agent-1",
63
+ status="executed",
64
+ detail="Email sent",
65
+ receipt_hash=receipt["hash_chain"]["receipt_hash"],
66
+ )
67
+
68
+ # 6. Verify the chain
69
+ chain = ledger.verify_chain()
70
+ assert chain["valid"] is True
71
+ ```
72
+
73
+ ## API Reference
74
+
75
+ The Python package mirrors the Node.js API exactly:
76
+
77
+ | Function | Description |
78
+ |---|---|
79
+ | `sha256(data)` | SHA-256 hash of a string |
80
+ | `hash_intent(...)` | Hash an intent object |
81
+ | `hash_execution(...)` | Hash an execution record |
82
+ | `hash_governance(...)` | Hash a governance decision (optional) |
83
+ | `hash_authorization(...)` | Hash an authorization record (optional) |
84
+ | `generate_receipt(...)` | Generate a v2.2 receipt |
85
+ | `verify_receipt(receipt)` | Verify a receipt's hash chain |
86
+ | `create_ledger(file_path=None)` | Create a tamper-evident ledger |
87
+ | `verify_receipt_standalone(receipt)` | Independent receipt verification |
88
+ | `verify_chain(entries)` | Verify a ledger hash chain |
89
+ | `verify_receipt_batch(receipts)` | Batch-verify multiple receipts |
90
+
91
+ ## License
92
+
93
+ Dual-licensed under MIT and Apache 2.0.
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rio-receipt-protocol"
7
+ version = "2.2.0"
8
+ description = "Cryptographic proof for AI actions. Generate tamper-evident receipts, maintain hash-chained ledgers, and verify AI action audit trails. Zero dependencies."
9
+ readme = "README.md"
10
+ license = {text = "MIT OR Apache-2.0"}
11
+ requires-python = ">=3.9"
12
+ authors = [
13
+ {name = "RIO Protocol Contributors"}
14
+ ]
15
+ keywords = [
16
+ "ai-governance", "ai-receipts", "cryptographic-proof", "tamper-evident",
17
+ "hash-chain", "ledger", "ai-audit", "ai-safety", "sha256", "ed25519",
18
+ "openai", "anthropic", "langchain", "ai-agent", "compliance", "audit-trail",
19
+ ]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: MIT License",
24
+ "License :: OSI Approved :: Apache Software License",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.9",
27
+ "Programming Language :: Python :: 3.10",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Topic :: Security :: Cryptography",
31
+ "Topic :: Software Development :: Libraries :: Python Modules",
32
+ "Typing :: Typed",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://rioprotocol-q9cry3ny.manus.space"
37
+ Repository = "https://github.com/bkr1297-RIO/rio-receipt-protocol"
38
+ Issues = "https://github.com/bkr1297-RIO/rio-receipt-protocol/issues"
39
+ Changelog = "https://github.com/bkr1297-RIO/rio-receipt-protocol/blob/main/CHANGELOG.md"
40
+
41
+ [project.optional-dependencies]
42
+ signing = ["pynacl>=1.5.0"]
43
+
44
+ [tool.setuptools.packages.find]
45
+ include = ["rio_receipt_protocol*"]
@@ -0,0 +1,26 @@
1
+ """
2
+ RIO Receipt Protocol — Python Implementation (v2.2)
3
+
4
+ Cryptographic proof for AI actions. Open standard. Zero required dependencies.
5
+ """
6
+
7
+ __version__ = "2.2.0"
8
+
9
+ from .receipts import (
10
+ sha256, hash_intent, hash_execution, hash_governance, hash_authorization,
11
+ generate_receipt, verify_receipt,
12
+ generate_keypair, sign_receipt,
13
+ )
14
+ from .ledger import GENESIS_HASH, create_ledger
15
+ from .verifier import (
16
+ verify_receipt_standalone, verify_chain,
17
+ verify_receipt_against_ledger, verify_receipt_batch,
18
+ )
19
+
20
+ __all__ = [
21
+ "sha256", "hash_intent", "hash_execution", "hash_governance", "hash_authorization",
22
+ "generate_receipt", "verify_receipt",
23
+ "generate_keypair", "sign_receipt",
24
+ "GENESIS_HASH", "create_ledger",
25
+ "verify_receipt_standalone", "verify_chain", "verify_receipt_against_ledger", "verify_receipt_batch",
26
+ ]
@@ -0,0 +1,106 @@
1
+ """
2
+ RIO Receipt Protocol — Tamper-Evident Ledger
3
+
4
+ Zero external dependencies beyond the Python standard library.
5
+ """
6
+
7
+ import json
8
+ import os
9
+ import uuid
10
+ from datetime import datetime, timezone
11
+
12
+ from .receipts import sha256
13
+
14
+ GENESIS_HASH = "0000000000000000000000000000000000000000000000000000000000000000"
15
+
16
+
17
+ def _canonicalize(entry: dict) -> str:
18
+ return json.dumps(
19
+ {"entry_id": entry["entry_id"], "prev_hash": entry["prev_hash"],
20
+ "timestamp": entry["timestamp"], "intent_id": entry["intent_id"],
21
+ "action": entry["action"], "agent_id": entry["agent_id"],
22
+ "status": entry["status"], "detail": entry["detail"],
23
+ "receipt_hash": entry.get("receipt_hash"),
24
+ "authorization_hash": entry.get("authorization_hash"),
25
+ "intent_hash": entry.get("intent_hash")},
26
+ separators=(",", ":"), ensure_ascii=False,
27
+ )
28
+
29
+
30
+ class Ledger:
31
+ def __init__(self, file_path: str = None):
32
+ self._entries = []
33
+ self._current_hash = GENESIS_HASH
34
+ self._file_path = file_path
35
+ if file_path and os.path.exists(file_path):
36
+ try:
37
+ with open(file_path, "r", encoding="utf-8") as f:
38
+ self._entries = json.load(f)
39
+ if self._entries:
40
+ self._current_hash = self._entries[-1]["ledger_hash"]
41
+ except Exception:
42
+ self._entries = []
43
+ self._current_hash = GENESIS_HASH
44
+
45
+ def _persist(self):
46
+ if not self._file_path:
47
+ return
48
+ os.makedirs(os.path.dirname(self._file_path) or ".", exist_ok=True)
49
+ with open(self._file_path, "w", encoding="utf-8") as f:
50
+ json.dump(self._entries, f, indent=2, ensure_ascii=False)
51
+
52
+ def append(self, intent_id: str, action: str, agent_id: str, status: str, detail: str,
53
+ receipt_hash: str = None, authorization_hash: str = None, intent_hash: str = None) -> dict:
54
+ prev_hash = self._current_hash
55
+ now = datetime.now(timezone.utc)
56
+ timestamp = now.strftime("%Y-%m-%dT%H:%M:%S.") + f"{now.microsecond // 1000:03d}Z"
57
+ entry = {
58
+ "entry_id": str(uuid.uuid4()), "prev_hash": prev_hash, "ledger_hash": None,
59
+ "timestamp": timestamp, "intent_id": intent_id, "action": action,
60
+ "agent_id": agent_id, "status": status, "detail": detail,
61
+ "receipt_hash": receipt_hash, "authorization_hash": authorization_hash,
62
+ "intent_hash": intent_hash,
63
+ }
64
+ entry["ledger_hash"] = sha256(_canonicalize(entry))
65
+ self._current_hash = entry["ledger_hash"]
66
+ self._entries.append(entry)
67
+ self._persist()
68
+ return entry
69
+
70
+ def verify_chain(self) -> dict:
71
+ if not self._entries:
72
+ return {"valid": True, "entries_checked": 0, "first_invalid": None}
73
+ prev = GENESIS_HASH
74
+ for i, e in enumerate(self._entries):
75
+ if e["prev_hash"] != prev:
76
+ return {"valid": False, "entries_checked": i + 1, "first_invalid": i,
77
+ "reason": f"Entry {i} prev_hash mismatch. Expected: {prev}, Got: {e['prev_hash']}"}
78
+ computed = sha256(_canonicalize(e))
79
+ if computed != e["ledger_hash"]:
80
+ return {"valid": False, "entries_checked": i + 1, "first_invalid": i,
81
+ "reason": f"Entry {i} hash mismatch. Computed: {computed}, Stored: {e['ledger_hash']}"}
82
+ prev = e["ledger_hash"]
83
+ return {"valid": True, "entries_checked": len(self._entries), "first_invalid": None}
84
+
85
+ def get_entries(self, limit: int = 100, offset: int = 0) -> list:
86
+ return self._entries[offset:offset + limit]
87
+
88
+ def get_entries_by_intent(self, intent_id: str) -> list:
89
+ return [e for e in self._entries if e["intent_id"] == intent_id]
90
+
91
+ def get_entry_count(self) -> int:
92
+ return len(self._entries)
93
+
94
+ def get_current_hash(self) -> str:
95
+ return self._current_hash
96
+
97
+ def get_latest_entry(self) -> dict:
98
+ return self._entries[-1] if self._entries else None
99
+
100
+ def export(self) -> list:
101
+ return json.loads(json.dumps(self._entries))
102
+
103
+
104
+ def create_ledger(file_path: str = None) -> Ledger:
105
+ """Create a new Ledger instance."""
106
+ return Ledger(file_path=file_path)
@@ -0,0 +1,247 @@
1
+ """
2
+ RIO Receipt Protocol — Receipt Generation and Verification
3
+
4
+ Zero external dependencies beyond the Python standard library.
5
+ """
6
+
7
+ import hashlib
8
+ import json
9
+ import uuid
10
+ from datetime import datetime, timezone
11
+
12
+
13
+ def sha256(data: str) -> str:
14
+ """Compute SHA-256 hash of a string. Returns 64-char lowercase hex."""
15
+ return hashlib.sha256(data.encode("utf-8")).hexdigest()
16
+
17
+
18
+ def hash_intent(intent_id: str, action: str, agent_id: str, parameters: dict, timestamp: str) -> str:
19
+ """Hash an intent object using canonical field order."""
20
+ canonical = json.dumps(
21
+ {"intent_id": intent_id, "action": action, "agent_id": agent_id,
22
+ "parameters": parameters, "timestamp": timestamp},
23
+ separators=(",", ":"), ensure_ascii=False,
24
+ )
25
+ return sha256(canonical)
26
+
27
+
28
+ def hash_execution(intent_id: str, action: str, result: object, connector: str, timestamp: str) -> str:
29
+ """Hash an execution record using canonical field order."""
30
+ canonical = json.dumps(
31
+ {"intent_id": intent_id, "action": action, "result": result,
32
+ "connector": connector, "timestamp": timestamp},
33
+ separators=(",", ":"), ensure_ascii=False,
34
+ )
35
+ return sha256(canonical)
36
+
37
+
38
+ def hash_governance(intent_id: str, status: str, risk_level: str, requires_approval: bool, checks: list) -> str:
39
+ """Hash a governance decision. Optional — only for governed receipts."""
40
+ canonical = json.dumps(
41
+ {"intent_id": intent_id, "status": status, "risk_level": risk_level,
42
+ "requires_approval": requires_approval, "checks": checks},
43
+ separators=(",", ":"), ensure_ascii=False,
44
+ )
45
+ return sha256(canonical)
46
+
47
+
48
+ def hash_authorization(intent_id: str, decision: str, authorized_by: str, timestamp: str, conditions=None) -> str:
49
+ """Hash an authorization record. Optional — only for governed receipts."""
50
+ canonical = json.dumps(
51
+ {"intent_id": intent_id, "decision": decision, "authorized_by": authorized_by,
52
+ "timestamp": timestamp, "conditions": conditions},
53
+ separators=(",", ":"), ensure_ascii=False,
54
+ )
55
+ return sha256(canonical)
56
+
57
+
58
+ def _build_chain_order(data: dict) -> list:
59
+ order = ["intent_hash"]
60
+ if data.get("governance_hash"):
61
+ order.append("governance_hash")
62
+ if data.get("authorization_hash"):
63
+ order.append("authorization_hash")
64
+ order.append("execution_hash")
65
+ order.append("receipt_hash")
66
+ return order
67
+
68
+
69
+ def generate_receipt(
70
+ intent_hash: str, execution_hash: str, intent_id: str, action: str, agent_id: str,
71
+ governance_hash: str = None, authorization_hash: str = None,
72
+ authorized_by: str = None, receipt_type: str = None,
73
+ ingestion: dict = None, identity_binding: dict = None,
74
+ ) -> dict:
75
+ """Generate a RIO Receipt (v2.2)."""
76
+ receipt_id = str(uuid.uuid4())
77
+ now = datetime.now(timezone.utc)
78
+ timestamp = now.strftime("%Y-%m-%dT%H:%M:%S.") + f"{now.microsecond // 1000:03d}Z"
79
+
80
+ is_governed = bool(governance_hash and authorization_hash)
81
+ if receipt_type is None:
82
+ receipt_type = "governed_action" if is_governed else "action"
83
+
84
+ data = {"intent_hash": intent_hash, "execution_hash": execution_hash,
85
+ "governance_hash": governance_hash, "authorization_hash": authorization_hash}
86
+ chain_order = _build_chain_order(data)
87
+
88
+ receipt_content = {"receipt_id": receipt_id}
89
+ for field in chain_order:
90
+ if field != "receipt_hash":
91
+ receipt_content[field] = data[field]
92
+ receipt_content["timestamp"] = timestamp
93
+ receipt_hash = sha256(json.dumps(receipt_content, separators=(",", ":"), ensure_ascii=False))
94
+
95
+ receipt = {
96
+ "receipt_id": receipt_id, "receipt_type": receipt_type,
97
+ "intent_id": intent_id, "action": action, "agent_id": agent_id,
98
+ "authorized_by": authorized_by, "timestamp": timestamp,
99
+ "hash_chain": {
100
+ "intent_hash": intent_hash, "governance_hash": governance_hash,
101
+ "authorization_hash": authorization_hash, "execution_hash": execution_hash,
102
+ "receipt_hash": receipt_hash,
103
+ },
104
+ "verification": {
105
+ "algorithm": "SHA-256", "chain_length": len(chain_order), "chain_order": chain_order,
106
+ },
107
+ }
108
+
109
+ if ingestion:
110
+ receipt["ingestion"] = {
111
+ "source": ingestion.get("source"), "channel": ingestion.get("channel"),
112
+ "source_message_id": ingestion.get("source_message_id"),
113
+ "timestamp": ingestion.get("timestamp", timestamp),
114
+ }
115
+ if identity_binding:
116
+ receipt["identity_binding"] = {
117
+ "signer_id": identity_binding.get("signer_id"),
118
+ "public_key_hex": identity_binding.get("public_key_hex"),
119
+ "signature_payload_hash": identity_binding.get("signature_payload_hash"),
120
+ "verification_method": identity_binding.get("verification_method"),
121
+ "ed25519_signed": identity_binding.get("ed25519_signed", False),
122
+ }
123
+
124
+ return receipt
125
+
126
+
127
+ def verify_receipt(receipt: dict) -> dict:
128
+ """Verify a receipt by recomputing the receipt hash from its components."""
129
+ chain_order = receipt.get("verification", {}).get("chain_order",
130
+ ["intent_hash", "execution_hash", "receipt_hash"])
131
+
132
+ receipt_content = {"receipt_id": receipt["receipt_id"]}
133
+ for field in chain_order:
134
+ if field != "receipt_hash":
135
+ receipt_content[field] = receipt["hash_chain"][field]
136
+ receipt_content["timestamp"] = receipt["timestamp"]
137
+
138
+ computed_hash = sha256(json.dumps(receipt_content, separators=(",", ":"), ensure_ascii=False))
139
+ stored_hash = receipt["hash_chain"]["receipt_hash"]
140
+
141
+ return {
142
+ "valid": computed_hash == stored_hash,
143
+ "computed_hash": computed_hash, "stored_hash": stored_hash,
144
+ "receipt_id": receipt["receipt_id"],
145
+ "receipt_type": receipt.get("receipt_type", "action"),
146
+ }
147
+
148
+
149
+ # ─── Ed25519 Signing (optional — requires no external dependencies) ──────────
150
+
151
+ def generate_keypair() -> dict:
152
+ """
153
+ Generate an Ed25519 keypair using Python's standard library (3.12+)
154
+ or PyNaCl as fallback.
155
+
156
+ Returns dict with: private_key_hex, public_key_hex, private_key_obj
157
+ """
158
+ try:
159
+ # Python 3.12+ has Ed25519 in the standard library
160
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
161
+ from cryptography.hazmat.primitives.serialization import (
162
+ Encoding, NoEncryption, PrivateFormat, PublicFormat,
163
+ )
164
+ private_key = Ed25519PrivateKey.generate()
165
+ private_bytes = private_key.private_bytes(
166
+ Encoding.Raw, PrivateFormat.Raw, NoEncryption(),
167
+ )
168
+ public_bytes = private_key.public_key().public_bytes(
169
+ Encoding.Raw, PublicFormat.Raw,
170
+ )
171
+ return {
172
+ "private_key_hex": private_bytes.hex(),
173
+ "public_key_hex": public_bytes.hex(),
174
+ "private_key_obj": private_key,
175
+ }
176
+ except ImportError:
177
+ pass
178
+
179
+ try:
180
+ from nacl.signing import SigningKey
181
+ from nacl.encoding import HexEncoder
182
+ signing_key = SigningKey.generate()
183
+ return {
184
+ "private_key_hex": signing_key.encode(HexEncoder).decode(),
185
+ "public_key_hex": signing_key.verify_key.encode(HexEncoder).decode(),
186
+ "private_key_obj": signing_key,
187
+ }
188
+ except ImportError:
189
+ raise ImportError(
190
+ "Ed25519 signing requires either 'cryptography' or 'PyNaCl'. "
191
+ "Install one: pip install cryptography OR pip install pynacl"
192
+ )
193
+
194
+
195
+ def sign_receipt(receipt: dict, private_key_obj, public_key_hex: str, signer_id: str) -> dict:
196
+ """
197
+ Sign a receipt with Ed25519.
198
+
199
+ The signed payload is the UTF-8 encoding of the 64-character hex
200
+ receipt_hash string (per spec Section 4.2). The signature is stored
201
+ in identity_binding.signature_hex as a lowercase hex string.
202
+
203
+ This function mutates the receipt in place and also returns it.
204
+
205
+ Args:
206
+ receipt: A generated RIO Receipt (must have hash_chain.receipt_hash)
207
+ private_key_obj: Ed25519 private key object (from generate_keypair)
208
+ public_key_hex: 64-char hex-encoded public key
209
+ signer_id: Identifier of the signing authority
210
+ """
211
+ receipt_hash = receipt.get("hash_chain", {}).get("receipt_hash", "")
212
+ if not receipt_hash or len(receipt_hash) != 64:
213
+ raise ValueError("Cannot sign: receipt has no valid receipt_hash")
214
+
215
+ payload = receipt_hash.encode("utf-8")
216
+
217
+ # Try cryptography library first, then PyNaCl
218
+ try:
219
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
220
+ if isinstance(private_key_obj, Ed25519PrivateKey):
221
+ signature_bytes = private_key_obj.sign(payload)
222
+ signature_hex = signature_bytes.hex()
223
+ else:
224
+ raise TypeError("Not a cryptography key")
225
+ except (ImportError, TypeError):
226
+ from nacl.signing import SigningKey
227
+ if isinstance(private_key_obj, SigningKey):
228
+ signed = private_key_obj.sign(payload)
229
+ signature_hex = signed.signature.hex()
230
+ else:
231
+ raise TypeError(
232
+ "private_key_obj must be an Ed25519PrivateKey (cryptography) "
233
+ "or SigningKey (PyNaCl)"
234
+ )
235
+
236
+ from datetime import datetime, timezone
237
+ receipt["identity_binding"] = {
238
+ "signer_id": signer_id,
239
+ "public_key_hex": public_key_hex,
240
+ "signature_hex": signature_hex,
241
+ "signature_payload_hash": receipt_hash,
242
+ "signed_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.") +
243
+ f"{datetime.now(timezone.utc).microsecond // 1000:03d}Z",
244
+ "verification_method": "ed25519-nacl",
245
+ "ed25519_signed": True,
246
+ }
247
+ return receipt
@@ -0,0 +1,128 @@
1
+ """
2
+ RIO Receipt Protocol — Standalone Verifier
3
+
4
+ Zero external dependencies beyond the Python standard library.
5
+ """
6
+
7
+ import re
8
+ import json
9
+
10
+ from .receipts import sha256
11
+
12
+ GENESIS_HASH = "0000000000000000000000000000000000000000000000000000000000000000"
13
+ _HEX64 = re.compile(r"^[a-f0-9]{64}$")
14
+
15
+
16
+ def verify_receipt_standalone(receipt: dict) -> dict:
17
+ """Verify a single RIO Receipt (proof-layer or governed)."""
18
+ errors = []
19
+ if not receipt.get("receipt_id"):
20
+ errors.append("Missing receipt_id")
21
+ if not receipt.get("timestamp"):
22
+ errors.append("Missing timestamp")
23
+ if not receipt.get("hash_chain"):
24
+ errors.append("Missing hash_chain")
25
+ if not receipt.get("hash_chain"):
26
+ return {"valid": False, "receipt_id": receipt.get("receipt_id", "unknown"), "errors": errors}
27
+
28
+ hc = receipt["hash_chain"]
29
+ for field in ("intent_hash", "execution_hash", "receipt_hash"):
30
+ if not hc.get(field):
31
+ errors.append(f"Missing hash_chain.{field}")
32
+ elif not _HEX64.match(hc[field]):
33
+ errors.append(f"Invalid hash format for hash_chain.{field}")
34
+ for field in ("governance_hash", "authorization_hash"):
35
+ val = hc.get(field)
36
+ if val is not None and val and not _HEX64.match(val):
37
+ errors.append(f"Invalid hash format for hash_chain.{field}")
38
+ if errors:
39
+ return {"valid": False, "receipt_id": receipt.get("receipt_id", "unknown"), "errors": errors}
40
+
41
+ chain_order = receipt.get("verification", {}).get("chain_order",
42
+ ["intent_hash", "execution_hash", "receipt_hash"])
43
+ receipt_content = {"receipt_id": receipt["receipt_id"]}
44
+ for field in chain_order:
45
+ if field != "receipt_hash":
46
+ receipt_content[field] = hc[field]
47
+ receipt_content["timestamp"] = receipt["timestamp"]
48
+ computed_hash = sha256(json.dumps(receipt_content, separators=(",", ":"), ensure_ascii=False))
49
+ stored_hash = hc["receipt_hash"]
50
+
51
+ verification = receipt.get("verification", {})
52
+ if verification.get("algorithm") and verification["algorithm"] != "SHA-256":
53
+ errors.append(f"Unexpected algorithm: {verification['algorithm']}")
54
+ if verification.get("chain_length") is not None and verification["chain_length"] != len(chain_order):
55
+ errors.append(f"chain_length {verification['chain_length']} != chain_order length {len(chain_order)}")
56
+
57
+ hash_valid = computed_hash == stored_hash
58
+ if not hash_valid:
59
+ errors.append(f"Receipt hash mismatch: computed {computed_hash}, stored {stored_hash}")
60
+
61
+ return {
62
+ "valid": hash_valid and len(errors) == 0,
63
+ "receipt_id": receipt["receipt_id"],
64
+ "receipt_type": receipt.get("receipt_type", "action"),
65
+ "computed_hash": computed_hash, "stored_hash": stored_hash,
66
+ "chain_length": len(chain_order), "errors": errors,
67
+ }
68
+
69
+
70
+ def verify_chain(entries: list) -> dict:
71
+ """Verify a ledger hash chain."""
72
+ if not isinstance(entries, list):
73
+ return {"valid": False, "entries_checked": 0, "first_invalid": None, "reason": "Input is not a list"}
74
+ if not entries:
75
+ return {"valid": True, "entries_checked": 0, "first_invalid": None}
76
+ prev = GENESIS_HASH
77
+ for i, e in enumerate(entries):
78
+ if e.get("prev_hash") != prev:
79
+ return {"valid": False, "entries_checked": i + 1, "first_invalid": i,
80
+ "reason": f"Entry {i} ({e.get('entry_id')}) prev_hash mismatch."}
81
+ canonical = json.dumps(
82
+ {"entry_id": e["entry_id"], "prev_hash": e["prev_hash"],
83
+ "timestamp": e["timestamp"], "intent_id": e["intent_id"],
84
+ "action": e["action"], "agent_id": e["agent_id"],
85
+ "status": e["status"], "detail": e["detail"],
86
+ "receipt_hash": e.get("receipt_hash"),
87
+ "authorization_hash": e.get("authorization_hash"),
88
+ "intent_hash": e.get("intent_hash")},
89
+ separators=(",", ":"), ensure_ascii=False,
90
+ )
91
+ computed = sha256(canonical)
92
+ if computed != e.get("ledger_hash"):
93
+ return {"valid": False, "entries_checked": i + 1, "first_invalid": i,
94
+ "reason": f"Entry {i} ({e.get('entry_id')}) hash mismatch."}
95
+ prev = e["ledger_hash"]
96
+ return {"valid": True, "entries_checked": len(entries), "first_invalid": None, "chain_tip": prev}
97
+
98
+
99
+ def verify_receipt_against_ledger(receipt: dict, ledger_entry: dict) -> dict:
100
+ """Cross-verify a receipt against its ledger entry."""
101
+ receipt_result = verify_receipt_standalone(receipt)
102
+ errors = list(receipt_result["errors"])
103
+ if ledger_entry.get("receipt_hash") != receipt["hash_chain"]["receipt_hash"]:
104
+ errors.append("Ledger entry receipt_hash does not match receipt")
105
+ if ledger_entry.get("intent_id") != receipt.get("intent_id"):
106
+ errors.append("Intent ID mismatch")
107
+ if ledger_entry.get("intent_hash") and ledger_entry["intent_hash"] != receipt["hash_chain"]["intent_hash"]:
108
+ errors.append("Intent hash mismatch")
109
+ return {
110
+ "valid": receipt_result["valid"] and len(errors) == len(receipt_result["errors"]),
111
+ "receipt_id": receipt["receipt_id"],
112
+ "entry_id": ledger_entry.get("entry_id"),
113
+ "receipt_valid": receipt_result["valid"],
114
+ "cross_references_valid": len(errors) == len(receipt_result["errors"]),
115
+ "errors": errors,
116
+ }
117
+
118
+
119
+ def verify_receipt_batch(receipts: list) -> dict:
120
+ """Verify multiple receipts in batch."""
121
+ results = [verify_receipt_standalone(r) for r in receipts]
122
+ valid_count = sum(1 for r in results if r["valid"])
123
+ return {
124
+ "total": len(receipts), "valid": valid_count,
125
+ "invalid": len(results) - valid_count,
126
+ "all_valid": all(r["valid"] for r in results),
127
+ "results": results,
128
+ }
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: rio-receipt-protocol
3
+ Version: 2.2.0
4
+ Summary: Cryptographic proof for AI actions. Generate tamper-evident receipts, maintain hash-chained ledgers, and verify AI action audit trails. Zero dependencies.
5
+ Author: RIO Protocol Contributors
6
+ License: MIT OR Apache-2.0
7
+ Project-URL: Homepage, https://rioprotocol-q9cry3ny.manus.space
8
+ Project-URL: Repository, https://github.com/bkr1297-RIO/rio-receipt-protocol
9
+ Project-URL: Issues, https://github.com/bkr1297-RIO/rio-receipt-protocol/issues
10
+ Project-URL: Changelog, https://github.com/bkr1297-RIO/rio-receipt-protocol/blob/main/CHANGELOG.md
11
+ Keywords: ai-governance,ai-receipts,cryptographic-proof,tamper-evident,hash-chain,ledger,ai-audit,ai-safety,sha256,ed25519,openai,anthropic,langchain,ai-agent,compliance,audit-trail
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Security :: Cryptography
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ Provides-Extra: signing
27
+ Requires-Dist: pynacl>=1.5.0; extra == "signing"
28
+
29
+ # rio-receipt-protocol
30
+
31
+ Cryptographic proof for AI actions. Open standard. **Zero required dependencies.**
32
+
33
+ Generate tamper-evident receipts, maintain hash-chained ledgers, and verify AI action audit trails — all with nothing more than the Python standard library.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install rio-receipt-protocol
39
+ ```
40
+
41
+ For Ed25519 signature support (optional):
42
+
43
+ ```bash
44
+ pip install rio-receipt-protocol[signing]
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```python
50
+ from rio_receipt_protocol import (
51
+ hash_intent, hash_execution, generate_receipt, verify_receipt, create_ledger
52
+ )
53
+
54
+ # 1. Hash the intent
55
+ intent_hash = hash_intent(
56
+ intent_id="i-001",
57
+ action="send_email",
58
+ agent_id="agent-1",
59
+ parameters={"to": "user@example.com", "subject": "Hello"},
60
+ timestamp="2026-04-01T00:00:00.000Z",
61
+ )
62
+
63
+ # 2. Hash the execution
64
+ execution_hash = hash_execution(
65
+ intent_id="i-001",
66
+ action="send_email",
67
+ result="sent",
68
+ connector="smtp",
69
+ timestamp="2026-04-01T00:00:01.000Z",
70
+ )
71
+
72
+ # 3. Generate a receipt
73
+ receipt = generate_receipt(
74
+ intent_hash=intent_hash,
75
+ execution_hash=execution_hash,
76
+ intent_id="i-001",
77
+ action="send_email",
78
+ agent_id="agent-1",
79
+ )
80
+
81
+ # 4. Verify it
82
+ result = verify_receipt(receipt)
83
+ assert result["valid"] is True
84
+
85
+ # 5. Append to a tamper-evident ledger
86
+ ledger = create_ledger()
87
+ entry = ledger.append(
88
+ intent_id="i-001",
89
+ action="send_email",
90
+ agent_id="agent-1",
91
+ status="executed",
92
+ detail="Email sent",
93
+ receipt_hash=receipt["hash_chain"]["receipt_hash"],
94
+ )
95
+
96
+ # 6. Verify the chain
97
+ chain = ledger.verify_chain()
98
+ assert chain["valid"] is True
99
+ ```
100
+
101
+ ## API Reference
102
+
103
+ The Python package mirrors the Node.js API exactly:
104
+
105
+ | Function | Description |
106
+ |---|---|
107
+ | `sha256(data)` | SHA-256 hash of a string |
108
+ | `hash_intent(...)` | Hash an intent object |
109
+ | `hash_execution(...)` | Hash an execution record |
110
+ | `hash_governance(...)` | Hash a governance decision (optional) |
111
+ | `hash_authorization(...)` | Hash an authorization record (optional) |
112
+ | `generate_receipt(...)` | Generate a v2.2 receipt |
113
+ | `verify_receipt(receipt)` | Verify a receipt's hash chain |
114
+ | `create_ledger(file_path=None)` | Create a tamper-evident ledger |
115
+ | `verify_receipt_standalone(receipt)` | Independent receipt verification |
116
+ | `verify_chain(entries)` | Verify a ledger hash chain |
117
+ | `verify_receipt_batch(receipts)` | Batch-verify multiple receipts |
118
+
119
+ ## License
120
+
121
+ Dual-licensed under MIT and Apache 2.0.
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ rio_receipt_protocol/__init__.py
4
+ rio_receipt_protocol/ledger.py
5
+ rio_receipt_protocol/receipts.py
6
+ rio_receipt_protocol/verifier.py
7
+ rio_receipt_protocol.egg-info/PKG-INFO
8
+ rio_receipt_protocol.egg-info/SOURCES.txt
9
+ rio_receipt_protocol.egg-info/dependency_links.txt
10
+ rio_receipt_protocol.egg-info/requires.txt
11
+ rio_receipt_protocol.egg-info/top_level.txt
12
+ tests/test_conformance.py
@@ -0,0 +1,3 @@
1
+
2
+ [signing]
3
+ pynacl>=1.5.0
@@ -0,0 +1 @@
1
+ rio_receipt_protocol
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,211 @@
1
+ """
2
+ RIO Receipt Protocol v2.2 — Python Conformance Tests
3
+
4
+ Mirrors the Node.js conformance test suite (tests/conformance.test.mjs).
5
+ Tests proof-layer receipts, governed receipts, hash integrity, ledger
6
+ operations, cross-verification, batch verification, mixed types, and
7
+ optional extensions.
8
+ """
9
+
10
+ import sys
11
+ import os
12
+ import re
13
+ import copy
14
+
15
+ # Add parent to path for imports
16
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
17
+
18
+ from rio_receipt_protocol import (
19
+ sha256,
20
+ hash_intent,
21
+ hash_execution,
22
+ hash_governance,
23
+ hash_authorization,
24
+ generate_receipt,
25
+ verify_receipt,
26
+ create_ledger,
27
+ verify_receipt_standalone,
28
+ verify_chain,
29
+ verify_receipt_against_ledger,
30
+ verify_receipt_batch,
31
+ GENESIS_HASH,
32
+ )
33
+
34
+ passed = 0
35
+ failed = 0
36
+ total = 0
37
+ HEX64 = re.compile(r"^[a-f0-9]{64}$")
38
+
39
+
40
+ def test(name, condition):
41
+ global passed, failed, total
42
+ total += 1
43
+ if condition:
44
+ passed += 1
45
+ print(f" \033[32m✓\033[0m {name}")
46
+ else:
47
+ failed += 1
48
+ print(f" \033[31m✗\033[0m {name}")
49
+
50
+
51
+ def section(name):
52
+ print(f"\n{name}")
53
+
54
+
55
+ INTENT = {
56
+ "intent_id": "test-intent-001",
57
+ "action": "send_email",
58
+ "agent_id": "test-agent-001",
59
+ "parameters": {"to": "user@example.com", "subject": "Test"},
60
+ "timestamp": "2026-04-01T00:00:00.000Z",
61
+ }
62
+ EXECUTION = {
63
+ "intent_id": "test-intent-001",
64
+ "action": "send_email",
65
+ "result": "sent",
66
+ "connector": "smtp",
67
+ "timestamp": "2026-04-01T00:00:01.000Z",
68
+ }
69
+ GOVERNANCE = {
70
+ "intent_id": "test-intent-001",
71
+ "status": "approved",
72
+ "risk_level": "low",
73
+ "requires_approval": False,
74
+ "checks": ["policy_check", "rate_limit"],
75
+ }
76
+ AUTHORIZATION = {
77
+ "intent_id": "test-intent-001",
78
+ "decision": "approved",
79
+ "authorized_by": "HUMAN:admin@example.com",
80
+ "timestamp": "2026-04-01T00:00:00.500Z",
81
+ "conditions": None,
82
+ }
83
+
84
+ section("1. Proof-Layer Receipt Generation")
85
+ intent_hash = hash_intent(**INTENT)
86
+ execution_hash = hash_execution(**EXECUTION)
87
+ proof_receipt = generate_receipt(
88
+ intent_hash=intent_hash, execution_hash=execution_hash,
89
+ intent_id=INTENT["intent_id"], action=INTENT["action"], agent_id=INTENT["agent_id"],
90
+ )
91
+ test("generates a proof-layer receipt with 3-hash chain", proof_receipt["verification"]["chain_length"] == 3)
92
+ test("proof-layer receipt has all required core fields",
93
+ all(proof_receipt.get(f) is not None for f in
94
+ ["receipt_id", "receipt_type", "intent_id", "action", "agent_id", "timestamp", "hash_chain", "verification"]))
95
+ test("proof-layer chain_order is [intent_hash, execution_hash, receipt_hash]",
96
+ proof_receipt["verification"]["chain_order"] == ["intent_hash", "execution_hash", "receipt_hash"])
97
+ test("proof-layer receipt self-verifies", verify_receipt(proof_receipt)["valid"])
98
+ test("proof-layer receipt verifies with standalone verifier", verify_receipt_standalone(proof_receipt)["valid"])
99
+
100
+ section("2. Governed Receipt Generation (Extension)")
101
+ governance_hash = hash_governance(**GOVERNANCE)
102
+ authorization_hash = hash_authorization(**AUTHORIZATION)
103
+ governed_receipt = generate_receipt(
104
+ intent_hash=intent_hash, execution_hash=execution_hash,
105
+ governance_hash=governance_hash, authorization_hash=authorization_hash,
106
+ intent_id=INTENT["intent_id"], action=INTENT["action"], agent_id=INTENT["agent_id"],
107
+ authorized_by="HUMAN:admin@example.com",
108
+ )
109
+ test("generates a governed receipt with 5-hash chain", governed_receipt["verification"]["chain_length"] == 5)
110
+ test("governed receipt chain_order has all 5 fields in order",
111
+ governed_receipt["verification"]["chain_order"] == [
112
+ "intent_hash", "governance_hash", "authorization_hash", "execution_hash", "receipt_hash"])
113
+ test("governed receipt self-verifies", verify_receipt(governed_receipt)["valid"])
114
+ test("governed receipt verifies with standalone verifier", verify_receipt_standalone(governed_receipt)["valid"])
115
+
116
+ section("3. Hash Integrity")
117
+ test("SHA-256 produces 64-char hex string", bool(HEX64.match(sha256("test"))))
118
+ test("SHA-256 is deterministic", sha256("hello") == sha256("hello"))
119
+ test("SHA-256 is collision-resistant (different inputs = different hashes)", sha256("input_a") != sha256("input_b"))
120
+ test("all receipt hashes are valid 64-char hex",
121
+ all(HEX64.match(v) for k, v in proof_receipt["hash_chain"].items() if v is not None))
122
+ tampered = copy.deepcopy(proof_receipt)
123
+ tampered["hash_chain"]["intent_hash"] = "a" * 64
124
+ test("tampered receipt fails verification", not verify_receipt(tampered)["valid"])
125
+ test("tampered receipt fails standalone verification", not verify_receipt_standalone(tampered)["valid"])
126
+
127
+ section("4. Ledger Operations")
128
+ ledger = create_ledger()
129
+ test("ledger starts empty with genesis hash",
130
+ ledger.get_entry_count() == 0 and ledger.get_current_hash() == GENESIS_HASH)
131
+ entry1 = ledger.append(
132
+ intent_id=INTENT["intent_id"], action=INTENT["action"], agent_id=INTENT["agent_id"],
133
+ status="executed", detail="Email sent successfully",
134
+ receipt_hash=proof_receipt["hash_chain"]["receipt_hash"],
135
+ )
136
+ test("append creates a valid entry with correct prev_hash",
137
+ entry1["prev_hash"] == GENESIS_HASH and bool(HEX64.match(entry1["ledger_hash"])))
138
+ entry2 = ledger.append(
139
+ intent_id="test-intent-002", action="schedule_meeting", agent_id=INTENT["agent_id"],
140
+ status="executed", detail="Meeting scheduled",
141
+ )
142
+ test("chain links correctly across multiple entries", entry2["prev_hash"] == entry1["ledger_hash"])
143
+ test("ledger chain verifies with standalone verifier", ledger.verify_chain()["valid"])
144
+ entries = ledger.export()
145
+ entries[0]["detail"] = "TAMPERED"
146
+ test("tampered ledger entry breaks chain verification", not verify_chain(entries)["valid"])
147
+
148
+ section("5. Cross-Verification (Receipt and Ledger)")
149
+ test("receipt cross-verifies against matching ledger entry",
150
+ verify_receipt_against_ledger(proof_receipt, entry1)["valid"])
151
+ test("mismatched receipt/ledger fails cross-verification",
152
+ not verify_receipt_against_ledger(proof_receipt, entry2)["valid"])
153
+
154
+ section("6. Batch Verification")
155
+ test("batch verifies multiple valid receipts",
156
+ verify_receipt_batch([proof_receipt, governed_receipt])["all_valid"])
157
+ tampered2 = copy.deepcopy(governed_receipt)
158
+ tampered2["hash_chain"]["receipt_hash"] = "a" * 64
159
+ batch_tampered = verify_receipt_batch([proof_receipt, tampered2])
160
+ test("batch detects tampered receipt among valid ones",
161
+ not batch_tampered["all_valid"] and batch_tampered["valid"] == 1)
162
+
163
+ section("7. Mixed Receipt Types (Proof-Layer + Governed)")
164
+ test("batch verifies mix of proof-layer and governed receipts",
165
+ verify_receipt_batch([proof_receipt, governed_receipt])["all_valid"])
166
+ mixed_ledger = create_ledger()
167
+ mixed_ledger.append(intent_id=INTENT["intent_id"], action=INTENT["action"],
168
+ agent_id=INTENT["agent_id"], status="executed",
169
+ detail="Proof-layer receipt", receipt_hash=proof_receipt["hash_chain"]["receipt_hash"])
170
+ mixed_ledger.append(intent_id=INTENT["intent_id"], action=INTENT["action"],
171
+ agent_id=INTENT["agent_id"], status="executed",
172
+ detail="Governed receipt", receipt_hash=governed_receipt["hash_chain"]["receipt_hash"])
173
+ test("ledger accepts both proof-layer and governed receipts", mixed_ledger.verify_chain()["valid"])
174
+
175
+ section("8. Optional Extensions")
176
+ ingestion_receipt = generate_receipt(
177
+ intent_hash=intent_hash, execution_hash=execution_hash,
178
+ intent_id=INTENT["intent_id"], action=INTENT["action"], agent_id=INTENT["agent_id"],
179
+ ingestion={"source": "api", "channel": "POST /intent", "source_message_id": "msg-123"},
180
+ )
181
+ test("receipt with ingestion provenance",
182
+ verify_receipt(ingestion_receipt)["valid"] and ingestion_receipt.get("ingestion") is not None)
183
+ identity_receipt = generate_receipt(
184
+ intent_hash=intent_hash, execution_hash=execution_hash,
185
+ intent_id=INTENT["intent_id"], action=INTENT["action"], agent_id=INTENT["agent_id"],
186
+ identity_binding={"signer_id": "human-root", "public_key_hex": "a" * 64,
187
+ "signature_payload_hash": "b" * 64, "verification_method": "ed25519-nacl",
188
+ "ed25519_signed": True},
189
+ )
190
+ test("receipt with identity binding",
191
+ verify_receipt(identity_receipt)["valid"] and identity_receipt.get("identity_binding") is not None)
192
+ plain_receipt = generate_receipt(
193
+ intent_hash=intent_hash, execution_hash=execution_hash,
194
+ intent_id=INTENT["intent_id"], action=INTENT["action"], agent_id=INTENT["agent_id"],
195
+ )
196
+ test("receipt without optional extensions still verifies", verify_receipt(plain_receipt)["valid"])
197
+
198
+ print()
199
+ print("=" * 60)
200
+ print("RIO Receipt Protocol v2.2 Python Conformance Results")
201
+ print("=" * 60)
202
+ print(f" Total: {total}")
203
+ print(f" Passed: {passed}")
204
+ if failed:
205
+ print(f" Failed: {failed}")
206
+ print("=" * 60)
207
+ if failed == 0:
208
+ print("\u2713 CONFORMANT \u2014 All tests passed")
209
+ else:
210
+ print("\u2717 NON-CONFORMANT \u2014 Some tests failed")
211
+ sys.exit(1)