treeship-sdk 0.1.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,40 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+
8
+ # testing
9
+ /coverage
10
+
11
+ # next.js
12
+ /.next/
13
+ /out/
14
+
15
+ # production
16
+ /build
17
+ /dist
18
+
19
+ # misc
20
+ .DS_Store
21
+ *.pem
22
+ .env
23
+ .env.local
24
+ .env.development.local
25
+ .env.test.local
26
+ .env.production.local
27
+
28
+ # debug
29
+ npm-debug.log*
30
+ yarn-debug.log*
31
+ yarn-error.log*
32
+
33
+ # IDE
34
+ .idea/
35
+ .vscode/
36
+ *.swp
37
+ *.swo
38
+
39
+ # vercel
40
+ .vercel
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: treeship-sdk
3
+ Version: 0.1.0
4
+ Summary: Cryptographic attestations for AI agents
5
+ Project-URL: Homepage, https://treeship.dev
6
+ Project-URL: Documentation, https://docs.treeship.dev
7
+ Project-URL: Repository, https://github.com/treeship-dev/treeship
8
+ Author-email: Treeship <hello@treeship.dev>
9
+ License: MIT
10
+ Keywords: agents,ai,attestation,cryptography,treeship,verification
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.9
21
+ Requires-Dist: httpx>=0.27.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # treeship-sdk
25
+
26
+ Cryptographic attestations for AI agents.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install treeship-sdk
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ from treeship import Treeship
38
+
39
+ # Initialize (uses TREESHIP_API_KEY and TREESHIP_AGENT env vars)
40
+ ts = Treeship()
41
+
42
+ # Create an attestation
43
+ result = ts.attest(
44
+ action="Approved loan application #12345",
45
+ inputs_hash=ts.hash({"customer_id": "cust_123", "amount": 50000})
46
+ )
47
+
48
+ print(f"Attestation: {result.attestation_id}")
49
+ print(f"Verify at: {result.url}")
50
+ ```
51
+
52
+ ## Environment Variables
53
+
54
+ | Variable | Description |
55
+ |----------|-------------|
56
+ | `TREESHIP_API_KEY` | Your API key (required for attest) |
57
+ | `TREESHIP_AGENT` | Default agent slug |
58
+ | `TREESHIP_API_URL` | API URL (default: https://api.treeship.dev) |
59
+
60
+ ## Async Support
61
+
62
+ ```python
63
+ from treeship import AsyncTreeship
64
+
65
+ async with AsyncTreeship() as ts:
66
+ result = await ts.attest(
67
+ action="Processed request",
68
+ inputs_hash=ts.hash(request_data)
69
+ )
70
+ ```
71
+
72
+ ## Verification
73
+
74
+ ```python
75
+ # Verify an attestation
76
+ result = ts.verify("attestation-id-here")
77
+ print(f"Valid: {result['valid']}")
78
+ ```
79
+
80
+ ## Documentation
81
+
82
+ - [Full Documentation](https://docs.treeship.dev)
83
+ - [API Reference](https://docs.treeship.dev/api-reference)
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,64 @@
1
+ # treeship-sdk
2
+
3
+ Cryptographic attestations for AI agents.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install treeship-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from treeship import Treeship
15
+
16
+ # Initialize (uses TREESHIP_API_KEY and TREESHIP_AGENT env vars)
17
+ ts = Treeship()
18
+
19
+ # Create an attestation
20
+ result = ts.attest(
21
+ action="Approved loan application #12345",
22
+ inputs_hash=ts.hash({"customer_id": "cust_123", "amount": 50000})
23
+ )
24
+
25
+ print(f"Attestation: {result.attestation_id}")
26
+ print(f"Verify at: {result.url}")
27
+ ```
28
+
29
+ ## Environment Variables
30
+
31
+ | Variable | Description |
32
+ |----------|-------------|
33
+ | `TREESHIP_API_KEY` | Your API key (required for attest) |
34
+ | `TREESHIP_AGENT` | Default agent slug |
35
+ | `TREESHIP_API_URL` | API URL (default: https://api.treeship.dev) |
36
+
37
+ ## Async Support
38
+
39
+ ```python
40
+ from treeship import AsyncTreeship
41
+
42
+ async with AsyncTreeship() as ts:
43
+ result = await ts.attest(
44
+ action="Processed request",
45
+ inputs_hash=ts.hash(request_data)
46
+ )
47
+ ```
48
+
49
+ ## Verification
50
+
51
+ ```python
52
+ # Verify an attestation
53
+ result = ts.verify("attestation-id-here")
54
+ print(f"Valid: {result['valid']}")
55
+ ```
56
+
57
+ ## Documentation
58
+
59
+ - [Full Documentation](https://docs.treeship.dev)
60
+ - [API Reference](https://docs.treeship.dev/api-reference)
61
+
62
+ ## License
63
+
64
+ MIT
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "treeship-sdk"
7
+ version = "0.1.0"
8
+ description = "Cryptographic attestations for AI agents"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ dependencies = ["httpx>=0.27.0"]
13
+ keywords = ["treeship", "attestation", "ai", "agents", "cryptography", "verification"]
14
+ authors = [{ name = "Treeship", email = "hello@treeship.dev" }]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://treeship.dev"
29
+ Documentation = "https://docs.treeship.dev"
30
+ Repository = "https://github.com/treeship-dev/treeship"
31
+
32
+ [tool.hatch.build.targets.wheel]
33
+ packages = ["treeship_sdk"]
@@ -0,0 +1,5 @@
1
+ from .client import Treeship, AttestResult
2
+ from .async_client import AsyncTreeship
3
+
4
+ __version__ = "0.1.0"
5
+ __all__ = ["Treeship", "AsyncTreeship", "AttestResult"]
@@ -0,0 +1,162 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ import os
6
+ from typing import Any, Optional, Dict
7
+
8
+ import httpx
9
+
10
+ from .client import AttestResult, _get_api_url
11
+
12
+
13
+ class AsyncTreeship:
14
+ """
15
+ Async Treeship client for creating cryptographic attestations.
16
+
17
+ Example:
18
+ >>> from treeship import AsyncTreeship
19
+ >>> async with AsyncTreeship() as ts:
20
+ ... result = await ts.attest(
21
+ ... action="Approved loan application #12345",
22
+ ... inputs_hash=ts.hash({"customer_id": "cust_123"})
23
+ ... )
24
+ ... print(result.url)
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ api_key: Optional[str] = None,
30
+ agent: Optional[str] = None,
31
+ api_url: Optional[str] = None,
32
+ ):
33
+ """
34
+ Initialize the async Treeship client.
35
+
36
+ Args:
37
+ api_key: API key for authentication. Defaults to TREESHIP_API_KEY env var.
38
+ agent: Default agent slug. Defaults to TREESHIP_AGENT env var.
39
+ api_url: API base URL. Defaults to TREESHIP_API_URL env var or https://api.treeship.dev.
40
+ """
41
+ self.api_key = api_key or os.environ.get("TREESHIP_API_KEY", "")
42
+ self.agent = agent or os.getenv("TREESHIP_AGENT", "")
43
+ self._api_url = api_url or _get_api_url()
44
+ self._client = httpx.AsyncClient(
45
+ base_url=self._api_url,
46
+ headers={
47
+ "Authorization": f"Bearer {self.api_key}",
48
+ "User-Agent": "treeship-sdk/0.1.0",
49
+ },
50
+ timeout=30.0,
51
+ )
52
+
53
+ @staticmethod
54
+ def hash(data: Any) -> str:
55
+ """
56
+ Create a SHA256 hash of data for use as inputs_hash.
57
+
58
+ Args:
59
+ data: Any JSON-serializable data, string, or bytes.
60
+
61
+ Returns:
62
+ Hex-encoded SHA256 hash.
63
+ """
64
+ if isinstance(data, (dict, list)):
65
+ data = json.dumps(data, sort_keys=True, separators=(",", ":"))
66
+ if isinstance(data, str):
67
+ data = data.encode()
68
+ return hashlib.sha256(data).hexdigest()
69
+
70
+ async def attest(
71
+ self,
72
+ action: str,
73
+ inputs_hash: Optional[str] = None,
74
+ agent: Optional[str] = None,
75
+ metadata: Optional[Dict[str, Any]] = None,
76
+ ) -> AttestResult:
77
+ """
78
+ Create a cryptographic attestation.
79
+
80
+ Args:
81
+ action: Human-readable description of the action.
82
+ inputs_hash: SHA256 hash of inputs. If not provided, hashes the action string.
83
+ agent: Agent slug. Uses default agent if not provided.
84
+ metadata: Optional key-value metadata.
85
+
86
+ Returns:
87
+ AttestResult with attestation details and verification URL.
88
+ """
89
+ slug = agent or self.agent
90
+ if not slug:
91
+ raise ValueError(
92
+ "Agent slug required: pass agent= or set TREESHIP_AGENT environment variable"
93
+ )
94
+
95
+ if not self.api_key:
96
+ raise ValueError(
97
+ "API key required: pass api_key= or set TREESHIP_API_KEY environment variable"
98
+ )
99
+
100
+ response = await self._client.post(
101
+ "/v1/attest",
102
+ json={
103
+ "agent_slug": slug,
104
+ "action": action,
105
+ "inputs_hash": inputs_hash or self.hash(action),
106
+ "metadata": metadata or {},
107
+ },
108
+ )
109
+ response.raise_for_status()
110
+ return AttestResult.from_dict(response.json())
111
+
112
+ async def verify(self, attestation_id: str) -> Dict[str, Any]:
113
+ """
114
+ Verify an attestation by ID.
115
+
116
+ Args:
117
+ attestation_id: The attestation UUID.
118
+
119
+ Returns:
120
+ Dict with verification result including 'valid' boolean.
121
+ """
122
+ response = await self._client.get(f"/v1/verify/{attestation_id}")
123
+ response.raise_for_status()
124
+ return response.json()
125
+
126
+ async def get_agent(self, slug: Optional[str] = None) -> Dict[str, Any]:
127
+ """
128
+ Get agent feed with recent attestations.
129
+
130
+ Args:
131
+ slug: Agent slug. Uses default agent if not provided.
132
+
133
+ Returns:
134
+ Dict with agent info and attestations list.
135
+ """
136
+ agent_slug = slug or self.agent
137
+ if not agent_slug:
138
+ raise ValueError("Agent slug required")
139
+ response = await self._client.get(f"/v1/agent/{agent_slug}")
140
+ response.raise_for_status()
141
+ return response.json()
142
+
143
+ async def get_pubkey(self) -> Dict[str, Any]:
144
+ """
145
+ Get the public signing key for independent verification.
146
+
147
+ Returns:
148
+ Dict with key_id, algorithm, and public_key_pem.
149
+ """
150
+ response = await self._client.get("/v1/pubkey")
151
+ response.raise_for_status()
152
+ return response.json()
153
+
154
+ async def close(self) -> None:
155
+ """Close the HTTP client."""
156
+ await self._client.aclose()
157
+
158
+ async def __aenter__(self) -> AsyncTreeship:
159
+ return self
160
+
161
+ async def __aexit__(self, *args: Any) -> None:
162
+ await self.close()
@@ -0,0 +1,203 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ import os
6
+ from dataclasses import dataclass
7
+ from typing import Any, Optional, Dict
8
+
9
+ import httpx
10
+
11
+
12
+ def _get_api_url() -> str:
13
+ return os.getenv("TREESHIP_API_URL", "https://api.treeship.dev")
14
+
15
+
16
+ @dataclass
17
+ class AttestResult:
18
+ """Result of creating an attestation."""
19
+
20
+ attestation_id: str
21
+ signature: str
22
+ payload_hash: str
23
+ key_id: str
24
+ timestamp: str
25
+ url: str
26
+ verify_command: str
27
+ agent_slug: str
28
+ action: str
29
+ inputs_hash: str
30
+
31
+ @classmethod
32
+ def from_dict(cls, d: Dict[str, Any]) -> AttestResult:
33
+ return cls(
34
+ attestation_id=d["attestation_id"],
35
+ signature=d["signature"],
36
+ payload_hash=d["payload_hash"],
37
+ key_id=d["key_id"],
38
+ timestamp=d["timestamp"],
39
+ url=d["public_url"],
40
+ verify_command=d["verify_command"],
41
+ agent_slug=d["agent_slug"],
42
+ action=d["action"],
43
+ inputs_hash=d["inputs_hash"],
44
+ )
45
+
46
+
47
+ class Treeship:
48
+ """
49
+ Treeship client for creating cryptographic attestations.
50
+
51
+ Example:
52
+ >>> from treeship import Treeship
53
+ >>> ts = Treeship()
54
+ >>> result = ts.attest(
55
+ ... action="Approved loan application #12345",
56
+ ... inputs_hash=ts.hash({"customer_id": "cust_123"})
57
+ ... )
58
+ >>> print(result.url)
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ api_key: Optional[str] = None,
64
+ agent: Optional[str] = None,
65
+ api_url: Optional[str] = None,
66
+ ):
67
+ """
68
+ Initialize the Treeship client.
69
+
70
+ Args:
71
+ api_key: API key for authentication. Defaults to TREESHIP_API_KEY env var.
72
+ agent: Default agent slug. Defaults to TREESHIP_AGENT env var.
73
+ api_url: API base URL. Defaults to TREESHIP_API_URL env var or https://api.treeship.dev.
74
+ """
75
+ self.api_key = api_key or os.environ.get("TREESHIP_API_KEY", "")
76
+ self.agent = agent or os.getenv("TREESHIP_AGENT", "")
77
+ self._api_url = api_url or _get_api_url()
78
+ self._client = httpx.Client(
79
+ base_url=self._api_url,
80
+ headers={
81
+ "Authorization": f"Bearer {self.api_key}",
82
+ "User-Agent": "treeship-sdk/0.1.0",
83
+ },
84
+ timeout=30.0,
85
+ )
86
+
87
+ @staticmethod
88
+ def hash(data: Any) -> str:
89
+ """
90
+ Create a SHA256 hash of data for use as inputs_hash.
91
+
92
+ Args:
93
+ data: Any JSON-serializable data, string, or bytes.
94
+
95
+ Returns:
96
+ Hex-encoded SHA256 hash.
97
+ """
98
+ if isinstance(data, (dict, list)):
99
+ data = json.dumps(data, sort_keys=True, separators=(",", ":"))
100
+ if isinstance(data, str):
101
+ data = data.encode()
102
+ return hashlib.sha256(data).hexdigest()
103
+
104
+ def attest(
105
+ self,
106
+ action: str,
107
+ inputs_hash: Optional[str] = None,
108
+ agent: Optional[str] = None,
109
+ metadata: Optional[Dict[str, Any]] = None,
110
+ ) -> AttestResult:
111
+ """
112
+ Create a cryptographic attestation.
113
+
114
+ Args:
115
+ action: Human-readable description of the action.
116
+ inputs_hash: SHA256 hash of inputs. If not provided, hashes the action string.
117
+ agent: Agent slug. Uses default agent if not provided.
118
+ metadata: Optional key-value metadata.
119
+
120
+ Returns:
121
+ AttestResult with attestation details and verification URL.
122
+
123
+ Raises:
124
+ ValueError: If no agent slug is available.
125
+ httpx.HTTPStatusError: If the API request fails.
126
+ """
127
+ slug = agent or self.agent
128
+ if not slug:
129
+ raise ValueError(
130
+ "Agent slug required: pass agent= or set TREESHIP_AGENT environment variable"
131
+ )
132
+
133
+ if not self.api_key:
134
+ raise ValueError(
135
+ "API key required: pass api_key= or set TREESHIP_API_KEY environment variable"
136
+ )
137
+
138
+ response = self._client.post(
139
+ "/v1/attest",
140
+ json={
141
+ "agent_slug": slug,
142
+ "action": action,
143
+ "inputs_hash": inputs_hash or self.hash(action),
144
+ "metadata": metadata or {},
145
+ },
146
+ )
147
+ response.raise_for_status()
148
+ return AttestResult.from_dict(response.json())
149
+
150
+ def verify(self, attestation_id: str) -> Dict[str, Any]:
151
+ """
152
+ Verify an attestation by ID.
153
+
154
+ Args:
155
+ attestation_id: The attestation UUID.
156
+
157
+ Returns:
158
+ Dict with verification result including 'valid' boolean.
159
+
160
+ Raises:
161
+ httpx.HTTPStatusError: If the attestation is not found or request fails.
162
+ """
163
+ response = self._client.get(f"/v1/verify/{attestation_id}")
164
+ response.raise_for_status()
165
+ return response.json()
166
+
167
+ def get_agent(self, slug: Optional[str] = None) -> Dict[str, Any]:
168
+ """
169
+ Get agent feed with recent attestations.
170
+
171
+ Args:
172
+ slug: Agent slug. Uses default agent if not provided.
173
+
174
+ Returns:
175
+ Dict with agent info and attestations list.
176
+ """
177
+ agent_slug = slug or self.agent
178
+ if not agent_slug:
179
+ raise ValueError("Agent slug required")
180
+ response = self._client.get(f"/v1/agent/{agent_slug}")
181
+ response.raise_for_status()
182
+ return response.json()
183
+
184
+ def get_pubkey(self) -> Dict[str, Any]:
185
+ """
186
+ Get the public signing key for independent verification.
187
+
188
+ Returns:
189
+ Dict with key_id, algorithm, and public_key_pem.
190
+ """
191
+ response = self._client.get("/v1/pubkey")
192
+ response.raise_for_status()
193
+ return response.json()
194
+
195
+ def close(self) -> None:
196
+ """Close the HTTP client."""
197
+ self._client.close()
198
+
199
+ def __enter__(self) -> Treeship:
200
+ return self
201
+
202
+ def __exit__(self, *args: Any) -> None:
203
+ self.close()
File without changes