rio-receipt-protocol 2.2.0__py3-none-any.whl

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,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,8 @@
1
+ rio_receipt_protocol/__init__.py,sha256=3ijXVqqOODxgaaAaCcI9Mdnme44TGAeVjtV2poSTS0g,848
2
+ rio_receipt_protocol/ledger.py,sha256=o7H5F7cjoy7HRWGV-LllcPy3_CjBipVnD5Dy3Z9eBHI,4280
3
+ rio_receipt_protocol/receipts.py,sha256=MXwU7NlHsUeH06K8dd7qRwfbI7fwYgGz_EafFQAGZw4,9813
4
+ rio_receipt_protocol/verifier.py,sha256=3OIiT73ANAWFryQqb3kiRlGJcpIsXMCthGMTHzmY9ng,5787
5
+ rio_receipt_protocol-2.2.0.dist-info/METADATA,sha256=UzjV_RR1CDtCtoOEBxIdp4MLcJirOnjcosJgxpr8xWY,3978
6
+ rio_receipt_protocol-2.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
7
+ rio_receipt_protocol-2.2.0.dist-info/top_level.txt,sha256=jvkcB089rvMq2daAYPMiisS2Eva7VA4w9hfNeaBTWjE,21
8
+ rio_receipt_protocol-2.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ rio_receipt_protocol