bastion-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.
- bastion_sdk-0.1.0/.gitignore +24 -0
- bastion_sdk-0.1.0/PKG-INFO +198 -0
- bastion_sdk-0.1.0/README.md +170 -0
- bastion_sdk-0.1.0/pyproject.toml +48 -0
- bastion_sdk-0.1.0/src/bastion_sdk/__init__.py +25 -0
- bastion_sdk-0.1.0/src/bastion_sdk/client.py +547 -0
- bastion_sdk-0.1.0/src/bastion_sdk/errors.py +81 -0
- bastion_sdk-0.1.0/src/bastion_sdk/types.py +119 -0
- bastion_sdk-0.1.0/tests/__init__.py +0 -0
- bastion_sdk-0.1.0/tests/test_async_client.py +185 -0
- bastion_sdk-0.1.0/tests/test_client.py +292 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
dist/
|
|
3
|
+
.env
|
|
4
|
+
.env.*
|
|
5
|
+
!.env.example
|
|
6
|
+
*.tsbuildinfo
|
|
7
|
+
|
|
8
|
+
# Python
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
.venv/
|
|
12
|
+
*.pyc
|
|
13
|
+
|
|
14
|
+
# IDE
|
|
15
|
+
.vscode/
|
|
16
|
+
.idea/
|
|
17
|
+
*.swp
|
|
18
|
+
|
|
19
|
+
# OS
|
|
20
|
+
.DS_Store
|
|
21
|
+
Thumbs.db
|
|
22
|
+
|
|
23
|
+
# Prisma
|
|
24
|
+
packages/api/prisma/*.db
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bastion-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the Bastion trust proxy
|
|
5
|
+
Project-URL: Homepage, https://github.com/Matthieuhakim/Bastion
|
|
6
|
+
Project-URL: Repository, https://github.com/Matthieuhakim/Bastion
|
|
7
|
+
Project-URL: Issues, https://github.com/Matthieuhakim/Bastion/issues
|
|
8
|
+
Author: Matthieu Hakim
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: agents,ai,audit,proxy,python,security
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Security
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: respx>=0.22.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# bastion-sdk
|
|
30
|
+
|
|
31
|
+
Python SDK for the [Bastion](../../README.md) trust proxy. Supports both sync and async usage.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install bastion-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires Python 3.10+.
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from bastion_sdk import BastionClient
|
|
45
|
+
|
|
46
|
+
with BastionClient("http://localhost:3000", api_key="your-project-api-key") as client:
|
|
47
|
+
print(client.health())
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Async
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from bastion_sdk import AsyncBastionClient
|
|
54
|
+
|
|
55
|
+
async with AsyncBastionClient("http://localhost:3000", api_key="your-key") as client:
|
|
56
|
+
print(await client.health())
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Admin Operations
|
|
60
|
+
|
|
61
|
+
Use the admin API key (`PROJECT_API_KEY`) to manage agents, credentials, and policies.
|
|
62
|
+
|
|
63
|
+
### Agents
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# Create an agent (returns one-time agentSecret)
|
|
67
|
+
agent = client.create_agent("Support Bot", description="Handles refund requests")
|
|
68
|
+
agent_secret = agent["agentSecret"] # save this — shown only once
|
|
69
|
+
|
|
70
|
+
# List, get, update, delete
|
|
71
|
+
agents = client.list_agents()
|
|
72
|
+
agent = client.get_agent(agent["id"])
|
|
73
|
+
client.update_agent(agent["id"], isActive=False)
|
|
74
|
+
client.delete_agent(agent["id"]) # soft-delete
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Credentials
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# Store a credential (encrypted at rest, raw value never returned)
|
|
81
|
+
credential = client.create_credential(
|
|
82
|
+
name="Stripe Production",
|
|
83
|
+
type="API_KEY",
|
|
84
|
+
value="sk_live_...",
|
|
85
|
+
agent_id=agent["id"],
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
credentials = client.list_credentials(agent_id=agent["id"])
|
|
89
|
+
client.revoke_credential(credential["id"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Policies
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
policy = client.create_policy(
|
|
96
|
+
agent_id=agent["id"],
|
|
97
|
+
credential_id=credential["id"],
|
|
98
|
+
allowed_actions=["charges.*"],
|
|
99
|
+
denied_actions=["transfers.*"],
|
|
100
|
+
constraints={
|
|
101
|
+
"maxAmountPerTransaction": 1000,
|
|
102
|
+
"maxDailySpend": 5000,
|
|
103
|
+
"rateLimit": {"maxRequests": 100, "windowSeconds": 3600},
|
|
104
|
+
},
|
|
105
|
+
requires_approval_above=500,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Dry-run evaluation (no side effects)
|
|
109
|
+
result = client.evaluate_policy(
|
|
110
|
+
agent_id=agent["id"],
|
|
111
|
+
credential_id=credential["id"],
|
|
112
|
+
action="charges.create",
|
|
113
|
+
params={"amount": 750},
|
|
114
|
+
)
|
|
115
|
+
# result["decision"] → "ESCALATE"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Agent Operations
|
|
119
|
+
|
|
120
|
+
Use an agent secret (`bst_...`) for proxy execution.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
agent_client = BastionClient("http://localhost:3000", api_key=agent_secret)
|
|
124
|
+
|
|
125
|
+
result = agent_client.execute(
|
|
126
|
+
credential_id=credential["id"],
|
|
127
|
+
action="charges.create",
|
|
128
|
+
target={
|
|
129
|
+
"url": "https://api.stripe.com/v1/charges",
|
|
130
|
+
"method": "POST",
|
|
131
|
+
"body": {"amount": 5000, "currency": "usd"},
|
|
132
|
+
},
|
|
133
|
+
params={"amount": 50},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# result["upstream"]["status"] → 200
|
|
137
|
+
# result["upstream"]["body"] → Stripe's response
|
|
138
|
+
# result["meta"]["policyDecision"] → "ALLOW"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Custom Credential Injection
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
agent_client.execute(
|
|
145
|
+
credential_id=credential["id"],
|
|
146
|
+
action="test",
|
|
147
|
+
target={"url": "https://api.example.com"},
|
|
148
|
+
injection={"location": "header", "key": "X-Api-Key"},
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## HITL (Human-in-the-Loop)
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
pending = client.list_pending_requests()
|
|
156
|
+
client.approve_request(pending[0]["requestId"])
|
|
157
|
+
client.deny_request(request_id, "Too risky")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Audit
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
records = client.query_audit_records(
|
|
164
|
+
agent["id"],
|
|
165
|
+
from_="2026-01-01",
|
|
166
|
+
policy_decision="DENY",
|
|
167
|
+
limit=10,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
verification = client.verify_chain(agent["id"])
|
|
171
|
+
# verification["valid"] → True
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Error Handling
|
|
175
|
+
|
|
176
|
+
All API errors raise typed exceptions:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from bastion_sdk import BastionForbiddenError, BastionNotFoundError
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
agent_client.execute(...)
|
|
183
|
+
except BastionForbiddenError as e:
|
|
184
|
+
print(f"Policy denied: {e.message}")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
| Error Class | Status | When |
|
|
188
|
+
|------------|--------|------|
|
|
189
|
+
| `BastionValidationError` | 400 | Invalid input |
|
|
190
|
+
| `BastionUnauthorizedError` | 401 | Bad or missing auth |
|
|
191
|
+
| `BastionForbiddenError` | 403 | Policy DENY or HITL timeout |
|
|
192
|
+
| `BastionNotFoundError` | 404 | Resource not found |
|
|
193
|
+
| `BastionConflictError` | 409 | State conflict |
|
|
194
|
+
| `BastionBadGatewayError` | 502 | Upstream API failure |
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# bastion-sdk
|
|
2
|
+
|
|
3
|
+
Python SDK for the [Bastion](../../README.md) trust proxy. Supports both sync and async usage.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install bastion-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Python 3.10+.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from bastion_sdk import BastionClient
|
|
17
|
+
|
|
18
|
+
with BastionClient("http://localhost:3000", api_key="your-project-api-key") as client:
|
|
19
|
+
print(client.health())
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Async
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from bastion_sdk import AsyncBastionClient
|
|
26
|
+
|
|
27
|
+
async with AsyncBastionClient("http://localhost:3000", api_key="your-key") as client:
|
|
28
|
+
print(await client.health())
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Admin Operations
|
|
32
|
+
|
|
33
|
+
Use the admin API key (`PROJECT_API_KEY`) to manage agents, credentials, and policies.
|
|
34
|
+
|
|
35
|
+
### Agents
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
# Create an agent (returns one-time agentSecret)
|
|
39
|
+
agent = client.create_agent("Support Bot", description="Handles refund requests")
|
|
40
|
+
agent_secret = agent["agentSecret"] # save this — shown only once
|
|
41
|
+
|
|
42
|
+
# List, get, update, delete
|
|
43
|
+
agents = client.list_agents()
|
|
44
|
+
agent = client.get_agent(agent["id"])
|
|
45
|
+
client.update_agent(agent["id"], isActive=False)
|
|
46
|
+
client.delete_agent(agent["id"]) # soft-delete
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Credentials
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# Store a credential (encrypted at rest, raw value never returned)
|
|
53
|
+
credential = client.create_credential(
|
|
54
|
+
name="Stripe Production",
|
|
55
|
+
type="API_KEY",
|
|
56
|
+
value="sk_live_...",
|
|
57
|
+
agent_id=agent["id"],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
credentials = client.list_credentials(agent_id=agent["id"])
|
|
61
|
+
client.revoke_credential(credential["id"])
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Policies
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
policy = client.create_policy(
|
|
68
|
+
agent_id=agent["id"],
|
|
69
|
+
credential_id=credential["id"],
|
|
70
|
+
allowed_actions=["charges.*"],
|
|
71
|
+
denied_actions=["transfers.*"],
|
|
72
|
+
constraints={
|
|
73
|
+
"maxAmountPerTransaction": 1000,
|
|
74
|
+
"maxDailySpend": 5000,
|
|
75
|
+
"rateLimit": {"maxRequests": 100, "windowSeconds": 3600},
|
|
76
|
+
},
|
|
77
|
+
requires_approval_above=500,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Dry-run evaluation (no side effects)
|
|
81
|
+
result = client.evaluate_policy(
|
|
82
|
+
agent_id=agent["id"],
|
|
83
|
+
credential_id=credential["id"],
|
|
84
|
+
action="charges.create",
|
|
85
|
+
params={"amount": 750},
|
|
86
|
+
)
|
|
87
|
+
# result["decision"] → "ESCALATE"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Agent Operations
|
|
91
|
+
|
|
92
|
+
Use an agent secret (`bst_...`) for proxy execution.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
agent_client = BastionClient("http://localhost:3000", api_key=agent_secret)
|
|
96
|
+
|
|
97
|
+
result = agent_client.execute(
|
|
98
|
+
credential_id=credential["id"],
|
|
99
|
+
action="charges.create",
|
|
100
|
+
target={
|
|
101
|
+
"url": "https://api.stripe.com/v1/charges",
|
|
102
|
+
"method": "POST",
|
|
103
|
+
"body": {"amount": 5000, "currency": "usd"},
|
|
104
|
+
},
|
|
105
|
+
params={"amount": 50},
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# result["upstream"]["status"] → 200
|
|
109
|
+
# result["upstream"]["body"] → Stripe's response
|
|
110
|
+
# result["meta"]["policyDecision"] → "ALLOW"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Custom Credential Injection
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
agent_client.execute(
|
|
117
|
+
credential_id=credential["id"],
|
|
118
|
+
action="test",
|
|
119
|
+
target={"url": "https://api.example.com"},
|
|
120
|
+
injection={"location": "header", "key": "X-Api-Key"},
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## HITL (Human-in-the-Loop)
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
pending = client.list_pending_requests()
|
|
128
|
+
client.approve_request(pending[0]["requestId"])
|
|
129
|
+
client.deny_request(request_id, "Too risky")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Audit
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
records = client.query_audit_records(
|
|
136
|
+
agent["id"],
|
|
137
|
+
from_="2026-01-01",
|
|
138
|
+
policy_decision="DENY",
|
|
139
|
+
limit=10,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
verification = client.verify_chain(agent["id"])
|
|
143
|
+
# verification["valid"] → True
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Error Handling
|
|
147
|
+
|
|
148
|
+
All API errors raise typed exceptions:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from bastion_sdk import BastionForbiddenError, BastionNotFoundError
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
agent_client.execute(...)
|
|
155
|
+
except BastionForbiddenError as e:
|
|
156
|
+
print(f"Policy denied: {e.message}")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Error Class | Status | When |
|
|
160
|
+
|------------|--------|------|
|
|
161
|
+
| `BastionValidationError` | 400 | Invalid input |
|
|
162
|
+
| `BastionUnauthorizedError` | 401 | Bad or missing auth |
|
|
163
|
+
| `BastionForbiddenError` | 403 | Policy DENY or HITL timeout |
|
|
164
|
+
| `BastionNotFoundError` | 404 | Resource not found |
|
|
165
|
+
| `BastionConflictError` | 409 | State conflict |
|
|
166
|
+
| `BastionBadGatewayError` | 502 | Upstream API failure |
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
[MIT](../../LICENSE)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bastion-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for the Bastion trust proxy"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Matthieu Hakim" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ai", "agents", "security", "proxy", "audit", "python"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Security",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"httpx>=0.27.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/Matthieuhakim/Bastion"
|
|
33
|
+
Repository = "https://github.com/Matthieuhakim/Bastion"
|
|
34
|
+
Issues = "https://github.com/Matthieuhakim/Bastion/issues"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=8.0.0",
|
|
39
|
+
"pytest-asyncio>=0.25.0",
|
|
40
|
+
"respx>=0.22.0",
|
|
41
|
+
"ruff>=0.8.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/bastion_sdk"]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Bastion SDK - Python client for the Bastion trust proxy."""
|
|
2
|
+
|
|
3
|
+
from .client import AsyncBastionClient, BastionClient
|
|
4
|
+
from .errors import (
|
|
5
|
+
BastionBadGatewayError,
|
|
6
|
+
BastionConflictError,
|
|
7
|
+
BastionError,
|
|
8
|
+
BastionForbiddenError,
|
|
9
|
+
BastionNotFoundError,
|
|
10
|
+
BastionUnauthorizedError,
|
|
11
|
+
BastionValidationError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"AsyncBastionClient",
|
|
16
|
+
"BastionBadGatewayError",
|
|
17
|
+
"BastionClient",
|
|
18
|
+
"BastionConflictError",
|
|
19
|
+
"BastionError",
|
|
20
|
+
"BastionForbiddenError",
|
|
21
|
+
"BastionNotFoundError",
|
|
22
|
+
"BastionUnauthorizedError",
|
|
23
|
+
"BastionValidationError",
|
|
24
|
+
]
|
|
25
|
+
__version__ = "0.1.0"
|