kavachos 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.
- kavachos-0.1.0/.gitignore +45 -0
- kavachos-0.1.0/PKG-INFO +342 -0
- kavachos-0.1.0/README.md +312 -0
- kavachos-0.1.0/kavachos/__init__.py +115 -0
- kavachos-0.1.0/kavachos/_http.py +290 -0
- kavachos-0.1.0/kavachos/agents.py +197 -0
- kavachos-0.1.0/kavachos/audit.py +110 -0
- kavachos-0.1.0/kavachos/auth.py +175 -0
- kavachos-0.1.0/kavachos/client.py +170 -0
- kavachos-0.1.0/kavachos/delegation.py +96 -0
- kavachos-0.1.0/kavachos/errors.py +124 -0
- kavachos-0.1.0/kavachos/permissions.py +165 -0
- kavachos-0.1.0/kavachos/types.py +505 -0
- kavachos-0.1.0/pyproject.toml +53 -0
- kavachos-0.1.0/tests/__init__.py +0 -0
- kavachos-0.1.0/tests/conftest.py +109 -0
- kavachos-0.1.0/tests/test_agents.py +340 -0
- kavachos-0.1.0/tests/test_audit_delegation.py +346 -0
- kavachos-0.1.0/tests/test_auth.py +238 -0
- kavachos-0.1.0/tests/test_client.py +191 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnpm-store/
|
|
4
|
+
|
|
5
|
+
# Build outputs
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.tsbuildinfo
|
|
9
|
+
.next/
|
|
10
|
+
.source/
|
|
11
|
+
|
|
12
|
+
# Test & coverage
|
|
13
|
+
coverage/
|
|
14
|
+
.nyc_output/
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
*~
|
|
22
|
+
|
|
23
|
+
# OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# Env
|
|
28
|
+
.env
|
|
29
|
+
.env.*
|
|
30
|
+
!.env.example
|
|
31
|
+
|
|
32
|
+
# Turbo
|
|
33
|
+
.turbo/
|
|
34
|
+
|
|
35
|
+
# Misc
|
|
36
|
+
*.log
|
|
37
|
+
tmp/
|
|
38
|
+
.tmp/
|
|
39
|
+
.cache/
|
|
40
|
+
|
|
41
|
+
# Docs local (research, reference repos, not shipped)
|
|
42
|
+
docs-local/
|
|
43
|
+
|
|
44
|
+
# Claude instructions (project-specific, not shipped)
|
|
45
|
+
CLAUDE.md
|
kavachos-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kavachos
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for KavachOS — auth OS for AI agents and humans
|
|
5
|
+
Project-URL: Homepage, https://kavachos.com
|
|
6
|
+
Project-URL: Repository, https://github.com/kavachos/kavachos
|
|
7
|
+
Project-URL: Documentation, https://kavachos.com/docs
|
|
8
|
+
Project-URL: Issues, https://github.com/kavachos/kavachos/issues
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: agents,ai,auth,identity,mcp,permissions
|
|
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 :: Security
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.9; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# kavachos
|
|
32
|
+
|
|
33
|
+
Python SDK for [KavachOS](https://kavachos.dev) — auth OS for AI agents and humans.
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/kavachos/)
|
|
36
|
+
[](https://pypi.org/project/kavachos/)
|
|
37
|
+
[](../../LICENSE)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install kavachos
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires Python 3.9+ and `httpx`.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
### Async (recommended)
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
from kavachos import KavachClient
|
|
58
|
+
from kavachos.types import CreateAgentInput
|
|
59
|
+
from kavachos.permissions import read, with_approval, execute
|
|
60
|
+
|
|
61
|
+
async def main():
|
|
62
|
+
async with KavachClient(
|
|
63
|
+
base_url="https://your-app.com/api/kavach",
|
|
64
|
+
token="kv_...",
|
|
65
|
+
) as client:
|
|
66
|
+
# Create an agent
|
|
67
|
+
agent = await client.agents.create(
|
|
68
|
+
CreateAgentInput(
|
|
69
|
+
owner_id="user-123",
|
|
70
|
+
name="github-reader",
|
|
71
|
+
type="autonomous",
|
|
72
|
+
permissions=[
|
|
73
|
+
read("mcp:github:*"),
|
|
74
|
+
with_approval(execute("mcp:deploy:production")),
|
|
75
|
+
],
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Check authorization
|
|
80
|
+
result = await client.authorize(
|
|
81
|
+
agent.id,
|
|
82
|
+
AuthorizeRequest(action="read", resource="mcp:github:repos"),
|
|
83
|
+
)
|
|
84
|
+
print(result.allowed) # True
|
|
85
|
+
|
|
86
|
+
asyncio.run(main())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Sync
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from kavachos import KavachSyncClient
|
|
93
|
+
from kavachos.types import CreateAgentInput
|
|
94
|
+
from kavachos.permissions import read
|
|
95
|
+
|
|
96
|
+
with KavachSyncClient(
|
|
97
|
+
base_url="https://your-app.com/api/kavach",
|
|
98
|
+
token="kv_...",
|
|
99
|
+
) as client:
|
|
100
|
+
agent = client.agents.create(
|
|
101
|
+
CreateAgentInput(
|
|
102
|
+
owner_id="user-123",
|
|
103
|
+
name="github-reader",
|
|
104
|
+
type="autonomous",
|
|
105
|
+
permissions=[read("mcp:github:*")],
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
print(agent.id, agent.token)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Authentication
|
|
114
|
+
|
|
115
|
+
Sign in and sign up with email and password.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
async with KavachClient(base_url="https://your-app.com/api/kavach") as client:
|
|
119
|
+
# Sign up
|
|
120
|
+
auth = await client.auth.sign_up(
|
|
121
|
+
email="user@example.com",
|
|
122
|
+
password="secure-password",
|
|
123
|
+
name="Alice",
|
|
124
|
+
)
|
|
125
|
+
print(auth.user.id)
|
|
126
|
+
print(auth.session.token)
|
|
127
|
+
|
|
128
|
+
# Sign in later
|
|
129
|
+
auth = await client.auth.sign_in(
|
|
130
|
+
email="user@example.com",
|
|
131
|
+
password="secure-password",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Get current session
|
|
135
|
+
session = await client.auth.get_session(token=auth.session.token)
|
|
136
|
+
|
|
137
|
+
# Sign out
|
|
138
|
+
await client.auth.sign_out()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Agent management
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from kavachos.types import AgentFilters, UpdateAgentInput
|
|
147
|
+
|
|
148
|
+
# List agents for a user
|
|
149
|
+
agents = await client.agents.list(AgentFilters(user_id="user-123", status="active"))
|
|
150
|
+
|
|
151
|
+
# Get a single agent (returns None if not found)
|
|
152
|
+
agent = await client.agents.get("agent-abc123")
|
|
153
|
+
|
|
154
|
+
# Update name or permissions
|
|
155
|
+
agent = await client.agents.update(
|
|
156
|
+
"agent-abc123",
|
|
157
|
+
UpdateAgentInput(name="better-name"),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Rotate the token (old token is immediately invalidated)
|
|
161
|
+
agent = await client.agents.rotate("agent-abc123")
|
|
162
|
+
print(agent.token) # kv_new_...
|
|
163
|
+
|
|
164
|
+
# Revoke (delete) an agent
|
|
165
|
+
await client.agents.revoke("agent-abc123")
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Authorization
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from kavachos.types import AuthorizeRequest
|
|
174
|
+
|
|
175
|
+
# Authorize by agent ID (requires admin/service token on the client)
|
|
176
|
+
result = await client.authorize(
|
|
177
|
+
"agent-abc123",
|
|
178
|
+
AuthorizeRequest(
|
|
179
|
+
action="execute",
|
|
180
|
+
resource="mcp:deploy:production",
|
|
181
|
+
arguments={"version": "1.2.3"},
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
print(result.allowed) # True / False
|
|
185
|
+
print(result.audit_id) # "aud_..."
|
|
186
|
+
|
|
187
|
+
# Authorize using the agent's own bearer token (no admin token needed)
|
|
188
|
+
result = await client.auth.authorize_by_token(
|
|
189
|
+
agent_token="kv_agent_xyz",
|
|
190
|
+
request=AuthorizeRequest(action="read", resource="mcp:github:repos"),
|
|
191
|
+
)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Permissions helpers
|
|
197
|
+
|
|
198
|
+
The `kavachos.permissions` module provides shorthand constructors.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from kavachos.permissions import (
|
|
202
|
+
read,
|
|
203
|
+
write,
|
|
204
|
+
execute,
|
|
205
|
+
read_write,
|
|
206
|
+
full_access,
|
|
207
|
+
with_approval,
|
|
208
|
+
rate_limited,
|
|
209
|
+
)
|
|
210
|
+
from kavachos.types import PermissionConstraints
|
|
211
|
+
|
|
212
|
+
# Simple read permission
|
|
213
|
+
perm = read("mcp:github:*")
|
|
214
|
+
|
|
215
|
+
# Require human approval before execution
|
|
216
|
+
perm = with_approval(execute("mcp:deploy:production"))
|
|
217
|
+
|
|
218
|
+
# Limit to 100 calls per hour
|
|
219
|
+
perm = rate_limited(read("mcp:github:*"), max_calls_per_hour=100)
|
|
220
|
+
|
|
221
|
+
# Full manual construction
|
|
222
|
+
from kavachos.types import Permission
|
|
223
|
+
perm = Permission(
|
|
224
|
+
resource="mcp:github:*",
|
|
225
|
+
actions=["read", "write"],
|
|
226
|
+
constraints=PermissionConstraints(
|
|
227
|
+
max_calls_per_hour=200,
|
|
228
|
+
ip_allowlist=["10.0.0.0/8"],
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Audit log
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from kavachos.types import AuditFilters, ExportOptions
|
|
239
|
+
|
|
240
|
+
# Query the audit log
|
|
241
|
+
entries = await client.audit.query(
|
|
242
|
+
AuditFilters(
|
|
243
|
+
agent_id="agent-abc123",
|
|
244
|
+
result="allowed",
|
|
245
|
+
limit=50,
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
for entry in entries:
|
|
249
|
+
print(entry.timestamp, entry.action, entry.resource, entry.result)
|
|
250
|
+
|
|
251
|
+
# Paginated response (includes total count)
|
|
252
|
+
page = await client.audit.query_paginated(AuditFilters(limit=20, offset=0))
|
|
253
|
+
print(f"{len(page.entries)} of {page.total}")
|
|
254
|
+
|
|
255
|
+
# Export as JSON or CSV
|
|
256
|
+
csv_text = await client.audit.export(
|
|
257
|
+
ExportOptions(
|
|
258
|
+
format="csv",
|
|
259
|
+
since="2024-01-01T00:00:00Z",
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Delegation
|
|
267
|
+
|
|
268
|
+
Delegate a subset of an agent's permissions to another agent, with an optional
|
|
269
|
+
depth limit.
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
from kavachos.types import DelegateInput
|
|
273
|
+
from kavachos.permissions import read
|
|
274
|
+
|
|
275
|
+
# Create a delegation
|
|
276
|
+
chain = await client.delegation.create(
|
|
277
|
+
DelegateInput(
|
|
278
|
+
from_agent="agent-abc123",
|
|
279
|
+
to_agent="agent-def456",
|
|
280
|
+
permissions=[read("mcp:github:repos")],
|
|
281
|
+
expires_at="2025-12-31T00:00:00Z",
|
|
282
|
+
max_depth=2,
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# List all chains for an agent
|
|
287
|
+
chains = await client.delegation.list_chains("agent-abc123")
|
|
288
|
+
|
|
289
|
+
# Get the effective (merged) permissions for an agent
|
|
290
|
+
perms = await client.delegation.get_effective_permissions("agent-def456")
|
|
291
|
+
|
|
292
|
+
# Revoke a delegation
|
|
293
|
+
await client.delegation.revoke(chain.id)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Error handling
|
|
299
|
+
|
|
300
|
+
All exceptions inherit from `kavachos.KavachError`.
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
from kavachos.errors import (
|
|
304
|
+
KavachError,
|
|
305
|
+
AuthenticationError, # 401
|
|
306
|
+
PermissionError, # 403
|
|
307
|
+
NotFoundError, # 404
|
|
308
|
+
RateLimitError, # 429 — has .retry_after
|
|
309
|
+
ServerError, # 5xx
|
|
310
|
+
NetworkError, # Transport failure
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
try:
|
|
314
|
+
agent = await client.agents.get("agent-missing")
|
|
315
|
+
except NotFoundError:
|
|
316
|
+
print("Agent does not exist")
|
|
317
|
+
except RateLimitError as e:
|
|
318
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
319
|
+
except AuthenticationError:
|
|
320
|
+
print("Check your token")
|
|
321
|
+
except KavachError as e:
|
|
322
|
+
print(f"[{e.code}] {e.message} (HTTP {e.status_code})")
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Configuration
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
KavachClient(
|
|
331
|
+
base_url="https://your-app.com/api/kavach", # required
|
|
332
|
+
token="kv_...", # optional bearer token
|
|
333
|
+
headers={"X-Tenant": "acme"}, # extra headers on every request
|
|
334
|
+
timeout=30.0, # seconds (default 30)
|
|
335
|
+
)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
MIT
|
kavachos-0.1.0/README.md
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# kavachos
|
|
2
|
+
|
|
3
|
+
Python SDK for [KavachOS](https://kavachos.dev) — auth OS for AI agents and humans.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/kavachos/)
|
|
6
|
+
[](https://pypi.org/project/kavachos/)
|
|
7
|
+
[](../../LICENSE)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install kavachos
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Python 3.9+ and `httpx`.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
### Async (recommended)
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import asyncio
|
|
27
|
+
from kavachos import KavachClient
|
|
28
|
+
from kavachos.types import CreateAgentInput
|
|
29
|
+
from kavachos.permissions import read, with_approval, execute
|
|
30
|
+
|
|
31
|
+
async def main():
|
|
32
|
+
async with KavachClient(
|
|
33
|
+
base_url="https://your-app.com/api/kavach",
|
|
34
|
+
token="kv_...",
|
|
35
|
+
) as client:
|
|
36
|
+
# Create an agent
|
|
37
|
+
agent = await client.agents.create(
|
|
38
|
+
CreateAgentInput(
|
|
39
|
+
owner_id="user-123",
|
|
40
|
+
name="github-reader",
|
|
41
|
+
type="autonomous",
|
|
42
|
+
permissions=[
|
|
43
|
+
read("mcp:github:*"),
|
|
44
|
+
with_approval(execute("mcp:deploy:production")),
|
|
45
|
+
],
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Check authorization
|
|
50
|
+
result = await client.authorize(
|
|
51
|
+
agent.id,
|
|
52
|
+
AuthorizeRequest(action="read", resource="mcp:github:repos"),
|
|
53
|
+
)
|
|
54
|
+
print(result.allowed) # True
|
|
55
|
+
|
|
56
|
+
asyncio.run(main())
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Sync
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from kavachos import KavachSyncClient
|
|
63
|
+
from kavachos.types import CreateAgentInput
|
|
64
|
+
from kavachos.permissions import read
|
|
65
|
+
|
|
66
|
+
with KavachSyncClient(
|
|
67
|
+
base_url="https://your-app.com/api/kavach",
|
|
68
|
+
token="kv_...",
|
|
69
|
+
) as client:
|
|
70
|
+
agent = client.agents.create(
|
|
71
|
+
CreateAgentInput(
|
|
72
|
+
owner_id="user-123",
|
|
73
|
+
name="github-reader",
|
|
74
|
+
type="autonomous",
|
|
75
|
+
permissions=[read("mcp:github:*")],
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
print(agent.id, agent.token)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Authentication
|
|
84
|
+
|
|
85
|
+
Sign in and sign up with email and password.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
async with KavachClient(base_url="https://your-app.com/api/kavach") as client:
|
|
89
|
+
# Sign up
|
|
90
|
+
auth = await client.auth.sign_up(
|
|
91
|
+
email="user@example.com",
|
|
92
|
+
password="secure-password",
|
|
93
|
+
name="Alice",
|
|
94
|
+
)
|
|
95
|
+
print(auth.user.id)
|
|
96
|
+
print(auth.session.token)
|
|
97
|
+
|
|
98
|
+
# Sign in later
|
|
99
|
+
auth = await client.auth.sign_in(
|
|
100
|
+
email="user@example.com",
|
|
101
|
+
password="secure-password",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Get current session
|
|
105
|
+
session = await client.auth.get_session(token=auth.session.token)
|
|
106
|
+
|
|
107
|
+
# Sign out
|
|
108
|
+
await client.auth.sign_out()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Agent management
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from kavachos.types import AgentFilters, UpdateAgentInput
|
|
117
|
+
|
|
118
|
+
# List agents for a user
|
|
119
|
+
agents = await client.agents.list(AgentFilters(user_id="user-123", status="active"))
|
|
120
|
+
|
|
121
|
+
# Get a single agent (returns None if not found)
|
|
122
|
+
agent = await client.agents.get("agent-abc123")
|
|
123
|
+
|
|
124
|
+
# Update name or permissions
|
|
125
|
+
agent = await client.agents.update(
|
|
126
|
+
"agent-abc123",
|
|
127
|
+
UpdateAgentInput(name="better-name"),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Rotate the token (old token is immediately invalidated)
|
|
131
|
+
agent = await client.agents.rotate("agent-abc123")
|
|
132
|
+
print(agent.token) # kv_new_...
|
|
133
|
+
|
|
134
|
+
# Revoke (delete) an agent
|
|
135
|
+
await client.agents.revoke("agent-abc123")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Authorization
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from kavachos.types import AuthorizeRequest
|
|
144
|
+
|
|
145
|
+
# Authorize by agent ID (requires admin/service token on the client)
|
|
146
|
+
result = await client.authorize(
|
|
147
|
+
"agent-abc123",
|
|
148
|
+
AuthorizeRequest(
|
|
149
|
+
action="execute",
|
|
150
|
+
resource="mcp:deploy:production",
|
|
151
|
+
arguments={"version": "1.2.3"},
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
print(result.allowed) # True / False
|
|
155
|
+
print(result.audit_id) # "aud_..."
|
|
156
|
+
|
|
157
|
+
# Authorize using the agent's own bearer token (no admin token needed)
|
|
158
|
+
result = await client.auth.authorize_by_token(
|
|
159
|
+
agent_token="kv_agent_xyz",
|
|
160
|
+
request=AuthorizeRequest(action="read", resource="mcp:github:repos"),
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Permissions helpers
|
|
167
|
+
|
|
168
|
+
The `kavachos.permissions` module provides shorthand constructors.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from kavachos.permissions import (
|
|
172
|
+
read,
|
|
173
|
+
write,
|
|
174
|
+
execute,
|
|
175
|
+
read_write,
|
|
176
|
+
full_access,
|
|
177
|
+
with_approval,
|
|
178
|
+
rate_limited,
|
|
179
|
+
)
|
|
180
|
+
from kavachos.types import PermissionConstraints
|
|
181
|
+
|
|
182
|
+
# Simple read permission
|
|
183
|
+
perm = read("mcp:github:*")
|
|
184
|
+
|
|
185
|
+
# Require human approval before execution
|
|
186
|
+
perm = with_approval(execute("mcp:deploy:production"))
|
|
187
|
+
|
|
188
|
+
# Limit to 100 calls per hour
|
|
189
|
+
perm = rate_limited(read("mcp:github:*"), max_calls_per_hour=100)
|
|
190
|
+
|
|
191
|
+
# Full manual construction
|
|
192
|
+
from kavachos.types import Permission
|
|
193
|
+
perm = Permission(
|
|
194
|
+
resource="mcp:github:*",
|
|
195
|
+
actions=["read", "write"],
|
|
196
|
+
constraints=PermissionConstraints(
|
|
197
|
+
max_calls_per_hour=200,
|
|
198
|
+
ip_allowlist=["10.0.0.0/8"],
|
|
199
|
+
),
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Audit log
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from kavachos.types import AuditFilters, ExportOptions
|
|
209
|
+
|
|
210
|
+
# Query the audit log
|
|
211
|
+
entries = await client.audit.query(
|
|
212
|
+
AuditFilters(
|
|
213
|
+
agent_id="agent-abc123",
|
|
214
|
+
result="allowed",
|
|
215
|
+
limit=50,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
for entry in entries:
|
|
219
|
+
print(entry.timestamp, entry.action, entry.resource, entry.result)
|
|
220
|
+
|
|
221
|
+
# Paginated response (includes total count)
|
|
222
|
+
page = await client.audit.query_paginated(AuditFilters(limit=20, offset=0))
|
|
223
|
+
print(f"{len(page.entries)} of {page.total}")
|
|
224
|
+
|
|
225
|
+
# Export as JSON or CSV
|
|
226
|
+
csv_text = await client.audit.export(
|
|
227
|
+
ExportOptions(
|
|
228
|
+
format="csv",
|
|
229
|
+
since="2024-01-01T00:00:00Z",
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Delegation
|
|
237
|
+
|
|
238
|
+
Delegate a subset of an agent's permissions to another agent, with an optional
|
|
239
|
+
depth limit.
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from kavachos.types import DelegateInput
|
|
243
|
+
from kavachos.permissions import read
|
|
244
|
+
|
|
245
|
+
# Create a delegation
|
|
246
|
+
chain = await client.delegation.create(
|
|
247
|
+
DelegateInput(
|
|
248
|
+
from_agent="agent-abc123",
|
|
249
|
+
to_agent="agent-def456",
|
|
250
|
+
permissions=[read("mcp:github:repos")],
|
|
251
|
+
expires_at="2025-12-31T00:00:00Z",
|
|
252
|
+
max_depth=2,
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# List all chains for an agent
|
|
257
|
+
chains = await client.delegation.list_chains("agent-abc123")
|
|
258
|
+
|
|
259
|
+
# Get the effective (merged) permissions for an agent
|
|
260
|
+
perms = await client.delegation.get_effective_permissions("agent-def456")
|
|
261
|
+
|
|
262
|
+
# Revoke a delegation
|
|
263
|
+
await client.delegation.revoke(chain.id)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Error handling
|
|
269
|
+
|
|
270
|
+
All exceptions inherit from `kavachos.KavachError`.
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from kavachos.errors import (
|
|
274
|
+
KavachError,
|
|
275
|
+
AuthenticationError, # 401
|
|
276
|
+
PermissionError, # 403
|
|
277
|
+
NotFoundError, # 404
|
|
278
|
+
RateLimitError, # 429 — has .retry_after
|
|
279
|
+
ServerError, # 5xx
|
|
280
|
+
NetworkError, # Transport failure
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
agent = await client.agents.get("agent-missing")
|
|
285
|
+
except NotFoundError:
|
|
286
|
+
print("Agent does not exist")
|
|
287
|
+
except RateLimitError as e:
|
|
288
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
289
|
+
except AuthenticationError:
|
|
290
|
+
print("Check your token")
|
|
291
|
+
except KavachError as e:
|
|
292
|
+
print(f"[{e.code}] {e.message} (HTTP {e.status_code})")
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Configuration
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
KavachClient(
|
|
301
|
+
base_url="https://your-app.com/api/kavach", # required
|
|
302
|
+
token="kv_...", # optional bearer token
|
|
303
|
+
headers={"X-Tenant": "acme"}, # extra headers on every request
|
|
304
|
+
timeout=30.0, # seconds (default 30)
|
|
305
|
+
)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## License
|
|
311
|
+
|
|
312
|
+
MIT
|