Skip to content

Commit 641c8b3

Browse files
Add orynq-ai-auditability community ability
Adds a community ability that creates tamper-proof, blockchain-anchored audit trails for AI conversations using Orynq Proof-of-Inference protocol. Builds SHA-256 rolling hash chains and anchors them to Cardano L1 (metadata label 2222) for independent verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8ffcc13 commit 641c8b3

4 files changed

Lines changed: 286 additions & 0 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Orynq AI Auditability
2+
3+
![Community](https://img.shields.io/badge/OpenHome-Community-orange?style=flat-square)
4+
![Author](https://img.shields.io/badge/Author-@flux--point--studios-lightgrey?style=flat-square)
5+
![Cardano](https://img.shields.io/badge/Blockchain-Cardano-blue?style=flat-square)
6+
7+
## What It Does
8+
9+
Creates tamper-proof, blockchain-anchored audit trails for AI conversations using Orynq Proof-of-Inference protocol. Each message is hashed into a rolling SHA-256 chain and anchored to Cardano L1 (metadata label 2222) for independent verification.
10+
11+
## Suggested Trigger Words
12+
13+
- "audit my AI"
14+
- "create audit trail"
15+
- "blockchain audit"
16+
- "proof of inference"
17+
- "verify AI"
18+
- "AI accountability"
19+
- "audit this conversation"
20+
21+
## Setup
22+
23+
1. Get an Orynq API key at [orynq.com](https://orynq.com)
24+
2. Replace YOUR_ORYNQ_API_KEY_HERE in main.py with your key
25+
3. Upload the ability to your OpenHome dashboard
26+
27+
## How It Works
28+
29+
1. You trigger the ability with a phrase like "audit my AI"
30+
2. Speak the messages you want included in the audit trail
31+
3. Say "done" when finished
32+
4. The ability builds a SHA-256 rolling hash chain where each entry links to the previous one
33+
5. The chain is submitted to Orynq API for anchoring to Cardano L1
34+
6. You receive a transaction hash for independent verification
35+
36+
The hash chain is tamper-proof: changing any single entry invalidates all subsequent hashes, making unauthorized modifications detectable.
37+
38+
## Example Conversation
39+
40+
> **User:** "Audit my AI"
41+
> **AI:** "I will create a blockchain audit trail for this conversation. Each message gets hashed into a tamper-proof chain anchored to Cardano."
42+
> **User:** "Log that the model recommended treatment plan A for patient 42"
43+
> **AI:** "Got it. I will include your messages in the audit trail. Say more to add entries, or say done when finished."
44+
> **User:** "The confidence score was 0.94 with no flagged biases"
45+
> **AI:** "Added. 2 messages in the trail so far. Say done to finalize."
46+
> **User:** "Done"
47+
> **AI:** "Your 2-entry audit trail has been anchored to Cardano. It is now tamper-proof and independently verifiable."
48+
49+
## Why Auditability Matters
50+
51+
As AI systems make increasingly consequential decisions, organizations need provable records of what AI said and when. Traditional logging can be altered. Blockchain anchoring provides:
52+
53+
- **Tamper evidence** - Any modification breaks the hash chain
54+
- **Independent verification** - Anyone can verify the trail on-chain
55+
- **Regulatory compliance** - Immutable records for audit requirements
56+
- **Accountability** - Provable AI decision history
57+
58+
## Technical Details
59+
60+
- **Hash algorithm**: SHA-256 rolling chain
61+
- **Blockchain**: Cardano L1
62+
- **Metadata label**: 2222 (Orynq Proof-of-Inference standard)
63+
- **Protocol**: Orynq Flux (402 Payment Required for anchoring fees)
64+
- **SDK**: [orynq-sdk](https://github.com/flux-point-studios/orynq-sdk)

community/orynq-ai-auditability/__init__.py

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "orynq-ai-auditability",
3+
"display_name": "Orynq AI Auditability",
4+
"description": "Create tamper-proof, blockchain-anchored audit trails for AI conversations using Orynq Proof-of-Inference on Cardano.",
5+
"version": "1.0.0",
6+
"author": "flux-point-studios",
7+
"category": "productivity",
8+
"hotwords": [
9+
"audit my AI",
10+
"create audit trail",
11+
"blockchain audit",
12+
"proof of inference",
13+
"verify AI",
14+
"AI accountability",
15+
"audit this conversation",
16+
"orynq audit",
17+
"tamper proof",
18+
"hash chain"
19+
],
20+
"requires_api_key": true,
21+
"api_key_name": "ORYNQ_API_KEY",
22+
"api_key_url": "https://orynq.com"
23+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import hashlib
2+
import json
3+
import time
4+
from typing import Optional
5+
6+
import requests
7+
from src.agent.capability import MatchingCapability
8+
from src.agent.capability_worker import CapabilityWorker
9+
from src.main import AgentWorker
10+
11+
# =============================================================================
12+
# Orynq AI Auditability Ability
13+
#
14+
# Creates tamper-proof, blockchain-anchored audit trails for AI conversations
15+
# using Orynq's Proof-of-Inference protocol. Builds SHA-256 rolling hash
16+
# chains and anchors them to Cardano L1 (metadata label 2222).
17+
# =============================================================================
18+
19+
ORYNQ_API_URL = "https://api.orynq.com"
20+
ORYNQ_API_KEY = "YOUR_ORYNQ_API_KEY_HERE"
21+
22+
EXIT_WORDS = {"stop", "exit", "quit", "done", "cancel", "bye", "goodbye", "leave", "nothing"}
23+
24+
25+
class OrynqAiAuditabilityCapability(MatchingCapability):
26+
worker: AgentWorker = None
27+
capability_worker: CapabilityWorker = None
28+
29+
# Do not change following tag of register capability
30+
#{{register_capability}}
31+
32+
def call(self, worker: AgentWorker):
33+
self.worker = worker
34+
self.capability_worker = CapabilityWorker(self)
35+
self.worker.session_tasks.create(self.run())
36+
37+
def _log_info(self, msg: str):
38+
if self.worker:
39+
self.worker.editor_logging_handler.info(msg)
40+
41+
def _log_error(self, msg: str):
42+
if self.worker:
43+
self.worker.editor_logging_handler.error(msg)
44+
45+
def _is_exit(self, text: Optional[str]) -> bool:
46+
return (text or "").lower().strip() in EXIT_WORDS
47+
48+
def _build_hash_chain_entry(
49+
self,
50+
role: str,
51+
content: str,
52+
previous_hash: str,
53+
sequence: int,
54+
) -> dict:
55+
"""Build a single entry in the rolling SHA-256 hash chain."""
56+
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
57+
payload = json.dumps(
58+
{
59+
"seq": sequence,
60+
"role": role,
61+
"content": content,
62+
"prev": previous_hash,
63+
"ts": timestamp,
64+
},
65+
sort_keys=True,
66+
separators=(",", ":"),
67+
)
68+
entry_hash = hashlib.sha256(payload.encode("utf-8")).hexdigest()
69+
return {
70+
"seq": sequence,
71+
"role": role,
72+
"content_hash": hashlib.sha256(content.encode("utf-8")).hexdigest(),
73+
"chain_hash": entry_hash,
74+
"previous_hash": previous_hash,
75+
"timestamp": timestamp,
76+
}
77+
78+
def _submit_to_orynq(self, trace: list[dict]) -> Optional[dict]:
79+
"""Submit the hash chain trace to Orynq for Cardano L1 anchoring."""
80+
try:
81+
headers = {
82+
"Authorization": "Bearer " + ORYNQ_API_KEY,
83+
"Content-Type": "application/json",
84+
}
85+
body = {
86+
"protocol": "proof-of-inference",
87+
"version": "1.0",
88+
"chain": "cardano:mainnet",
89+
"metadata_label": 2222,
90+
"trace": trace,
91+
"anchor_hash": trace[-1]["chain_hash"] if trace else "",
92+
}
93+
response = requests.post(
94+
ORYNQ_API_URL + "/v1/audit/anchor",
95+
headers=headers,
96+
json=body,
97+
timeout=30,
98+
)
99+
if response.status_code == 200:
100+
data = response.json()
101+
self._log_info("[OrynqAudit] Anchored: tx=" + str(data.get("tx_hash", "pending")))
102+
return data
103+
if response.status_code == 402:
104+
self._log_info("[OrynqAudit] Payment required for anchoring")
105+
return {"status": "payment_required", "message": "Anchoring requires payment via Flux protocol"}
106+
self._log_error("[OrynqAudit] API returned " + str(response.status_code) + ": " + response.text)
107+
return None
108+
except Exception as e:
109+
self._log_error("[OrynqAudit] Submission error: " + str(e))
110+
return None
111+
112+
async def run(self):
113+
try:
114+
await self.capability_worker.speak(
115+
"I will create a blockchain audit trail for this conversation. "
116+
"Each message gets hashed into a tamper-proof chain anchored to Cardano."
117+
)
118+
119+
user_input = await self.capability_worker.user_response()
120+
if self._is_exit(user_input):
121+
await self.capability_worker.speak("Okay, no audit trail created.")
122+
return
123+
124+
await self.capability_worker.speak(
125+
"Got it. I will include your messages in the audit trail. "
126+
"Say more to add entries, or say done when finished."
127+
)
128+
129+
messages_to_audit = [("user", user_input)]
130+
131+
while True:
132+
next_input = await self.capability_worker.user_response()
133+
if not next_input or self._is_exit(next_input):
134+
break
135+
if next_input.lower().strip() == "done":
136+
break
137+
messages_to_audit.append(("user", next_input))
138+
count = str(len(messages_to_audit))
139+
await self.capability_worker.speak(
140+
"Added. " + count + " messages in the trail so far. Say done to finalize."
141+
)
142+
143+
if not messages_to_audit:
144+
await self.capability_worker.speak("No messages to audit. Cancelling.")
145+
return
146+
147+
# Build the rolling hash chain
148+
trace = []
149+
previous_hash = "0" * 64 # genesis hash
150+
seq = 0
151+
for role, content in messages_to_audit:
152+
entry = self._build_hash_chain_entry(role, content, previous_hash, seq)
153+
trace.append(entry)
154+
previous_hash = entry["chain_hash"]
155+
seq += 1
156+
157+
trace_len = str(len(trace))
158+
await self.capability_worker.speak(
159+
"Built a " + trace_len + "-entry hash chain. Submitting to Orynq for Cardano anchoring."
160+
)
161+
162+
result = self._submit_to_orynq(trace)
163+
164+
if result:
165+
if result.get("status") == "payment_required":
166+
response_text = self.capability_worker.text_to_text_response(
167+
"Explain briefly that the audit trail was built but anchoring to Cardano "
168+
"requires a small payment through the Orynq Flux protocol. The hash chain "
169+
"has " + trace_len + " entries. Keep it to one sentence for voice."
170+
)
171+
elif result.get("tx_hash"):
172+
tx = str(result["tx_hash"])
173+
response_text = self.capability_worker.text_to_text_response(
174+
"Summarize for voice: audit trail with " + trace_len + " entries was anchored "
175+
"to Cardano. Transaction hash is " + tx + ". "
176+
"Mention it is now tamper-proof and independently verifiable. One sentence."
177+
)
178+
else:
179+
response_text = self.capability_worker.text_to_text_response(
180+
"Summarize for voice: audit trail with " + trace_len + " entries was submitted "
181+
"to Orynq and is being processed for Cardano anchoring. One sentence."
182+
)
183+
await self.capability_worker.speak(response_text)
184+
else:
185+
anchor_hash = trace[-1]["chain_hash"]
186+
await self.capability_worker.speak(
187+
"I built the hash chain locally but could not reach Orynq right now. "
188+
"Your anchor hash is " + anchor_hash[:16] + ". You can verify it later."
189+
)
190+
191+
except Exception as e:
192+
self._log_error("[OrynqAudit] Unexpected error: " + str(e))
193+
if self.capability_worker:
194+
await self.capability_worker.speak(
195+
"Sorry, something went wrong creating the audit trail. Please try again."
196+
)
197+
finally:
198+
if self.capability_worker:
199+
self.capability_worker.resume_normal_flow()

0 commit comments

Comments
 (0)