Skip to main content

CapForge

Policy-as-Code SDK for AI agents — signed capability manifests with runtime authorization.

License Python 3.12+ Ruff Mypy CI

Version: 0.6.1 | Status: Alpha

CapForge lets you wrap any AI agent tool with a signed, expiring permission slip. Before every tool invocation, the agent must present a cryptographically verifiable manifest declaring who it is, what it's allowed to do, and what limits it operates within.

pip install capforge
from capforge import ACM, Capability, Constraints

# Create a signed permission slip for your agent
manifest = ACM.create(
    spiffe_id="spiffe://org/agent/support-bot",
    sponsor="alice@org.com",
    capabilities=[Capability(resource="knowledge_base", action="read")],
)
manifest.sign("my-key.pem")

# At runtime: may this agent execute this action right now?
result = manifest.check(resource="knowledge_base", action="read")
print(result.allowed)   # True
print(result.reason)    # "Action authorized"

No framework dependencies. No external services. Works with LangGraph, CrewAI, OpenAI Agents SDK, FastAPI, or any custom agent runtime.


Why CapForge?

When you give an AI agent a tool — a database query, an API call, a shell command — how do you know the agent won't use it beyond what you intended?

Today, teams solve this ad-hoc: hardcoded API keys, fragile conditionals scattered across tool code, and manual review that doesn't scale.

CapForge answers five questions that every production agent deployment needs:

Question How CapForge Answers
Who is this agent? Cryptographically signed identity (SPIFFE ID)
What is it allowed to do? Resource:action capability pairs
Who is responsible? Human sponsor field
What are its limits? Constraints (cost, tokens, models, tools, time)
Can I verify this? Ed25519 signature + canonical JSON

When should you use CapForge?

  • You're deploying multi-agent systems in production
  • Multiple teams build agents that access shared infrastructure
  • You need audit trails for agent actions
  • Your agents interact across organizational boundaries
  • You operate in regulated environments (fintech, healthcare, government)

When should you NOT use CapForge?

  • Single-agent, no-tool setup — You don't need capability manifests for a chatbot that summarises notes
  • Simple API key scoping — If a single static key is good enough, it's simpler
  • Early prototyping — ACM shines at production scale, not during rapid iteration

Quick Start (under 5 minutes)

1. Install

pip install capforge

2. Create a manifest and check authorization

from capforge import ACM, Capability, Constraints

# This is the core value of CapForge — runtime authorization decisions
manifest = ACM.create(
    spiffe_id="spiffe://acme-corp.com/agent/support-bot",
    sponsor="alice@acme-corp.com",
    capabilities=[
        Capability(resource="knowledge_base", action="read"),
        Capability(resource="tickets", action="read"),
    ],
    constraints=Constraints(max_cost_per_session=0.05),
)

# Before every tool call: may this action execute?
result = manifest.check(resource="knowledge_base", action="read")
if result.allowed:
    print("✅ Authorized — executing tool")
    print(f"   Remaining budget: ${result.remaining_budget['cost']:.2f}")
else:
    print(f"❌ Denied: {result.reason}")
    print(f"   Failed constraint: {result.failed_constraint}")

3. Cryptographically sign and persist

capforge keygen --output ./my-agent-key
# Sign
manifest.sign("./my-agent-key.pem")
manifest.save("manifest.json")

# Later: load and verify
loaded = ACM.load("manifest.json")
loaded.verify(trusted_keys=["./my-agent-key.pub"])
print(f"Agent: {loaded.spiffe_id}")    # spiffe://acme-corp.com/agent/support-bot
print(f"Valid: {loaded.is_valid()}")   # True

4. Delegate scope to a sub-agent

child = manifest.delegate(
    child_spiffe_id="spiffe://acme-corp.com/agent/search-worker",
    capabilities=[Capability(resource="knowledge_base", action="read")],
    key="./my-agent-key.pem",
)
child.save("delegated.json")
# Child can only read — cannot write tickets or escalate scope

Framework Integrations

CapForge provides tools to protect agent execution across popular frameworks.

LangGraph

Wrap LangChain tools with ACM authorization checks:

from capforge_langgraph import ACMControlledTool, wrap_tools

wrapped = ACMControlledTool(
    tool=search_kb,
    acm=manifest,
    resource="knowledge_base",
    action="read",
)
result = wrapped.invoke({"query": "ACM protocol"})
# → ACM.check() runs before every invocation

📖 examples/langgraph/ — runnable example

CrewAI

Wrap CrewAI tools with ACM authorization:

from examples.crewai.acm_controlled_crew import ACMControlledCrewTool

safe_tool = ACMControlledCrewTool(
    tool=SearchKnowledgeBase(),
    acm=manifest,
    resource="knowledge_base",
    action="read",
)
result = safe_tool(query="ACM protocol")

📖 examples/crewai/ — runnable example

FastAPI

Protect API endpoints with ACM dependency injection:

from fastapi import Depends

@app.get("/agents/{agent_id}")
async def get_agent(
    agent_id: str,
    acm: Annotated[ACM, Depends(require_capability("agent", "read"))],
):
    """Returns agent info only if the request carries a valid ACM."""
    return {"agent_id": agent_id, "authorized_by": acm.sponsor}

📖 examples/fastapi/ — runnable server + curl test commands

OpenAI Agents SDK

Wrap function tools with ACM checks:

class ACMControlledFunctionTool:
    def __call__(self, *args, **kwargs):
        result = self.acm.check(self.resource, self.action)
        if not result.allowed:
            raise PermissionError(f"ACM denied: {result.reason}")
        return self.fn(*args, **kwargs)

📖 examples/openai_agents/ — runnable example


CLI Reference

capforge --help
capforge --version
Command Description
capforge keygen --output <prefix> Generate an Ed25519 key pair (.pem + .pub)
capforge create Create and sign an ACM document
capforge verify <file> --trusted-roots <pubkey> Verify temporal validity + cryptographic signature
capforge validate <file> Validate temporal + structural correctness (no crypto)
capforge info <file> Display human-readable ACM information
capforge delegate Create a delegated ACM with narrowed scope

CLI Example

# Generate keys
capforge keygen --output ./my-key

# Create and sign a manifest
capforge create \
  --spiffe-id spiffe://acme-corp.com/agent/support-bot \
  --sponsor alice@acme-corp.com \
  --capability knowledge_base:read \
  --capability tickets:read \
  --constraint max_cost_per_session=0.05 \
  --key-file ./my-key.pem \
  --output manifest.json

# Verify
capforge verify manifest.json --trusted-roots ./my-key.pub
# → Verification PASSED (signature valid)
# → Agent:   spiffe://acme-corp.com/agent/support-bot
# → Sponsor: alice@acme-corp.com

📖 examples/cli/ — full end-to-end CLI workflow


Python SDK API

from capforge import ACM, Capability, Constraints, PolicyDecision

# Create
manifest = ACM.create(
    spiffe_id="spiffe://org/agent/bot",
    sponsor="user@org.com",
    capabilities=[Capability(resource="kb", action="read")],
    constraints=Constraints(max_cost_per_session=0.05),
    ttl=3600,
)

# Sign & persist
manifest.sign("key.pem")
manifest.save("manifest.json")

# Load & verify
loaded = ACM.load("manifest.json")
loaded.verify(trusted_keys=["key.pub"])

# Runtime authorization
result: PolicyDecision = loaded.check(
    resource="kb", action="read",
    cost=0.01, session_cost=0.03,
    model="gpt-5",
)
print(result.allowed, result.reason, result.remaining_budget)

# Delegate scope
child = loaded.delegate(
    child_spiffe_id="spiffe://org/agent/sub",
    capabilities=[Capability(resource="kb", action="read")],
)

Full Method Reference

Method Returns Description
ACM.create(...) ACM Factory: create a new manifest
ACM.load(source) ACM Factory: load from file path or dict
manifest.sign(key) ACM Sign with Ed25519 (PEM path or key object)
manifest.verify(trusted_keys=...) None Temporal + optional crypto verification (raises on failure)
manifest.save(path) str Save JSON to file, returns JSON string
manifest.check(...) PolicyDecision Runtime authorization decision
manifest.delegate(...) ACM Create delegated ACM with narrowed scope
manifest.has_capability(r, a) bool Check if capability exists
manifest.get_capability(r, a) Capability|None Get capability by resource/action
manifest.is_valid() bool Check temporal validity window
manifest.to_dict() dict Export as dictionary
manifest.to_json() str Export as pretty-printed JSON

Error Handling

from capforge import ACMError
from capforge._exceptions import ACMExpiredError, ACMSignatureError

try:
    manifest.verify(trusted_keys=["root.pub"])
except ACMExpiredError:
    print("Manifest has expired — renew it")
except ACMSignatureError:
    print("Signature invalid — tampered or wrong key")
except ACMError:
    print("Other ACM error")

Runtime Policy Engine

Beyond simple capability lookup, ACM.check() evaluates whether an action is authorized right now considering:

Check Field Example
Temporal validity expires_at, not_before Block expired manifests
Capability presence resource:action Is knowledge_base:read granted?
Cost budget max_cost_per_session Limit spend per session
Token budget max_tokens_per_session Limit LLM token usage
Model restriction allowed_models Only allow specific models
Blocked tools disallowed_tools Forbid dangerous operations
Execution timeout max_execution_seconds Prevent runaway agents
result = manifest.check(
    resource="github", action="search",
    tool="search_code", cost=0.02,
    session_cost=0.10, session_tokens=500,
    model="gpt-5", execution_seconds=10,
)

if result.allowed:
    print(f"✅ Allowed. Budget remaining: ${result.remaining_budget['cost']:.2f}")
else:
    print(f"❌ Denied: {result.reason} (constraint: {result.failed_constraint})")

📖 examples/policy/ — 7 scenarios with expected output


CI/CD Integration

Verify ACM documents as a deployment gate in GitHub Actions:

- name: Verify ACM signature
  run: capforge verify manifests/agent-acm.json --trusted-roots manifests/trusted-roots.pub

- name: Check capabilities
  run: python -c "
from capforge import ACM;
m = ACM.load('manifest.json');
m.check('knowledge_base', 'read')
print('✅ All checks passed')
"

📖 examples/github_actions/ — full workflow


Benchmarks

Core operation performance at varying manifest sizes (100 iterations each):

Operation n=1 n=10 n=100 n=1000
Manifest load ~5 us ~10 us ~57 us ~837 us
Signature verify ~147 us ~173 us ~307 us ~1838 us
ACM.check() ~3 us ~3 us ~3 us ~3 us
Delegation ~80 us ~81 us ~89 us ~160 us

Run locally: python benchmarks/acm_benchmarks.py


FAQ

What problem does CapForge solve?

When an AI agent calls a tool in production, there is no standard way to verify the agent is authorized to make that call. CapForge provides a lightweight, cryptographically verifiable "permission slip" that agents must present before executing tools.

How is it different from RBAC / IAM?

IAM systems (AWS IAM, Kubernetes RBAC) control human or service access to resources. CapForge controls AI agent access to tools and functions within an agent runtime — a problem IAM systems don't address. ACM is complementary: you can use IAM for infrastructure access and ACM for agent tool authorization.

How does it work with MCP (Model Context Protocol)?

MCP defines how agents connect to external tools and data sources. ACM defines what each agent is allowed to do with those connections. An MCP server can require an ACM before executing a tool — the MCP handshake conveys identity, the ACM conveys authorization scope.

How does it work with LangGraph?

CapForge's LangGraph adapter (capforge-langgraph) wraps LangChain tools with ACM.check() calls. Every tool.invoke() first verifies the ACM is valid, the capability is granted, and all constraints are met — before the inner tool executes.

How does signing work?

CapForge uses Ed25519 (Curve25519) for signing. Before signing, the ACM document is canonicalized (sorted keys, compact JSON, signature field removed). The canonical bytes are signed, and the base64 signature is stored in the signature field. Verification re-canonicalizes and checks the signature against trusted public keys.

What happens when a manifest expires?

The agent can no longer execute authorized actions. The ACM.check() method returns PolicyDecision(allowed=False, reason="ACM manifest is expired"). You must issue a new ACM with a fresh expires_at. Short-lived ACMs (TTL of hours) are recommended for production.

Can I revoke a manifest before it expires?

Not directly — ACMs are self-contained documents with no central authority. Revocation is handled by:

  1. Short TTLs — Manifests expire quickly (hours, not days)
  2. Revocation lists — Maintain an external list of revoked signature keys
  3. Key rotation — Rotate trusted root keys, making old signatures invalid

What does a manifest look like?

{
  "acm_version": "1.0",
  "agent": {
    "spiffe_id": "spiffe://acme-corp.com/agent/support-bot"
  },
  "human_sponsor": "alice@acme-corp.com",
  "capabilities": [
    {"resource": "knowledge_base", "action": "read"},
    {"resource": "tickets", "action": "write",
     "constraints": {"status": "resolved_only"}}
  ],
  "issuer": "spiffe://acme-corp.com/user/alice",
  "expires_at": "2026-08-22T00:00:00Z",
  "signature": "base64_encoded_signature..."
}

Full specification: SPECIFICATION.md


Documentation

Document Purpose
SPECIFICATION.md ACM protocol specification v1.0 (canonical)
SPEC.md Protocol specification (alias)
ARCHITECTURE.md Module design, data flow, design principles
DECISIONS.md Architecture Decision Records
ROADMAP.md Phased milestones through standardization
CHANGELOG.md Version history (v0.1.0 → v0.6.1)
SECURITY.md Security policy and vulnerability reporting
CONTRIBUTING.md Contribution guidelines

Project Structure

capforge/                       # Core SDK (zero framework dependencies)
├── __init__.py                 # Public API: ACM, Capability, Constraints
├── _acm.py                     # Main ACM class
├── _model.py                   # Pydantic data models
├── _crypto.py                  # Ed25519 signing/verification
├── _policy.py                  # Runtime policy engine (ACM.check())
├── _validator.py               # Temporal + delegation validation
├── _delegation.py              # Delegation scope narrowing
├── _exceptions.py              # Exception hierarchy
├── _version.py                 # Package version (dynamic)
├── py.typed                    # PEP 561 type marker
└── cli/
    ├── __init__.py
    └── main.py                 # Click CLI (6 commands)

tests/                          # 103 tests
├── test_acm.py                 # 30 tests
├── test_crypto.py              # 14 tests
├── test_policy.py              # 34 tests
├── test_langgraph_adapter.py   # 10 tests
├── test_model.py               # 7 tests
├── test_validator.py           # 5 tests
└── test_version.py             # 3 tests

capforge_langgraph/             # LangGraph adapter (separate package)
├── __init__.py
├── _tool_wrapper.py
└── pyproject.toml

examples/                       # Production showcase
├── langgraph/                  # LangGraph integration
├── crewai/                     # CrewAI integration
├── fastapi/                    # FastAPI + ACM dependencies
├── openai_agents/              # OpenAI Agents SDK integration
├── cli/                        # CLI workflow
├── github_actions/             # CI/CD verification
└── policy/                     # Runtime policy engine

benchmarks/                     # Performance benchmarks
└── acm_benchmarks.py

spec/                           # Protocol specification
├── acm-schema.json             # JSON Schema (Draft 2020-12)
└── examples/
    ├── valid-acm.json
    ├── minimal-acm.json
    └── expired-acm.json

Progress

Area Status
Protocol specification ✅ 100%
JSON Schema ✅ 100%
Pydantic data models ✅ 100%
Ed25519 cryptography ✅ 100%
Temporal validation ✅ 100%
Delegation chain ✅ 100%
Public API (ACM class) ✅ 100%
Click CLI (6 commands) ✅ 100%
Test suite (216 tests) ✅ 100%
ruff + mypy strict ✅ 100%
CI pipeline ✅ 100%
LangGraph adapter ✅ 100%
Runtime policy engine ✅ 100%
MCP adapter ✅ 100%
Audit logging ✅ 100%
Remote policy distribution ✅ 100%
Policy server (REST API) ✅ 100%
Framework examples (6 frameworks) ✅ 100%
Performance benchmarks ✅ 100%
Repository stabilization ✅ 100%

Roadmap

Phase 1 ─── Foundation + Specification              ✓  (v0.1.0)
Phase 2 ─── Cryptography + CLI Enhancement           ✓  (v0.2.0)
Phase 3a ─ CapForge SDK                             ✓  (v0.3.0)
Phase 3b ─ LangGraph adapter                        ✓
Phase 4 ─── Runtime Policy Engine                    ✓  (v0.4.0)
Phase 5 ─── Production Showcase                     ✓  (v0.5.0)
Phase 6 ─── MCP Integration                         ✓  (v0.6.0)
Phase 7 ─── Audit Logging                           ✓  (v0.6.0)
Phase 8 ─── Remote Policy Distribution              ✓  (v0.6.0)
Phase 9 ─── Policy Server                           ✓  (v0.6.0)
Phase 9.1 ─ Repository Stabilization                ✓  (v0.6.0)
Phase 9.2 ─ Packaging Fix & CI Stabilization        ✓  (v0.6.1)  ← You are here

See ROADMAP.md for details on upcoming phases.


Contributing

See CONTRIBUTING.md for development setup, coding standards, and pull request process.


License

Apache License 2.0


Built for the AI agent ecosystem. Not another chatbot. Not another agent framework. Infrastructure for agent authorization.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

capforge-0.6.1.tar.gz (155.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

capforge-0.6.1-py3-none-any.whl (69.9 kB view details)

Uploaded Python 3

File details

Details for the file capforge-0.6.1.tar.gz.

File metadata

  • Download URL: capforge-0.6.1.tar.gz
  • Upload date:
  • Size: 155.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for capforge-0.6.1.tar.gz
Algorithm Hash digest
SHA256 eb763581b37247900cdea696a3948a9e4175e37ed6e1f20f7941ba023e057e70
MD5 189e4749646d896aa5b8f5dad48e56b8
BLAKE2b-256 3997da595e9ccc653b5aa9df51e68393e55eccc78260188f493a0c7558c91564

See more details on using hashes here.

File details

Details for the file capforge-0.6.1-py3-none-any.whl.

File metadata

  • Download URL: capforge-0.6.1-py3-none-any.whl
  • Upload date:
  • Size: 69.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for capforge-0.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 befd9c98358ba2fb29fdf6e108a8988eaeffa6f5834b1c452a13d9f94d3cd471
MD5 1261df5b99db0c04d5f0f7595c1bf522
BLAKE2b-256 15f45cb6c4d891e8a99a449c5319e8382a2ccc7212de136e36380ea4ad090411

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.6.1 This release

2 files

0.6.0

2 files

0.4.1

2 files

0.4.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page