vouch-protocol 1.0.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.
vouch/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .auditor import Auditor
2
+ from .verifier import Verifier
3
+ from .seal import vouch_seal_component
vouch/auditor.py ADDED
@@ -0,0 +1,28 @@
1
+ import json
2
+ import time
3
+ import uuid
4
+ from jwcrypto import jwk, jws
5
+
6
+ class Auditor:
7
+ def __init__(self, private_key_json):
8
+ self.signing_key = jwk.JWK.from_json(private_key_json)
9
+
10
+ def issue_vouch(self, agent_data):
11
+ # Default starting score for new agents
12
+ reputation_score = agent_data.get('reputation_score', 50)
13
+
14
+ payload = {
15
+ "jti": str(uuid.uuid4()),
16
+ "sub": agent_data.get('did'),
17
+ "iss": "did:web:vouch-authority",
18
+ "nbf": int(time.time()),
19
+ "exp": int(time.time()) + 86400, # 24 Hours
20
+ "vc": {
21
+ "integrity_hash": agent_data.get('integrity_hash'),
22
+ "reputation_score": reputation_score, # <-- NEW MANIFESTO FIELD
23
+ "type": "Identity+Reputation"
24
+ }
25
+ }
26
+ signer = jws.JWS(json.dumps(payload).encode('utf-8'))
27
+ signer.add_signature(self.signing_key, None, json.dumps({"alg":"EdDSA"}), json.dumps({"kid":self.signing_key.key_id}))
28
+ return {"certificate": signer.serialize(compact=True)}
File without changes
@@ -0,0 +1,22 @@
1
+ from vouch import Auditor
2
+ import os
3
+ from typing import Annotated
4
+
5
+ def sign_action(
6
+ intent: Annotated[str, "The action you are about to take"]
7
+ ) -> str:
8
+ """
9
+ Generates a Vouch-Token to sign a sensitive action for AutoGen agents.
10
+ """
11
+ key = os.getenv("VOUCH_PRIVATE_KEY")
12
+ did = os.getenv("VOUCH_DID")
13
+
14
+ if not key:
15
+ return "Error: VOUCH_PRIVATE_KEY not configured."
16
+
17
+ auditor = Auditor(key)
18
+ proof = auditor.issue_vouch({
19
+ "did": did,
20
+ "integrity_hash": intent
21
+ })
22
+ return f"Vouch-Token: {proof['certificate']}"
File without changes
@@ -0,0 +1,30 @@
1
+ import os
2
+ from vouch import Auditor
3
+
4
+ # Mock decorator if AutoGPT is not installed
5
+ try:
6
+ from autogpt.command_decorator import command
7
+ except ImportError:
8
+ def command(*args, **kwargs):
9
+ def decorator(func): return func
10
+ return decorator
11
+
12
+ @command(
13
+ "sign_with_vouch",
14
+ "Generates a Vouch-Token to prove identity",
15
+ {
16
+ "intent": {"type": "string", "description": "What you are doing", "required": True},
17
+ "target_service": {"type": "string", "description": "Target domain", "required": True}
18
+ },
19
+ )
20
+ def sign_with_vouch(intent: str, target_service: str) -> str:
21
+ try:
22
+ key = os.getenv("VOUCH_PRIVATE_KEY")
23
+ did = os.getenv("VOUCH_DID", "did:web:anonymous")
24
+ if not key: return "Error: VOUCH_PRIVATE_KEY missing"
25
+
26
+ auditor = Auditor(key)
27
+ proof = auditor.issue_vouch({"did": did, "integrity_hash": f"{target_service}:{intent}"})
28
+ return f"Vouch-Token generated. Add header: 'Vouch-Token: {proof['certificate']}'"
29
+ except Exception as e:
30
+ return f"Error: {str(e)}"
File without changes
@@ -0,0 +1,16 @@
1
+ from langchain.tools import tool
2
+ from vouch import Auditor
3
+ import os
4
+
5
+ class VouchCrewTools:
6
+ @tool("Sign Request with Vouch")
7
+ def sign_request(intent: str):
8
+ """Generates a cryptographic Vouch-Token. Useful for proving identity to external APIs."""
9
+ key = os.getenv("VOUCH_PRIVATE_KEY")
10
+ did = os.getenv("VOUCH_DID")
11
+
12
+ if not key: return "Error: VOUCH_PRIVATE_KEY not found."
13
+
14
+ auditor = Auditor(key)
15
+ proof = auditor.issue_vouch({"did": did, "integrity_hash": intent})
16
+ return f"Vouch-Token: {proof['certificate']}"
File without changes
@@ -0,0 +1,25 @@
1
+ from typing import Optional, Type
2
+ from langchain.tools import BaseTool
3
+ from pydantic import BaseModel, Field
4
+ from vouch import Auditor
5
+ import os
6
+
7
+ class VouchSignerInput(BaseModel):
8
+ intent: str = Field(description="A description of the action being signed (e.g. 'search_database')")
9
+
10
+ class VouchSignerTool(BaseTool):
11
+ name = "vouch_signer"
12
+ description = "Generates a verifiable identity proof (Vouch-Token) for sensitive actions."
13
+ args_schema: Type[BaseModel] = VouchSignerInput
14
+
15
+ def _run(self, intent: str) -> str:
16
+ private_key = os.getenv("VOUCH_PRIVATE_KEY")
17
+ agent_did = os.getenv("VOUCH_DID")
18
+ if not private_key: return "Error: VOUCH_PRIVATE_KEY not set."
19
+
20
+ auditor = Auditor(private_key)
21
+ proof = auditor.issue_vouch({"did": agent_did, "integrity_hash": intent})
22
+ return f"Vouch-Token: {proof['certificate']}"
23
+
24
+ def _arun(self, intent: str):
25
+ raise NotImplementedError("Async not implemented yet")
@@ -0,0 +1,49 @@
1
+ from typing import Optional, Type
2
+ from pydantic import BaseModel, Field
3
+
4
+ # Try importing LangChain (It's optional, so we handle the error if missing)
5
+ try:
6
+ from langchain.tools import BaseTool
7
+ except ImportError:
8
+ # Fallback for users who don't have LangChain installed
9
+ class BaseTool:
10
+ pass
11
+
12
+ from vouch import Auditor
13
+
14
+ class VouchSignerInput(BaseModel):
15
+ """Input for the Vouch Signer."""
16
+ action_summary: str = Field(..., description="A brief summary of what action the agent is taking (e.g., 'booking_flight')")
17
+
18
+ class VouchSignerTool(BaseTool):
19
+ name = "vouch_identity_signer"
20
+ description = "Use this tool to generate a cryptographic Vouch-Token when you need to prove your identity to an external API."
21
+ args_schema: Type[BaseModel] = VouchSignerInput
22
+
23
+ # Private attributes
24
+ _auditor: Auditor = None
25
+ _did: str = None
26
+
27
+ def __init__(self, private_key_json: str, agent_did: str):
28
+ super().__init__()
29
+ self._auditor = Auditor(private_key_json)
30
+ self._did = agent_did
31
+
32
+ def _run(self, action_summary: str) -> str:
33
+ """Generates the Vouch-Token header."""
34
+
35
+ # Create the payload based on the agent's intent
36
+ vouch_data = {
37
+ "did": self._did,
38
+ "integrity_hash": f"action:{action_summary}" # Simple binding of intent
39
+ }
40
+
41
+ # Issue the token
42
+ result = self._auditor.issue_vouch(vouch_data)
43
+
44
+ # Return the ready-to-use header value
45
+ return result['certificate']
46
+
47
+ async def _arun(self, action_summary: str) -> str:
48
+ """Async support (optional but good practice)."""
49
+ return self._run(action_summary)
File without changes
@@ -0,0 +1,71 @@
1
+ import sys
2
+ import json
3
+ import logging
4
+ from vouch import Auditor
5
+
6
+ # A simple Standard-IO Model Context Protocol (MCP) Server
7
+ # This allows Claude Desktop or Cursor to "use" Vouch tools locally.
8
+
9
+ logging.basicConfig(level=logging.INFO, stream=sys.stderr)
10
+
11
+ class VouchMCPServer:
12
+ def __init__(self):
13
+ self.auditor = None
14
+ # Try to load keys from env
15
+ self.load_keys()
16
+
17
+ def load_keys(self):
18
+ # In a real app, manage this securely. For MVP, we look for env vars.
19
+ import os
20
+ key = os.getenv("VOUCH_PRIVATE_KEY")
21
+ if key:
22
+ self.auditor = Auditor(key)
23
+
24
+ def process_request(self, line):
25
+ try:
26
+ request = json.loads(line)
27
+ if request.get("method") == "tools/list":
28
+ return {
29
+ "jsonrpc": "2.0",
30
+ "id": request["id"],
31
+ "result": {
32
+ "tools": [{
33
+ "name": "sign_action",
34
+ "description": "Generate a cryptographic Vouch-Token to sign a sensitive action.",
35
+ "inputSchema": {
36
+ "type": "object",
37
+ "properties": {
38
+ "intent": {"type": "string", "description": "What are you doing? (e.g. 'read_email')"},
39
+ "did": {"type": "string", "description": "Your Agent DID"}
40
+ },
41
+ "required": ["intent", "did"]
42
+ }
43
+ }]
44
+ }
45
+ }
46
+ elif request.get("method") == "tools/call":
47
+ if request["params"]["name"] == "sign_action":
48
+ if not self.auditor:
49
+ return {"jsonrpc": "2.0", "id": request["id"], "error": {"code": -32603, "message": "VOUCH_PRIVATE_KEY not set in env"}}
50
+
51
+ args = request["params"]["arguments"]
52
+ proof = self.auditor.issue_vouch({
53
+ "did": args["did"],
54
+ "integrity_hash": args["intent"]
55
+ })
56
+ return {
57
+ "jsonrpc": "2.0", "id": request["id"],
58
+ "result": {"content": [{"type": "text", "text": proof["certificate"]}]}
59
+ }
60
+ except Exception as e:
61
+ pass
62
+ return None
63
+
64
+ if __name__ == "__main__":
65
+ server = VouchMCPServer()
66
+ # MCP loop
67
+ for line in sys.stdin:
68
+ response = server.process_request(line)
69
+ if response:
70
+ print(json.dumps(response))
71
+ sys.stdout.flush()
File without changes
@@ -0,0 +1,9 @@
1
+ from vouch import Auditor
2
+ import os
3
+ def sign_request_with_vouch(intent: str) -> str:
4
+ key = os.environ.get("VOUCH_PRIVATE_KEY")
5
+ did = os.environ.get("VOUCH_DID")
6
+ if not key: return "Error: Keys missing"
7
+ auditor = Auditor(key)
8
+ proof = auditor.issue_vouch({"did": did, "integrity_hash": intent})
9
+ return f"Vouch-Token: {proof['certificate']}"
vouch/keys.py ADDED
@@ -0,0 +1,24 @@
1
+ import json
2
+ from jwcrypto import jwk
3
+
4
+ def generate_identity():
5
+ """
6
+ Generates a fresh Ed25519 Keypair for a new Agent.
7
+ """
8
+ # 1. Generate Key
9
+ key = jwk.JWK.generate(kty='OKP', crv='Ed25519')
10
+
11
+ # 2. Export Private Key (Save this securely!)
12
+ private_key = key.export_private()
13
+
14
+ # 3. Export Public Key (Put this in vouch.json)
15
+ public_key = key.export_public()
16
+
17
+ print("🔑 NEW AGENT IDENTITY GENERATED\n")
18
+ print("--- PRIVATE KEY (Keep Secret / Set as Env Var) ---")
19
+ print(private_key)
20
+ print("\n--- PUBLIC KEY (Put this in vouch.json) ---")
21
+ print(public_key)
22
+
23
+ if __name__ == "__main__":
24
+ generate_identity()
vouch/seal.py ADDED
@@ -0,0 +1,7 @@
1
+ import streamlit as st
2
+
3
+ def vouch_seal_component(is_verified=False, agent_name="Agent"):
4
+ if is_verified:
5
+ st.success(f"✅ {agent_name}: VOUCHED")
6
+ else:
7
+ st.error(f"⚠️ {agent_name}: UNVERIFIED")
vouch/verifier.py ADDED
@@ -0,0 +1,57 @@
1
+ import json
2
+ import time
3
+ import base64
4
+ import requests
5
+ from jwcrypto import jwk, jws
6
+
7
+ class Verifier:
8
+ def __init__(self, trusted_roots=None):
9
+ self.trusted_roots = trusted_roots or {}
10
+ self.used_nonces = set()
11
+
12
+ def _resolve_did(self, did):
13
+ if did in self.trusted_roots:
14
+ return jwk.JWK.from_json(self.trusted_roots[did])
15
+
16
+ if not did.startswith("did:web:"):
17
+ raise ValueError(f"Unsupported DID method: {did}")
18
+
19
+ domain = did.replace("did:web:", "")
20
+ url = f"https://{domain}/.well-known/did.json"
21
+
22
+ try:
23
+ response = requests.get(url, timeout=5)
24
+ response.raise_for_status()
25
+ did_doc = response.json()
26
+ key_data = did_doc['verificationMethod'][0]['publicKeyJwk']
27
+ return jwk.JWK.from_json(json.dumps(key_data))
28
+ except Exception as e:
29
+ return None
30
+
31
+ def check_vouch(self, token):
32
+ try:
33
+ parts = token.split('.')
34
+ if len(parts) != 3: return False, "Invalid Token Format"
35
+
36
+ payload_str = parts[1] + '=' * (-len(parts[1]) % 4)
37
+ payload = json.loads(base64.urlsafe_b64decode(payload_str))
38
+
39
+ agent_did = payload.get('sub')
40
+ if not agent_did: return False, "No Identity (sub) in token"
41
+
42
+ public_key = self._resolve_did(agent_did)
43
+ if not public_key:
44
+ return False, f"Could not resolve public key for {agent_did}"
45
+
46
+ verifier = jws.JWS()
47
+ verifier.deserialize(token)
48
+ verifier.verify(public_key)
49
+
50
+ if time.time() > payload['exp']: return False, "Expired Vouch"
51
+ if payload['jti'] in self.used_nonces: return False, "Replay Detected"
52
+ self.used_nonces.add(payload['jti'])
53
+
54
+ return True, payload
55
+
56
+ except Exception as e:
57
+ return False, f"Verification Error: {str(e)}"