capforge 0.4.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.
Files changed (69) hide show
  1. capforge-0.4.0/.github/workflows/ci.yml +36 -0
  2. capforge-0.4.0/.gitignore +31 -0
  3. capforge-0.4.0/ARCHITECTURE.md +411 -0
  4. capforge-0.4.0/CHANGELOG.md +284 -0
  5. capforge-0.4.0/CONTRIBUTING.md +212 -0
  6. capforge-0.4.0/DECISIONS.md +433 -0
  7. capforge-0.4.0/LICENSE +201 -0
  8. capforge-0.4.0/PKG-INFO +568 -0
  9. capforge-0.4.0/README.md +535 -0
  10. capforge-0.4.0/RESEARCH_REPORT.md +499 -0
  11. capforge-0.4.0/ROADMAP.md +124 -0
  12. capforge-0.4.0/SECURITY.md +92 -0
  13. capforge-0.4.0/SPEC.md +284 -0
  14. capforge-0.4.0/SPECIFICATION.md +277 -0
  15. capforge-0.4.0/benchmarks/README.md +40 -0
  16. capforge-0.4.0/benchmarks/acm_benchmarks.py +242 -0
  17. capforge-0.4.0/capforge/__init__.py +50 -0
  18. capforge-0.4.0/capforge/_acm.py +491 -0
  19. capforge-0.4.0/capforge/_crypto.py +149 -0
  20. capforge-0.4.0/capforge/_delegation.py +83 -0
  21. capforge-0.4.0/capforge/_exceptions.py +21 -0
  22. capforge-0.4.0/capforge/_model.py +176 -0
  23. capforge-0.4.0/capforge/_policy.py +341 -0
  24. capforge-0.4.0/capforge/_validator.py +71 -0
  25. capforge-0.4.0/capforge/_version.py +3 -0
  26. capforge-0.4.0/capforge/cli/__init__.py +1 -0
  27. capforge-0.4.0/capforge/cli/main.py +347 -0
  28. capforge-0.4.0/capforge/py.typed +0 -0
  29. capforge-0.4.0/capforge.egg-info/PKG-INFO +568 -0
  30. capforge-0.4.0/capforge.egg-info/SOURCES.txt +67 -0
  31. capforge-0.4.0/capforge.egg-info/dependency_links.txt +1 -0
  32. capforge-0.4.0/capforge.egg-info/entry_points.txt +2 -0
  33. capforge-0.4.0/capforge.egg-info/requires.txt +12 -0
  34. capforge-0.4.0/capforge.egg-info/top_level.txt +2 -0
  35. capforge-0.4.0/capforge_langgraph/__init__.py +36 -0
  36. capforge-0.4.0/capforge_langgraph/_tool_wrapper.py +219 -0
  37. capforge-0.4.0/capforge_langgraph/pyproject.toml +35 -0
  38. capforge-0.4.0/examples/cli/README.md +74 -0
  39. capforge-0.4.0/examples/cli/run_cli_examples.sh +151 -0
  40. capforge-0.4.0/examples/crewai/README.md +118 -0
  41. capforge-0.4.0/examples/crewai/acm_controlled_crew.py +233 -0
  42. capforge-0.4.0/examples/crewai/requirements.txt +2 -0
  43. capforge-0.4.0/examples/fastapi/README.md +123 -0
  44. capforge-0.4.0/examples/fastapi/acm_protected_api.py +274 -0
  45. capforge-0.4.0/examples/fastapi/requirements.txt +3 -0
  46. capforge-0.4.0/examples/github_actions/README.md +104 -0
  47. capforge-0.4.0/examples/github_actions/verify_acm.yml +117 -0
  48. capforge-0.4.0/examples/langgraph/README.md +127 -0
  49. capforge-0.4.0/examples/langgraph/basic_acm_agent.py +225 -0
  50. capforge-0.4.0/examples/langgraph/requirements.txt +3 -0
  51. capforge-0.4.0/examples/openai_agents/README.md +118 -0
  52. capforge-0.4.0/examples/openai_agents/acm_controlled_agent.py +287 -0
  53. capforge-0.4.0/examples/openai_agents/requirements.txt +2 -0
  54. capforge-0.4.0/examples/policy/README.md +96 -0
  55. capforge-0.4.0/examples/policy/requirements.txt +1 -0
  56. capforge-0.4.0/examples/policy/runtime_checks.py +168 -0
  57. capforge-0.4.0/pyproject.toml +78 -0
  58. capforge-0.4.0/setup.cfg +4 -0
  59. capforge-0.4.0/spec/acm-schema.json +150 -0
  60. capforge-0.4.0/spec/examples/expired-acm.json +17 -0
  61. capforge-0.4.0/spec/examples/minimal-acm.json +15 -0
  62. capforge-0.4.0/spec/examples/valid-acm.json +41 -0
  63. capforge-0.4.0/tests/__init__.py +1 -0
  64. capforge-0.4.0/tests/test_acm.py +431 -0
  65. capforge-0.4.0/tests/test_crypto.py +163 -0
  66. capforge-0.4.0/tests/test_langgraph_adapter.py +217 -0
  67. capforge-0.4.0/tests/test_model.py +61 -0
  68. capforge-0.4.0/tests/test_policy.py +328 -0
  69. capforge-0.4.0/tests/test_validator.py +82 -0
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint-test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]" -e capforge_langgraph/
28
+
29
+ - name: Lint with ruff
30
+ run: ruff check .
31
+
32
+ - name: Type-check with mypy
33
+ run: mypy capforge/
34
+
35
+ - name: Test with pytest
36
+ run: pytest
@@ -0,0 +1,31 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+ .eggs/
9
+
10
+ # Testing
11
+ .pytest_cache/
12
+ .ruff_cache/
13
+ .mypy_cache/
14
+ htmlcov/
15
+ .coverage
16
+
17
+ # IDE
18
+ .vscode/
19
+ .idea/
20
+ *.swp
21
+ *.swo
22
+ *~
23
+
24
+ # OS
25
+ .DS_Store
26
+ Thumbs.db
27
+
28
+ # Environment
29
+ .env
30
+ .venv/
31
+ venv/
@@ -0,0 +1,411 @@
1
+ # Architecture
2
+
3
+ **CapForge — Agent Capability Manifest (ACM) SDK**
4
+
5
+ This document describes the architecture, module design, data flow, and
6
+ design principles of the CapForge SDK. It is a living document that evolves
7
+ with each phase.
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ 1. [Design Principles](#1-design-principles)
14
+ 2. [Package Structure](#2-package-structure)
15
+ 3. [Module Responsibilities](#3-module-responsibilities)
16
+ 4. [Public API](#4-public-api)
17
+ 5. [Data Flow](#5-data-flow)
18
+ 6. [Design Decisions](#6-design-decisions)
19
+
20
+ ---
21
+
22
+ ## 1. Design Principles
23
+
24
+ ### 1.1 Clean Public API
25
+
26
+ The SDK exposes a minimal, intuitive API. Users should be able to write:
27
+
28
+ ```python
29
+ from capforge import ACM, Capability, Constraints
30
+
31
+ manifest = ACM.create(...)
32
+ manifest.sign(key).save(path)
33
+ loaded = ACM.load(path)
34
+ loaded.verify(trusted_keys=...)
35
+ ```
36
+
37
+ Internal implementation details are hidden behind `_`-prefixed modules.
38
+
39
+ ### 1.2 Protocol, Not Platform
40
+
41
+ ACM is a document format and signing protocol, not a runtime platform.
42
+
43
+ **CapForge does not define:**
44
+ - How agents execute (LangGraph, CrewAI handle this)
45
+ - How policies are evaluated (OPA handles this)
46
+ - How audit records are stored (OTel handles this)
47
+ - How identities are issued (SPIFFE handles this)
48
+
49
+ **CapForge defines:**
50
+ - A signed JSON document format (the ACM)
51
+ - A canonical JSON algorithm for deterministic signing
52
+ - A capability expression model (resource:action pairs)
53
+ - A delegation model with scope narrowing
54
+ - A trust model with root keys and chain of custody
55
+
56
+ ### 1.3 Extensibility Through Metadata
57
+
58
+ The `metadata` field on ACM documents supports implementation-specific
59
+ annotations, framework-specific configuration, compliance labels, and version
60
+ tags.
61
+
62
+ ### 1.4 Zero Framework Dependency
63
+
64
+ The CapForge core has **zero dependencies** on any AI framework. Framework
65
+ adapters (LangGraph, CrewAI, etc.) are separate packages.
66
+
67
+ ---
68
+
69
+ ## 2. Package Structure
70
+
71
+ ```
72
+ capforge/ # Package root
73
+ ├── __init__.py # Public API — ACM, Capability, Constraints
74
+ ├── _version.py # Package version (private)
75
+ ├── _exceptions.py # Exception hierarchy (private)
76
+ ├── _model.py # Pydantic data models (private)
77
+ │ # Public classes: Capability, Constraints
78
+ │ # Internal classes: _ACMModel, _AgentInfo
79
+ ├── _crypto.py # Ed25519 signing/verification (private)
80
+ ├── _validator.py # Temporal + delegation validation (private)
81
+ ├── _delegation.py # Delegation scope narrowing (private)
82
+ ├── _acm.py # Main ACM class (private module, public class)
83
+ ├── py.typed # PEP 561 type marker
84
+ └── cli/
85
+ ├── __init__.py
86
+ └── main.py # Click CLI (6 commands)
87
+
88
+ tests/ # Test suite
89
+ ├── test_acm.py # 30 tests for the ACM class
90
+ ├── test_crypto.py # 14 tests for crypto functions
91
+ ├── test_validator.py # 5 tests for validation logic
92
+ └── test_model.py # 7 tests for Capability/Constraints models
93
+
94
+ spec/ # Protocol specification
95
+ ├── acm-schema.json # JSON Schema (Draft 2020-12)
96
+ └── examples/
97
+ ├── valid-acm.json
98
+ ├── minimal-acm.json
99
+ └── expired-acm.json
100
+ ```
101
+
102
+ ### Naming Convention
103
+
104
+ - **Public modules:** `capforge/__init__.py` (re-exports the public API)
105
+ - **Private modules:** All modules prefixed with `_` (e.g., `_acm.py`, `_model.py`)
106
+ - **Public classes:** `ACM`, `Capability`, `Constraints` (exported from `__init__.py`)
107
+ - **Internal classes:** Prefixed with `_` (e.g., `_ACMModel`, `_AgentInfo`)
108
+
109
+ ---
110
+
111
+ ## 3. Module Responsibilities
112
+
113
+ ### `capforge/_exceptions.py`
114
+
115
+ Exception hierarchy — all inheriting from `ACMError`:
116
+
117
+ ```
118
+ ACMError
119
+ ├── ACMValidationError — Schema or business rule violation
120
+ ├── ACMSignatureError — Signature verification failure
121
+ ├── ACMExpiredError — Document expired or not yet valid
122
+ └── ACMInvalidError — Structurally invalid document
123
+ ```
124
+
125
+ ### `capforge/_model.py`
126
+
127
+ Pydantic models mirroring the ACM JSON Schema:
128
+
129
+ | Class | Visibility | Description |
130
+ |:------|:-----------|:------------|
131
+ | `Capability` | **Public** | Resource:action pair with optional constraints |
132
+ | `Constraints` | **Public** | Global limits (cost, tokens, models, tools, time) |
133
+ | `_AgentInfo` | Private | Agent identity (SPIFFE + model + provider) |
134
+ | `_ACMModel` | Private | Internal ACM document model with validation |
135
+
136
+ `_ACMModel` provides:
137
+ - Structural validation (SPIFFE patterns, version const, minimum capabilities)
138
+ - `is_expired()` and `is_valid_now()` temporal checks
139
+
140
+ ### `capforge/_crypto.py`
141
+
142
+ Ed25519 cryptographic operations:
143
+
144
+ | Function | Purpose |
145
+ |:---------|:--------|
146
+ | `generate_private_key()` | Create a new Ed25519 key pair |
147
+ | `canonical_json(acm_dict)` | Deterministic JSON for signing |
148
+ | `sign(canonical_bytes, key)` | Return base64 signature |
149
+ | `verify(canonical_bytes, sig, key)` | Verify signature |
150
+ | `sign_dict(acm_dict, key)` | Canonicalize + sign |
151
+ | `verify_dict(acm_dict, sig, key)` | Canonicalize + verify |
152
+ | `load_private_key(path)` | Load PEM private key |
153
+ | `load_public_key(path)` | Load PEM public key |
154
+ | `write_key_pair(key, ...)` | Save PEM key pair to files |
155
+
156
+ ### `capforge/_validator.py`
157
+
158
+ Business-logic validation that Pydantic cannot express:
159
+
160
+ - Temporal bounds (`expires_at`, `not_before`)
161
+ - Delegation chain SPIFFE ID validity
162
+
163
+ Exposes a single function: `validate(acm: _ACMModel, ...) -> None`.
164
+
165
+ ### `capforge/_delegation.py`
166
+
167
+ Delegation scope narrowing logic:
168
+
169
+ - Validates child capabilities are a subset of parent capabilities
170
+ - Builds delegation chain (parent chain + parent issuer)
171
+ - Returns a new `_ACMModel` for the delegated document
172
+
173
+ ### `capforge/_policy.py` (Phase 4)
174
+
175
+ Runtime authorization engine. Turns "does this capability exist?" into
176
+ "may this action execute right now?":
177
+
178
+ | Component | Role |
179
+ |:----------|:-----|
180
+ | `RuntimeEvaluator` | Orchestrates all checks in order: temporal → capability → model → tool → execution time → cost → tokens |
181
+ | `BudgetEvaluator` | Evaluates cost and token limits against session budgets |
182
+ | `ConstraintEvaluator` | Evaluates model restrictions, disallowed tools, execution timeouts |
183
+ | `PolicyDecision` | Public dataclass with `allowed`, `reason`, `failed_constraint`, `remaining_budget` |
184
+
185
+ All evaluators are private. The only public interface is `ACM.check()`.
186
+
187
+ ### `capforge/_acm.py`
188
+
189
+ The main `ACM` class — the public interface for all SDK operations:
190
+
191
+ | Method | Description |
192
+ |:-------|:------------|
193
+ | `ACM.create(...)` | Factory: create a new manifest |
194
+ | `ACM.load(source)` | Factory: load from file or dict |
195
+ | `manifest.save(path)` | Save to file (returns JSON string) |
196
+ | `manifest.sign(key)` | Sign with Ed25519 key |
197
+ | `manifest.verify(...)` | Temporal + optional crypto verification |
198
+ | `manifest.delegate(...)` | Create delegated ACM |
199
+ | `manifest.has_capability(...)` | Check capability presence |
200
+ | `manifest.get_capability(...)` | Get capability by resource/action |
201
+ | `manifest.is_valid()` | Temporal validity check |
202
+ | `manifest.is_expired()` | Expiry check |
203
+ | `manifest.to_dict()` | Export as dictionary |
204
+ | `manifest.to_json()` | Export as JSON string |
205
+
206
+ Property accessors: `spiffe_id`, `capabilities_list`, `constraints_obj`,
207
+ `sponsor`, `issuer`, `expires_at`, `not_before`, `delegation_chain`,
208
+ `signature`, `metadata`, `acm_version`, `agent_model`, `agent_provider`.
209
+
210
+ ### `capforge/cli/main.py`
211
+
212
+ Click-based CLI with 6 commands:
213
+
214
+ | Command | Purpose |
215
+ |:--------|:---------|
216
+ | `capforge keygen` | Generate Ed25519 key pair |
217
+ | `capforge create` | Create (and optionally sign) an ACM document |
218
+ | `capforge verify` | Verify an ACM document (temporal + optional crypto) |
219
+ | `capforge validate` | Temporal + structural validation only |
220
+ | `capforge info` | Display human-readable ACM information |
221
+ | `capforge delegate` | Create a delegated ACM with narrowed scope |
222
+
223
+ ### `capforge_langgraph/_tool_wrapper.py` (Phase 3b)
224
+
225
+ ACM-controlled LangGraph tool wrapper:
226
+
227
+ | Class / Function | Description |
228
+ |:-----------------|:------------|
229
+ | `ACMControlledTool` | Wraps a LangChain ``BaseTool`` with ACM authorization checks |
230
+ | `wrap_tools()` | Convenience to wrap multiple tools with matching capabilities |
231
+
232
+ `ACMControlledTool` overrides `invoke()`/`ainvoke()` to:
233
+ 1. Check ACM temporal validity (`is_valid()`)
234
+ 2. Check capability presence (`has_capability(resource, action)`)
235
+ 3. Delegates to the inner tool if both checks pass
236
+
237
+ The adapter uses **only the public CapForge API** (`capforge.ACM`,
238
+ `capforge.Capability`). No private internal modules are imported.
239
+
240
+ ---
241
+
242
+ ## 4. Public API
243
+
244
+ ### Import Paths
245
+
246
+ ```python
247
+ from capforge import ACM # Main class
248
+ from capforge import Capability # Capability data model
249
+ from capforge import Constraints # Constraints data model
250
+ from capforge import __version__ # Package version
251
+ ```
252
+
253
+ ### User-Facing API Pattern
254
+
255
+ ```python
256
+ # Create
257
+ manifest = ACM.create(
258
+ spiffe_id="spiffe://org/agent/bot",
259
+ sponsor="user@org.com",
260
+ capabilities=[Capability(resource="kb", action="read")],
261
+ constraints=Constraints(max_cost_per_session=0.05),
262
+ ttl=3600, # optional, default 3600
263
+ model="gpt-5", # optional
264
+ provider="openai", # optional
265
+ )
266
+
267
+ # Sign
268
+ manifest.sign("my-key.pem") # or pass Ed25519PrivateKey object
269
+
270
+ # Save
271
+ manifest.save("manifest.json")
272
+
273
+ # Load
274
+ loaded = ACM.load("manifest.json")
275
+
276
+ # Verify
277
+ loaded.verify() # temporal only
278
+ loaded.verify(trusted_keys=["root.pub"]) # temporal + crypto
279
+
280
+ # Delegate
281
+ child = loaded.delegate(
282
+ child_spiffe_id="spiffe://org/agent/sub",
283
+ capabilities=[Capability(resource="kb", action="read")],
284
+ key="parent-key.pem", # optional
285
+ )
286
+ ```
287
+
288
+ ### Error Handling
289
+
290
+ All CapForge-specific exceptions inherit from `ACMError`:
291
+
292
+ ```python
293
+ from capforge import ACMError
294
+ from capforge._exceptions import ACMExpiredError, ACMSignatureError
295
+
296
+ try:
297
+ manifest.verify(trusted_keys=["root.pub"])
298
+ except ACMExpiredError:
299
+ print("Manifest has expired")
300
+ except ACMSignatureError:
301
+ print("Signature verification failed")
302
+ except ACMError:
303
+ print("Other ACM error")
304
+ ```
305
+
306
+ ---
307
+
308
+ ## 5. Data Flow
309
+
310
+ ### ACM Creation Flow
311
+
312
+ ```
313
+ User code or CLI
314
+
315
+ ├── Provides: SPIFFE ID, sponsor, capabilities, constraints
316
+
317
+
318
+ ACM.create()
319
+
320
+ ├── Constructs _ACMModel (Pydantic validates)
321
+ │ └── Validates: version="1.0", SPIFFE patterns, min 1 capability
322
+
323
+
324
+ ACM instance (wraps _ACMModel)
325
+
326
+ ├── manifest.sign(key)
327
+ │ ├── Canonicalizes JSON (sorted keys, no whitespace, no signature)
328
+ │ ├── Signs with Ed25519
329
+ │ └── Stores base64 signature in model
330
+
331
+ ├── manifest.save(path)
332
+ │ └── Writes pretty-printed JSON to file
333
+
334
+
335
+ ACM Document (JSON file or dict)
336
+ ```
337
+
338
+ ### ACM Verification Flow
339
+
340
+ ```
341
+ ACM Document (JSON or dict)
342
+
343
+
344
+ ACM.load(source)
345
+
346
+ ├── Parses JSON
347
+ ├── Constructs _ACMModel (Pydantic structural validation)
348
+
349
+
350
+ ACM instance
351
+
352
+ ├── manifest.verify()
353
+ │ │
354
+ │ ├── _validator.validate(_ACMModel)
355
+ │ │ ├── Temporal check (expires_at, not_before)
356
+ │ │ └── Delegation chain SPIFFE check
357
+ │ │
358
+ │ ├── [If trusted_keys provided]
359
+ │ │ ├── Re-canonicalize JSON
360
+ │ │ ├── Verify signature against each trusted key
361
+ │ │ └── Raise ACMSignatureError if none match
362
+ │ │
363
+ │ ▼
364
+ │ Pass / Raise
365
+
366
+
367
+ Returns None (raises on failure)
368
+ ```
369
+
370
+ ### Delegation Flow
371
+
372
+ ```
373
+ Parent ACM (signed)
374
+
375
+
376
+ ACM.delegate(child_spiffe_id, capabilities, ...)
377
+
378
+ ├── Validate child capabilities ⊆ parent capabilities
379
+ ├── Build delegation chain (parent chain + parent issuer)
380
+ ├── Create child _ACMModel with narrowed scope
381
+ ├── [If key provided] Sign child
382
+
383
+
384
+ Child ACM (delegated, scoped, optionally signed)
385
+ ```
386
+
387
+ ---
388
+
389
+ ## 6. Design Decisions
390
+
391
+ See [DECISIONS.md](DECISIONS.md) for all 13 Architecture Decision Records.
392
+
393
+ | ADR | Title | Phase |
394
+ |:----|:------|:------|
395
+ | 001 | SPIFFE ID Format | 1 |
396
+ | 002 | Pydantic for Data Modeling | 1 |
397
+ | 003 | Resource:Action Capability Expression | 1 |
398
+ | 004 | Ed25519 for Signing | 2 |
399
+ | 005 | Separate Validator from Models | 1 |
400
+ | 006 | Manual CLI → Click Migration | 2 |
401
+ | 007 | Optional Metadata Field | 1 |
402
+ | 008 | Canonical JSON Format | 2 |
403
+ | 009 | Type-Narrowed Key Loading | 2 |
404
+ | 010 | PEM-Only Key Format | 2 |
405
+ | 011 | Adapter Interface as Protocol Classes | 3 |
406
+ | 012 | Python Entry Points for Plugin Discovery | 3 |
407
+ | 013 | Zero Core Dependency on AI Frameworks | 3 |
408
+
409
+ ---
410
+
411
+ *This document was last updated for Phase 3a (CapForge SDK, 2026-07-22).*