truss-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,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: truss-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Truss trust infrastructure API
5
+ License: Apache-2.0
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: pynacl>=1.6
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest>=7; extra == "dev"
10
+ Requires-Dist: pytest-mock>=3; extra == "dev"
@@ -0,0 +1,135 @@
1
+ ![Banner](banner.png)
2
+
3
+ # Truss SDK — Python
4
+
5
+ **Python SDK for the Truss trust infrastructure API — create mandates, record actions, manage agents, and verify evidence.**
6
+
7
+ [![PyPI version](https://img.shields.io/pypi/v/truss-sdk)](https://pypi.org/project/truss-sdk/)
8
+ [![Python](https://img.shields.io/pypi/pyversions/truss-sdk)](https://pypi.org/project/truss-sdk/)
9
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
10
+ [![CI](https://img.shields.io/github/actions/workflow/status/tensflare/truss-sdk-python/ci.yml)](https://github.com/tensflare/truss-sdk-python/actions)
11
+
12
+ ---
13
+
14
+ ## What is Truss?
15
+
16
+ Truss is an **accountability layer for AI agents** — it records every agent action as a cryptographically signed, tamper-evident audit trail. [Learn more →](https://truss.tensflare.com/docs)
17
+
18
+ ## Features
19
+
20
+ - **Ed25519 cryptography** — Key generation, payload signing, and signature verification via `PyNaCl`
21
+ - **`TrussClient`** — Full-featured HTTP client for the Truss API
22
+ - **`ActionContext`** — Builder pattern for recording actions with chain-of-custody linkage
23
+ - **Dataclass models** — Mirroring Truss TAP schemas with full type annotations
24
+ - **Python ≥3.9**
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ pip install truss-sdk
30
+ ```
31
+
32
+ ## Quick start
33
+
34
+ ```python
35
+ from truss_sdk import TrussClient, generate_keypair, sign_payload, verify_signature
36
+
37
+ # 1. Generate an Ed25519 keypair
38
+ kp = generate_keypair()
39
+ print(f"Public key: {kp.public_key}")
40
+ print(f"Private key: {kp.private_key}") # keep secret!
41
+
42
+ # 2. Sign and verify
43
+ sig = sign_payload({"action": "read", "value": 42}, kp.private_key)
44
+ assert verify_signature({"action": "read", "value": 42}, sig, kp.public_key)
45
+ print("Signature valid: True")
46
+
47
+ # 3. Create an API client
48
+ client = TrussClient(api_key="tr_your_api_key")
49
+
50
+ # 4. Register an agent
51
+ agent = client.create_agent(
52
+ name="My Agent",
53
+ public_key=kp.public_key,
54
+ description="My autonomous agent",
55
+ )
56
+
57
+ # 5. Create a mandate
58
+ mandate = client.create_mandate(
59
+ mandate_id="mnd_001",
60
+ agent_id=agent.id,
61
+ agent_name=agent.name,
62
+ issuing_principal={
63
+ "entity": "org_1",
64
+ "human_id": "usr_1",
65
+ "role": "Admin",
66
+ },
67
+ scope={"permitted_actions": ["read", "write"]},
68
+ jurisdiction_context={
69
+ "deploying_org_jurisdiction": "US",
70
+ "operating_jurisdictions": ["US"],
71
+ },
72
+ validity={
73
+ "issued_at": "2026-06-06T00:00:00Z",
74
+ "expires_at": "2026-12-31T23:59:59Z",
75
+ },
76
+ private_key=kp.private_key,
77
+ )
78
+
79
+ # 6. Record an action using the builder pattern
80
+ ctx = client.action("read", mandate.id, kp.private_key)
81
+ ctx.record_input({"file": "/data/report.pdf"})
82
+ ctx.record_output({"summary": "Report contains 42 records"})
83
+ result = ctx.commit(agent_id=agent.id, chain_position=1, prev_record_hash=None)
84
+ print(f"Action recorded: {result.id}")
85
+ ```
86
+
87
+ ## API reference
88
+
89
+ ### `generate_keypair()`
90
+
91
+ Returns a `Keypair` namedtuple with `.public_key` and `.private_key` fields.
92
+
93
+ ### `sign_payload(payload, private_key)`
94
+
95
+ Signs any JSON-serialisable `dict` and returns a hex-encoded Ed25519 signature string.
96
+
97
+ ### `verify_signature(payload, signature, public_key)`
98
+
99
+ Verifies an Ed25519 signature. Returns `bool`.
100
+
101
+ ### `TrussClient(api_key, api_url=None)`
102
+
103
+ | Parameter | Type | Description |
104
+ |---|---|---|
105
+ | `api_key` | `str` | Truss API key (`tr_` prefix) |
106
+ | `api_url` | `str` | API base URL (default `http://localhost:4000`) |
107
+
108
+ **Client methods:** `create_agent`, `get_agent`, `list_agents`, `create_mandate`, `get_mandate`, `list_mandates`, `record_action`, `get_action`, `list_actions`, `create_delegation`, `generate_evidence`, `verify_evidence`, and more.
109
+
110
+ ### `client.action(action_type, mandate_id, private_key)`
111
+
112
+ Returns an `ActionContext` builder with `.record_input()`, `.record_output()`, `.commit()`.
113
+
114
+ ## Related packages
115
+
116
+ | Package | Description |
117
+ |---|---|
118
+ | [@tensflare/tap](https://github.com/tensflare/truss-tap) | Core Zod schemas for mandates, actions, and delegations |
119
+ | [@tensflare/truss-sdk](https://github.com/tensflare/truss-sdk-js) | TypeScript SDK equivalent |
120
+ | [@tensflare/cli](https://github.com/tensflare/truss-cli) | Command-line interface |
121
+
122
+ ## Development
123
+
124
+ ```bash
125
+ pip install -e ".[dev]"
126
+ pytest
127
+ ```
128
+
129
+ ## Contributing
130
+
131
+ Pull requests are welcome. Please see the [contribution guidelines](https://truss.tensflare.com/docs/contributing).
132
+
133
+ ## License
134
+
135
+ Apache 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "truss-sdk"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the Truss trust infrastructure API"
9
+ requires-python = ">=3.9"
10
+ license = { text = "Apache-2.0" }
11
+ dependencies = [
12
+ "pynacl>=1.6",
13
+ ]
14
+
15
+ [project.optional-dependencies]
16
+ dev = [
17
+ "pytest>=7",
18
+ "pytest-mock>=3",
19
+ ]
20
+
21
+ [tool.setuptools.packages.find]
22
+ include = ["truss_sdk*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,118 @@
1
+ import pytest
2
+ from unittest.mock import patch, MagicMock
3
+ from truss_sdk.client import TrussClient, TrussClientError, ActionContext
4
+
5
+
6
+ class TestTrussClient:
7
+ def test_generate_keypair_returns_keypair(self):
8
+ kp = TrussClient.generate_keypair()
9
+ assert len(kp.public_key) == 64
10
+ assert len(kp.private_key) == 128
11
+
12
+ @patch("truss_sdk.client.urlopen")
13
+ def test_create_mandate(self, mock_urlopen):
14
+ mock_resp = MagicMock()
15
+ mock_resp.read.return_value = b'{"mandate_id": "mnd_1", "status": "active"}'
16
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
17
+
18
+ client = TrussClient(api_key="tr_test_key")
19
+ result = client.create_mandate(
20
+ mandate_id="mnd_1",
21
+ agent_id="agt_1",
22
+ agent_name="Test Agent",
23
+ issuing_principal={"entity": "org_1", "human_id": "usr_1", "role": "Admin"},
24
+ scope={"permitted_actions": ["read"]},
25
+ jurisdiction_context={"deploying_org_jurisdiction": "US", "operating_jurisdictions": ["US"]},
26
+ validity={"issued_at": "2026-01-01T00:00:00Z", "expires_at": "2026-12-31T00:00:00Z"},
27
+ private_key="ab" * 64,
28
+ )
29
+ assert result["mandate_id"] == "mnd_1"
30
+ assert result["status"] == "active"
31
+
32
+ request = mock_urlopen.call_args[0][0]
33
+ assert request.method == "POST"
34
+ assert request.full_url.endswith("/mandates")
35
+ assert "Bearer tr_test_key" in request.headers.get("Authorization", "")
36
+
37
+ @patch("truss_sdk.client.urlopen")
38
+ def test_get_mandate(self, mock_urlopen):
39
+ mock_resp = MagicMock()
40
+ mock_resp.read.return_value = b'{"mandate_id": "mnd_1", "status": "active"}'
41
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
42
+
43
+ client = TrussClient(api_key="tr_test_key")
44
+ result = client.get_mandate("mnd_1")
45
+ assert result["mandate_id"] == "mnd_1"
46
+
47
+ request = mock_urlopen.call_args[0][0]
48
+ assert request.full_url.endswith("/mandates/mnd_1")
49
+
50
+ @patch("truss_sdk.client.urlopen")
51
+ def test_record_action(self, mock_urlopen):
52
+ mock_resp = MagicMock()
53
+ mock_resp.read.return_value = b'{"record_id": "act_1", "chain_position": 1}'
54
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
55
+
56
+ client = TrussClient(api_key="tr_test_key")
57
+ result = client.record_action(
58
+ record_id="act_1",
59
+ mandate_id="mnd_1",
60
+ action_type="read",
61
+ timestamp="2026-01-01T00:00:00Z",
62
+ agent_id="agt_1",
63
+ input_hash="sha256:abc",
64
+ output_hash="sha256:def",
65
+ chain_position=1,
66
+ prev_record_hash=None,
67
+ private_key="ab" * 64,
68
+ )
69
+ assert result["record_id"] == "act_1"
70
+
71
+ @patch("truss_sdk.client.urlopen")
72
+ def test_http_error_raises_truss_error(self, mock_urlopen):
73
+ import urllib.error
74
+
75
+ error_resp = MagicMock()
76
+ error_resp.read.return_value = b'{"error": "Invalid API key"}'
77
+ error_resp.code = 401
78
+ mock_urlopen.side_effect = urllib.error.HTTPError(
79
+ "http://localhost:4000/me", 401, "Unauthorized", {}, error_resp
80
+ )
81
+
82
+ client = TrussClient(api_key="bad_key")
83
+ with pytest.raises(TrussClientError, match="401"):
84
+ client.get_mandate("mnd_1")
85
+
86
+
87
+ class TestActionContext:
88
+ @patch("truss_sdk.client.urlopen")
89
+ def test_commit_requires_input_and_output(self, mock_urlopen):
90
+ client = TrussClient(api_key="tr_test_key")
91
+ ctx = client.action("read", "mnd_1", "ab" * 64)
92
+
93
+ with pytest.raises(TrussClientError, match="input_hash"):
94
+ ctx.commit("agt_1", 1, None)
95
+
96
+ ctx.record_input("sha256:abc")
97
+ with pytest.raises(TrussClientError, match="output_hash"):
98
+ ctx.commit("agt_1", 1, None)
99
+
100
+ @patch("truss_sdk.client.urlopen")
101
+ def test_commit_sends_signed_action(self, mock_urlopen):
102
+ mock_resp = MagicMock()
103
+ mock_resp.read.return_value = b'{"record_id": "act_xyz", "chain_position": 1}'
104
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
105
+
106
+ client = TrussClient(api_key="tr_test_key")
107
+ ctx = client.action("write", "mnd_1", "ab" * 64)
108
+ ctx.record_input("sha256:in")
109
+ ctx.record_output("sha256:out")
110
+
111
+ result = ctx.commit(agent_id="agt_1", chain_position=1, prev_record_hash=None)
112
+ assert result["record_id"] == "act_xyz"
113
+
114
+ request = mock_urlopen.call_args[0][0]
115
+ body = request.data
116
+ assert b"sha256:in" in body
117
+ assert b"sha256:out" in body
118
+ assert b"signature" in body
@@ -0,0 +1,48 @@
1
+ import pytest
2
+ from truss_sdk.crypto import generate_keypair, sign_payload, verify_signature
3
+
4
+
5
+ class TestCrypto:
6
+ def test_generate_keypair_returns_hex_keys(self):
7
+ kp = generate_keypair()
8
+ assert len(kp.public_key) == 64
9
+ assert len(kp.private_key) == 128
10
+ int(kp.public_key, 16)
11
+ int(kp.private_key, 16)
12
+
13
+ def test_sign_payload_and_verify_round_trip(self):
14
+ kp = generate_keypair()
15
+ payload = {"action": "test", "value": 42}
16
+
17
+ sig = sign_payload(payload, kp.private_key)
18
+ assert len(sig) == 128
19
+ int(sig, 16)
20
+
21
+ valid = verify_signature(payload, sig, kp.public_key)
22
+ assert valid is True
23
+
24
+ def test_verify_signature_rejects_wrong_key(self):
25
+ kp1 = generate_keypair()
26
+ kp2 = generate_keypair()
27
+ payload = {"action": "test"}
28
+
29
+ sig = sign_payload(payload, kp1.private_key)
30
+ valid = verify_signature(payload, sig, kp2.public_key)
31
+ assert valid is False
32
+
33
+ def test_verify_signature_rejects_tampered_payload(self):
34
+ kp = generate_keypair()
35
+ payload = {"action": "test"}
36
+
37
+ sig = sign_payload(payload, kp.private_key)
38
+ tampered = {"action": "tampered"}
39
+ valid = verify_signature(tampered, sig, kp.public_key)
40
+ assert valid is False
41
+
42
+ def test_signature_is_deterministic(self):
43
+ kp = generate_keypair()
44
+ payload = {"msg": "hello", "num": 1}
45
+
46
+ sig1 = sign_payload(payload, kp.private_key)
47
+ sig2 = sign_payload(payload, kp.private_key)
48
+ assert sig1 == sig2
@@ -0,0 +1,30 @@
1
+ from .crypto import generate_keypair, sign_payload, verify_signature
2
+ from .client import TrussClient, ActionContext
3
+ from .models import (
4
+ Keypair,
5
+ Mandate,
6
+ ActionRecord,
7
+ IssuingPrincipal,
8
+ Scope,
9
+ JurisdictionContext,
10
+ Validity,
11
+ JurisdictionEvaluation,
12
+ JurisdictionFlag,
13
+ )
14
+
15
+ __all__ = [
16
+ "generate_keypair",
17
+ "sign_payload",
18
+ "verify_signature",
19
+ "TrussClient",
20
+ "ActionContext",
21
+ "Keypair",
22
+ "Mandate",
23
+ "ActionRecord",
24
+ "IssuingPrincipal",
25
+ "Scope",
26
+ "JurisdictionContext",
27
+ "Validity",
28
+ "JurisdictionEvaluation",
29
+ "JurisdictionFlag",
30
+ ]
@@ -0,0 +1,178 @@
1
+ import json
2
+ import uuid
3
+ from datetime import datetime, timezone
4
+ from urllib.request import Request, urlopen
5
+ from urllib.error import HTTPError
6
+ from typing import Optional
7
+ from .crypto import sign_payload, generate_keypair
8
+ from .models import Keypair
9
+
10
+
11
+ class TrussClientError(Exception):
12
+ pass
13
+
14
+
15
+ class TrussClient:
16
+ def __init__(
17
+ self,
18
+ api_key: str,
19
+ base_url: str = "http://localhost:4000",
20
+ agent_id: Optional[str] = None,
21
+ ):
22
+ self.api_key = api_key
23
+ self.base_url = base_url.rstrip("/")
24
+ self.agent_id = agent_id
25
+
26
+ @staticmethod
27
+ def generate_keypair() -> Keypair:
28
+ return generate_keypair()
29
+
30
+ def _request(
31
+ self,
32
+ method: str,
33
+ path: str,
34
+ body: Optional[dict] = None,
35
+ ) -> dict:
36
+ url = f"{self.base_url}{path}"
37
+ headers = {
38
+ "Authorization": f"Bearer {self.api_key}",
39
+ "Content-Type": "application/json",
40
+ }
41
+ data = json.dumps(body, separators=(",", ":"), sort_keys=True).encode("utf-8") if body else None
42
+ req = Request(url, data=data, headers=headers, method=method)
43
+ try:
44
+ with urlopen(req) as resp:
45
+ return json.loads(resp.read().decode("utf-8"))
46
+ except HTTPError as e:
47
+ error_body = e.read().decode("utf-8")
48
+ raise TrussClientError(
49
+ f"HTTP {e.code}: {error_body}"
50
+ ) from e
51
+
52
+ def create_mandate(
53
+ self,
54
+ mandate_id: str,
55
+ agent_id: str,
56
+ agent_name: str,
57
+ issuing_principal: dict,
58
+ scope: dict,
59
+ jurisdiction_context: dict,
60
+ validity: dict,
61
+ private_key: str,
62
+ ) -> dict:
63
+ body = {
64
+ "mandate_id": mandate_id,
65
+ "agent_id": agent_id,
66
+ "agent_name": agent_name,
67
+ "issuing_principal": issuing_principal,
68
+ "scope": scope,
69
+ "jurisdiction_context": jurisdiction_context,
70
+ "validity": validity,
71
+ "version": "1.0",
72
+ "issuer_public_key": "",
73
+ "signature": "",
74
+ }
75
+ sig_payload = {k: v for k, v in body.items() if k != "signature"}
76
+ body["signature"] = sign_payload(sig_payload, private_key)
77
+ return self._request("POST", "/mandates", body)
78
+
79
+ def record_action(
80
+ self,
81
+ record_id: str,
82
+ mandate_id: str,
83
+ action_type: str,
84
+ timestamp: str,
85
+ agent_id: str,
86
+ input_hash: str,
87
+ output_hash: str,
88
+ chain_position: int,
89
+ prev_record_hash: Optional[str],
90
+ private_key: str,
91
+ ) -> dict:
92
+ body = {
93
+ "record_id": record_id,
94
+ "mandate_id": mandate_id,
95
+ "action_type": action_type,
96
+ "timestamp": timestamp,
97
+ "agent_id": agent_id,
98
+ "input_hash": input_hash,
99
+ "output_hash": output_hash,
100
+ "chain_position": chain_position,
101
+ "prev_record_hash": prev_record_hash,
102
+ "within_mandate": True,
103
+ "signature": "",
104
+ }
105
+ sig_payload = {k: v for k, v in body.items() if k != "signature"}
106
+ body["signature"] = sign_payload(sig_payload, private_key)
107
+ return self._request("POST", "/actions", body)
108
+
109
+ def get_mandate(self, mandate_id: str) -> dict:
110
+ return self._request("GET", f"/mandates/{mandate_id}")
111
+
112
+ def get_action(self, record_id: str) -> dict:
113
+ return self._request("GET", f"/actions/{record_id}")
114
+
115
+ def get_mandate_chain(self, mandate_id: str) -> dict:
116
+ return self._request("GET", f"/mandates/{mandate_id}/chain")
117
+
118
+ def action(
119
+ self,
120
+ action_type: str,
121
+ mandate_id: str,
122
+ private_key: str,
123
+ ) -> "ActionContext":
124
+ return ActionContext(
125
+ client=self,
126
+ action_type=action_type,
127
+ mandate_id=mandate_id,
128
+ private_key=private_key,
129
+ )
130
+
131
+
132
+ class ActionContext:
133
+ def __init__(
134
+ self,
135
+ client: TrussClient,
136
+ action_type: str,
137
+ mandate_id: str,
138
+ private_key: str,
139
+ ):
140
+ self._client = client
141
+ self._action_type = action_type
142
+ self._mandate_id = mandate_id
143
+ self._private_key = private_key
144
+ self._input_hash: Optional[str] = None
145
+ self._output_hash: Optional[str] = None
146
+
147
+ def record_input(self, input_hash: str) -> None:
148
+ self._input_hash = input_hash
149
+
150
+ def record_output(self, output_hash: str) -> None:
151
+ self._output_hash = output_hash
152
+
153
+ def commit(
154
+ self,
155
+ agent_id: str,
156
+ chain_position: int,
157
+ prev_record_hash: Optional[str],
158
+ ) -> dict:
159
+ if self._input_hash is None:
160
+ raise TrussClientError("input_hash not set. Call record_input() before commit.")
161
+ if self._output_hash is None:
162
+ raise TrussClientError("output_hash not set. Call record_output() before commit.")
163
+
164
+ record_id = f"act_{uuid.uuid4().hex[:24]}"
165
+ timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
166
+
167
+ return self._client.record_action(
168
+ record_id=record_id,
169
+ mandate_id=self._mandate_id,
170
+ action_type=self._action_type,
171
+ timestamp=timestamp,
172
+ agent_id=agent_id,
173
+ input_hash=self._input_hash,
174
+ output_hash=self._output_hash,
175
+ chain_position=chain_position,
176
+ prev_record_hash=prev_record_hash,
177
+ private_key=self._private_key,
178
+ )
@@ -0,0 +1,47 @@
1
+ import json
2
+ from nacl.signing import SigningKey, VerifyKey
3
+ from nacl.encoding import HexEncoder
4
+ from nacl.exceptions import BadSignatureError
5
+ from .models import Keypair
6
+
7
+
8
+ def generate_keypair() -> Keypair:
9
+ signing_key = SigningKey.generate()
10
+ public_key = signing_key.verify_key.encode(encoder=HexEncoder).decode("ascii")
11
+ seed = signing_key.encode(encoder=HexEncoder).decode("ascii")
12
+ private_key = seed + public_key
13
+ return Keypair(public_key=public_key, private_key=private_key)
14
+
15
+
16
+ def _canonical_json(payload: dict) -> bytes:
17
+ return json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")
18
+
19
+
20
+ def _hex_to_seed(key_hex: str) -> bytes:
21
+ raw = bytes.fromhex(key_hex)
22
+ if len(raw) == 32:
23
+ return raw
24
+ if len(raw) == 64:
25
+ return raw[:32]
26
+ raise ValueError(
27
+ f"Invalid key length: expected 32 or 64 bytes, got {len(raw)}"
28
+ )
29
+
30
+
31
+ def sign_payload(payload: dict, private_key_hex: str) -> str:
32
+ canonical = _canonical_json(payload)
33
+ seed = _hex_to_seed(private_key_hex)
34
+ signing_key = SigningKey(seed)
35
+ signed = signing_key.sign(canonical)
36
+ return signed.signature.hex()
37
+
38
+
39
+ def verify_signature(payload: dict, signature_hex: str, public_key_hex: str) -> bool:
40
+ canonical = _canonical_json(payload)
41
+ signature = bytes.fromhex(signature_hex)
42
+ verify_key = VerifyKey(public_key_hex, encoder=HexEncoder)
43
+ try:
44
+ verify_key.verify(canonical, signature)
45
+ return True
46
+ except BadSignatureError:
47
+ return False
@@ -0,0 +1,85 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Optional
3
+
4
+
5
+ @dataclass
6
+ class Keypair:
7
+ public_key: str
8
+ private_key: str
9
+
10
+
11
+ @dataclass
12
+ class IssuingPrincipal:
13
+ entity: str
14
+ human_id: str
15
+ role: str
16
+
17
+
18
+ @dataclass
19
+ class Scope:
20
+ permitted_actions: list[str]
21
+ forbidden_actions: list[str] = field(default_factory=list)
22
+ permitted_data_classes: list[str] = field(default_factory=list)
23
+ max_delegation_depth: int = 0
24
+ resource_bounds: list[str] = field(default_factory=list)
25
+
26
+
27
+ @dataclass
28
+ class JurisdictionContext:
29
+ deploying_org_jurisdiction: str
30
+ operating_jurisdictions: list[str]
31
+ regulatory_frameworks: list[str] = field(default_factory=list)
32
+
33
+
34
+ @dataclass
35
+ class Validity:
36
+ issued_at: str
37
+ expires_at: str
38
+ single_use: bool = False
39
+
40
+
41
+ @dataclass
42
+ class Mandate:
43
+ mandate_id: str
44
+ version: str
45
+ agent_id: str
46
+ agent_name: str
47
+ issuing_principal: IssuingPrincipal
48
+ scope: Scope
49
+ jurisdiction_context: JurisdictionContext
50
+ validity: Validity
51
+ signature: str
52
+ issuer_public_key: str
53
+
54
+
55
+ @dataclass
56
+ class JurisdictionFlag:
57
+ framework: str
58
+ obligation: str
59
+ severity: str
60
+ article: Optional[str] = None
61
+ guidance: Optional[str] = None
62
+
63
+
64
+ @dataclass
65
+ class JurisdictionEvaluation:
66
+ evaluated_at: str
67
+ frameworks_applied: list[str] = field(default_factory=list)
68
+ status: str = "unknown"
69
+ flags: list[JurisdictionFlag] = field(default_factory=list)
70
+
71
+
72
+ @dataclass
73
+ class ActionRecord:
74
+ record_id: str
75
+ mandate_id: str
76
+ action_type: str
77
+ timestamp: str
78
+ agent_id: str
79
+ input_hash: str
80
+ output_hash: str
81
+ within_mandate: bool = True
82
+ jurisdiction_evaluation: Optional[JurisdictionEvaluation] = None
83
+ chain_position: int = 0
84
+ prev_record_hash: Optional[str] = None
85
+ signature: str = ""
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: truss-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Truss trust infrastructure API
5
+ License: Apache-2.0
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: pynacl>=1.6
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest>=7; extra == "dev"
10
+ Requires-Dist: pytest-mock>=3; extra == "dev"
@@ -0,0 +1,13 @@
1
+ README.md
2
+ pyproject.toml
3
+ tests/test_client.py
4
+ tests/test_crypto.py
5
+ truss_sdk/__init__.py
6
+ truss_sdk/client.py
7
+ truss_sdk/crypto.py
8
+ truss_sdk/models.py
9
+ truss_sdk.egg-info/PKG-INFO
10
+ truss_sdk.egg-info/SOURCES.txt
11
+ truss_sdk.egg-info/dependency_links.txt
12
+ truss_sdk.egg-info/requires.txt
13
+ truss_sdk.egg-info/top_level.txt
@@ -0,0 +1,5 @@
1
+ pynacl>=1.6
2
+
3
+ [dev]
4
+ pytest>=7
5
+ pytest-mock>=3
@@ -0,0 +1 @@
1
+ truss_sdk