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.

Metadata

Release files for capforge 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for capforge 0.6.1
File Size Uploaded Provenance Actions
capforge-0.6.1.tar.gz 155.5 kB Without provenance Details for capforge-0.6.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for capforge 0.6.1
File Interpreter ABI Platform Actions
capforge-0.6.1-py3-none-any.whl Python 3 none any Details for capforge-0.6.1-py3-none-any.whl

Total release size: 225.4 kB

Release files / capforge-0.6.1.tar.gz

Download URL capforge-0.6.1.tar.gz
Size 155.5 kB
Tags Source
SHA-256 checksum
How to use checksums
eb763581b37247900cdea696a3948a9e4175e37ed6e1f20f7941ba023e057e70
BLAKE2b-256 checksum
How to use checksums
3997da595e9ccc653b5aa9df51e68393e55eccc78260188f493a0c7558c91564
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / capforge-0.6.1-py3-none-any.whl

Download URL capforge-0.6.1-py3-none-any.whl
Size 69.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
befd9c98358ba2fb29fdf6e108a8988eaeffa6f5834b1c452a13d9f94d3cd471
BLAKE2b-256 checksum
How to use checksums
15f45cb6c4d891e8a99a449c5319e8382a2ccc7212de136e36380ea4ad090411
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Security for capforge 0.6.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page