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.
- capforge-0.4.0/.github/workflows/ci.yml +36 -0
- capforge-0.4.0/.gitignore +31 -0
- capforge-0.4.0/ARCHITECTURE.md +411 -0
- capforge-0.4.0/CHANGELOG.md +284 -0
- capforge-0.4.0/CONTRIBUTING.md +212 -0
- capforge-0.4.0/DECISIONS.md +433 -0
- capforge-0.4.0/LICENSE +201 -0
- capforge-0.4.0/PKG-INFO +568 -0
- capforge-0.4.0/README.md +535 -0
- capforge-0.4.0/RESEARCH_REPORT.md +499 -0
- capforge-0.4.0/ROADMAP.md +124 -0
- capforge-0.4.0/SECURITY.md +92 -0
- capforge-0.4.0/SPEC.md +284 -0
- capforge-0.4.0/SPECIFICATION.md +277 -0
- capforge-0.4.0/benchmarks/README.md +40 -0
- capforge-0.4.0/benchmarks/acm_benchmarks.py +242 -0
- capforge-0.4.0/capforge/__init__.py +50 -0
- capforge-0.4.0/capforge/_acm.py +491 -0
- capforge-0.4.0/capforge/_crypto.py +149 -0
- capforge-0.4.0/capforge/_delegation.py +83 -0
- capforge-0.4.0/capforge/_exceptions.py +21 -0
- capforge-0.4.0/capforge/_model.py +176 -0
- capforge-0.4.0/capforge/_policy.py +341 -0
- capforge-0.4.0/capforge/_validator.py +71 -0
- capforge-0.4.0/capforge/_version.py +3 -0
- capforge-0.4.0/capforge/cli/__init__.py +1 -0
- capforge-0.4.0/capforge/cli/main.py +347 -0
- capforge-0.4.0/capforge/py.typed +0 -0
- capforge-0.4.0/capforge.egg-info/PKG-INFO +568 -0
- capforge-0.4.0/capforge.egg-info/SOURCES.txt +67 -0
- capforge-0.4.0/capforge.egg-info/dependency_links.txt +1 -0
- capforge-0.4.0/capforge.egg-info/entry_points.txt +2 -0
- capforge-0.4.0/capforge.egg-info/requires.txt +12 -0
- capforge-0.4.0/capforge.egg-info/top_level.txt +2 -0
- capforge-0.4.0/capforge_langgraph/__init__.py +36 -0
- capforge-0.4.0/capforge_langgraph/_tool_wrapper.py +219 -0
- capforge-0.4.0/capforge_langgraph/pyproject.toml +35 -0
- capforge-0.4.0/examples/cli/README.md +74 -0
- capforge-0.4.0/examples/cli/run_cli_examples.sh +151 -0
- capforge-0.4.0/examples/crewai/README.md +118 -0
- capforge-0.4.0/examples/crewai/acm_controlled_crew.py +233 -0
- capforge-0.4.0/examples/crewai/requirements.txt +2 -0
- capforge-0.4.0/examples/fastapi/README.md +123 -0
- capforge-0.4.0/examples/fastapi/acm_protected_api.py +274 -0
- capforge-0.4.0/examples/fastapi/requirements.txt +3 -0
- capforge-0.4.0/examples/github_actions/README.md +104 -0
- capforge-0.4.0/examples/github_actions/verify_acm.yml +117 -0
- capforge-0.4.0/examples/langgraph/README.md +127 -0
- capforge-0.4.0/examples/langgraph/basic_acm_agent.py +225 -0
- capforge-0.4.0/examples/langgraph/requirements.txt +3 -0
- capforge-0.4.0/examples/openai_agents/README.md +118 -0
- capforge-0.4.0/examples/openai_agents/acm_controlled_agent.py +287 -0
- capforge-0.4.0/examples/openai_agents/requirements.txt +2 -0
- capforge-0.4.0/examples/policy/README.md +96 -0
- capforge-0.4.0/examples/policy/requirements.txt +1 -0
- capforge-0.4.0/examples/policy/runtime_checks.py +168 -0
- capforge-0.4.0/pyproject.toml +78 -0
- capforge-0.4.0/setup.cfg +4 -0
- capforge-0.4.0/spec/acm-schema.json +150 -0
- capforge-0.4.0/spec/examples/expired-acm.json +17 -0
- capforge-0.4.0/spec/examples/minimal-acm.json +15 -0
- capforge-0.4.0/spec/examples/valid-acm.json +41 -0
- capforge-0.4.0/tests/__init__.py +1 -0
- capforge-0.4.0/tests/test_acm.py +431 -0
- capforge-0.4.0/tests/test_crypto.py +163 -0
- capforge-0.4.0/tests/test_langgraph_adapter.py +217 -0
- capforge-0.4.0/tests/test_model.py +61 -0
- capforge-0.4.0/tests/test_policy.py +328 -0
- 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).*
|