Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,27 @@ pip install capiscio-sdk==0.1.0

## [Unreleased]

### Planned for v0.2.0
- Signature verification (crypto validation)
- Agent card validation
- Upstream agent testing
- Integration tests
- End-to-end tests
- Performance benchmarks
## [0.3.0] - 2025-11-22
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The CHANGELOG lists the release date as "2025-11-22" (November 2025), but the current date context indicates it's November 2025. This appears to be a future date used during development. Consider updating this to the actual release date when the PR is merged, or use "Unreleased" until an official release is made.

Suggested change
## [0.3.0] - 2025-11-22
## [0.3.0] - Unreleased

Copilot uses AI. Check for mistakes.

### Added
- **SimpleGuard Security Strategy**:
- **Identity**: Ed25519 JWS signature verification (`X-Capiscio-JWS` header).
- **Integrity**: SHA-256 Body Hash verification (`bh` claim) to prevent payload tampering.
- **Freshness**: Replay protection using `exp` (expiration) and `iat` (issued at) claims with a 60-second window.
- **Zero Config**: Secure by default with minimal setup.
- **FastAPI Integration**:
- `CapiscioMiddleware`: Automatic request validation and identity injection into `request.state.agent_id`.
- `Server-Timing` header support for telemetry (verification time).
- **Telemetry**:
- Added `dur` (duration) metric to `Server-Timing` header for monitoring security overhead.
- **Documentation**:
- Updated `README.md` with "Enforcement First" strategy.
- Updated `SECURITY.md` with threat model and verification steps.
- Added `examples/secure_ping_pong` demo.

### Changed
- **Breaking Change**: Shifted from "Validation" focus to "Enforcement" focus.
- Updated `pyproject.toml` dependencies to include `cryptography` and `pyjwt`.

### Planned for v1.0.0
- Full A2A v1.0 compliance
Expand Down
194 changes: 47 additions & 147 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,182 +1,82 @@
# CapiscIO SDK (Python)

**Runtime security middleware for A2A (Agent-to-Agent) protocol agents**
**Enforcement-First Security for A2A Agents.**

[![PyPI version](https://badge.fury.io/py/capiscio-sdk.svg)](https://badge.fury.io/py/capiscio-sdk)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

## What is CapiscIO SDK?
**CapiscIO** is the "Customs Officer" for your AI Agent. It provides military-grade Identity and Integrity enforcement for the [Agent-to-Agent (A2A) Protocol](https://github.com/google/A2A) with **zero configuration**.

CapiscIO SDK provides **always-on runtime protection** for agents using the [A2A (Agent-to-Agent) protocol](https://github.com/google/A2A). It wraps your agent executor to validate incoming requests, verify signatures, and protect against malicious actors—all without requiring peer cooperation.
## 🚀 The 60-Second Upgrade

### Key Features

- ✅ **Message validation** - Schema and protocol compliance checking
- ✅ **Signature verification** - JWS/JWKS cryptographic validation (RFC 7515)
- ✅ **Upstream protection** - Validate agents you call
- ✅ **Downstream protection** - Validate agents calling you
- ✅ **Rate limiting** - Token bucket algorithm
- ✅ **Caching** - Performance-optimized validation results
- ✅ **Three integration patterns** - Minimal, explicit, or decorator

## Installation

```bash
pip install capiscio-sdk
```

## Quick Start


### Pattern 1: Minimal (One-liner with Preset)
Turn any FastAPI application into a Verified A2A Agent in 3 lines of code.

```python
from capiscio_sdk import secure, SecurityConfig
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.tasks import InMemoryTaskStore

# Wrap your agent with security (production defaults)
agent = secure(MyAgentExecutor(), SecurityConfig.production())

# Use in A2A request handler
handler = DefaultRequestHandler(
agent_executor=agent,
task_store=InMemoryTaskStore()
)

# Access validation results (three-dimensional scoring)
result = await agent.validate_agent_card(card_url)
print(result.compliance.total, result.trust.total, result.availability.total)
```
from fastapi import FastAPI
from capiscio_sdk.simple_guard import SimpleGuard
from capiscio_sdk.integrations.fastapi import CapiscioMiddleware

### Pattern 2: Granular Control
# 1. Initialize Guard (Auto-generates keys in dev_mode)
guard = SimpleGuard(dev_mode=True)

```python
from capiscio_sdk import CapiscIOSecurityExecutor, SecurityConfig

# Start with a preset, customize what matters to you
config = SecurityConfig.production()
config.downstream.rate_limit_requests_per_minute = 100 # Higher rate limit
config.downstream.require_signatures = True # Enforce signatures
config.upstream.test_endpoints = True # Test before calling
config.fail_mode = "monitor" # Log but don't block yet

secure_agent = CapiscIOSecurityExecutor(
delegate=MyAgentExecutor(),
config=config
)
```
app = FastAPI()

### Pattern 3: Environment-Driven (12-Factor App)
# 2. Add Enforcement Middleware
app.add_middleware(CapiscioMiddleware, guard=guard)

```python
from capiscio_sdk import secure_agent, SecurityConfig
from a2a import AgentExecutor, RequestContext, EventQueue

@secure_agent(config=SecurityConfig.from_env())
class MyAgentExecutor(AgentExecutor):
async def execute(self, context: RequestContext, event_queue: EventQueue):
# Your agent logic - config loaded from env vars
pass

# Already secured - use directly!
handler = DefaultRequestHandler(agent_executor=MyAgentExecutor())
@app.post("/agent/task")
async def handle_task(request: Request):
Comment on lines +16 to +29
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing import statement: The example code uses Request on line 29 but doesn't import it from FastAPI. The import line should include: from fastapi import FastAPI, Request

Copilot uses AI. Check for mistakes.
# 🔒 Only reachable if Identity + Integrity are verified
caller = request.state.agent_id
return {"status": "accepted", "verified_caller": caller}
```

**All 16 configuration options documented in the [Configuration Guide](https://docs.capisc.io/sdk-python/guides/configuration/).**

## Why CapiscIO?

### The Problem
## 🛡️ What You Get (Out of the Box)

When building A2A agents, you face security risks from:
- **Malicious downstream agents** sending invalid/malicious requests
- **Broken upstream dependencies** with invalid agent cards
- **Protocol violations** causing runtime failures
- **Missing signatures** with no authenticity verification
1. **Zero-Config Identity**:
* Auto-generates **Ed25519** keys and `agent-card.json` on first run.
* No manual key management required for development.

### The Solution
2. **Payload Integrity**:
* Enforces **SHA-256 Body Hash (`bh`)** verification.
* Blocks tampered payloads instantly (returns `403 Forbidden`).

CapiscIO wraps your agent executor and provides:
3. **Replay Protection**:
* Enforces strict **60-second** token expiration (`exp`).
* Prevents replay attacks and ensures freshness.

1. **Downstream Protection** - Validates all incoming requests
2. **Upstream Protection** - Validates agents you call
3. **Always-On** - Works without peer cooperation
4. **Performance** - Caching and parallel validation
5. **Three-Dimensional Scoring** - Compliance, trust, and availability insights
4. **Performance Telemetry**:
* Adds `<1ms` overhead.
* Includes `Server-Timing` headers for transparent monitoring.

## Configuration

### Presets

```python
# Development - Permissive, verbose logging
SecurityConfig.development()
## Installation

# Production - Balanced (default)
SecurityConfig.production()
```bash
pip install capiscio-sdk
```

# Strict - Maximum security
SecurityConfig.strict()
## How It Works

# From environment variables
SecurityConfig.from_env()
```
### 1. The Handshake
CapiscIO enforces the **A2A Trust Protocol**:
* **Sender**: Signs the request body (JWS + Body Hash).
* **Receiver**: Verifies the signature and re-hashes the body to ensure integrity.

### Custom Configuration
### 2. The "Customs Officer"
The `SimpleGuard` acts as a local authority. It manages your agent's "Passport" (Agent Card) and verifies the "Visas" (Tokens) of incoming requests.

```python
from capiscio_sdk import SecurityConfig, DownstreamConfig, UpstreamConfig

config = SecurityConfig(
downstream=DownstreamConfig(
validate_schema=True,
verify_signatures=True,
require_signatures=False,
enable_rate_limiting=True,
rate_limit_requests_per_minute=100
),
upstream=UpstreamConfig(
validate_agent_cards=True,
verify_signatures=True,
cache_validation=True,
cache_timeout=3600 # seconds
),
fail_mode="block", # "block" | "monitor" | "log"
timeout_ms=5000
)
### 3. Telemetry
Every response includes a `Server-Timing` header showing exactly how fast the verification was:
```http
Server-Timing: capiscio-auth;dur=0.618;desc="CapiscIO Verification"
```

## Documentation

- [Quickstart Guide](docs/quickstart.md)
- [Configuration Reference](docs/configuration.md)
- [API Documentation](docs/api-reference.md)
- [Examples](examples/)

## Roadmap

- **V1.0** (Q4 2025) - Core middleware (this package)
- **V2.0** (Q2 2026) - Extension protocol (validation feedback)
- **V3.0** (Q3 2026) - Platform integration (trust network)
- **V4.0** (Q4 2026) - Enterprise features (policies, audit logs)

## Contributing

We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
- [Official Documentation](https://docs.capisc.io)
- [A2A Protocol Spec](https://github.com/google/A2A)

## License

Apache License 2.0 - see [LICENSE](LICENSE) for details.

## About A2A

The [Agent-to-Agent (A2A) protocol](https://github.com/google/A2A) is an open standard for agent interoperability, supported by Google and 50+ partners including Salesforce, ServiceNow, SAP, Intuit, and more. CapiscIO provides the security layer for production A2A deployments.

## Support

- **Issues:** [GitHub Issues](https://github.com/capiscio/capiscio-sdk-python/issues)
- **Discussions:** [GitHub Discussions](https://github.com/capiscio/capiscio-sdk-python/discussions)
- **Documentation:** [docs.capisc.io](https://docs.capisc.io)
- **Website:** [capisc.io](https://capisc.io)
3 changes: 2 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

| Version | Supported |
| ------- | ------------------ |
| 0.1.x | :white_check_mark: |
| 0.2.x | :white_check_mark: |
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SECURITY.md file marks version 0.2.x as supported, but this PR bumps the version to 0.3.0 (as seen in pyproject.toml). The supported versions table should be updated to reflect that 0.3.x is now the supported version, and 0.2.x status should be clarified (either supported or deprecated).

Suggested change
| 0.2.x | :white_check_mark: |
| 0.3.x | :white_check_mark: |
| 0.2.x | :warning: (Deprecated, security fixes only) |

Copilot uses AI. Check for mistakes.
| 0.1.x | :x: |

## Reporting a Vulnerability

Expand Down
2 changes: 2 additions & 0 deletions capiscio_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

# Core exports
from .executor import CapiscioSecurityExecutor, secure, secure_agent
from .simple_guard import SimpleGuard
from .config import SecurityConfig, DownstreamConfig, UpstreamConfig
from .errors import (
CapiscioSecurityError,
Expand All @@ -25,6 +26,7 @@
__all__ = [
"__version__",
"CapiscioSecurityExecutor",
"SimpleGuard",
"secure",
"secure_agent",
"SecurityConfig",
Expand Down
10 changes: 10 additions & 0 deletions capiscio_sdk/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,13 @@ class CapiscioTimeoutError(CapiscioSecurityError):
"""Operation timed out."""

pass


class ConfigurationError(CapiscioSecurityError):
"""Missing keys or invalid paths (SimpleGuard)."""
pass


class VerificationError(CapiscioSecurityError):
"""Invalid signature, expired token, or untrusted key (SimpleGuard)."""
pass
73 changes: 73 additions & 0 deletions capiscio_sdk/integrations/fastapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""FastAPI integration for Capiscio SimpleGuard."""
from typing import Callable, Awaitable, Any, Dict
try:
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from starlette.types import ASGIApp
except ImportError:
raise ImportError("FastAPI/Starlette is required for this integration. Install with 'pip install fastapi'.")

from ..simple_guard import SimpleGuard
from ..errors import VerificationError
import time

class CapiscioMiddleware(BaseHTTPMiddleware):
"""
Middleware to enforce A2A identity verification on incoming requests.
"""
def __init__(self, app: ASGIApp, guard: SimpleGuard) -> None:
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The __init__ method of CapiscioMiddleware lacks a docstring. Public methods should have docstrings that explain parameters and behavior. Consider adding a docstring that documents the guard parameter and explains the middleware's purpose.

Suggested change
def __init__(self, app: ASGIApp, guard: SimpleGuard) -> None:
def __init__(self, app: ASGIApp, guard: SimpleGuard) -> None:
"""
Initialize the CapiscioMiddleware.
Args:
app (ASGIApp): The ASGI application to wrap.
guard (SimpleGuard): Instance of SimpleGuard used to verify incoming requests.
This middleware enforces A2A identity verification on incoming requests using the provided guard.
"""

Copilot uses AI. Check for mistakes.
super().__init__(app)
self.guard = guard

async def dispatch(
self,
request: Request,
call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The dispatch method lacks a docstring. This is the main method of the middleware and should document its behavior, including:

  • What it does (verifies incoming requests)
  • Parameters (request, call_next)
  • Return value
  • Exceptions or error responses (401 for missing auth, 403 for invalid auth)
  • Side effects (modifies request.state)
Suggested change
) -> Response:
) -> Response:
"""
Verifies incoming requests for A2A identity using Capiscio SimpleGuard.
Parameters:
request (Request): The incoming HTTP request.
call_next (Callable[[Request], Awaitable[Response]]): The next middleware or endpoint handler.
Returns:
Response: The HTTP response, either an error (401/403) or the result of the downstream handler.
Error Responses:
- 401: If the X-Capiscio-JWS header is missing.
- 403: If the authentication/verification fails.
Side Effects:
- Modifies request.state by injecting agent claims and agent_id if verification succeeds.
- Resets the request body receive channel for downstream handlers.
"""

Copilot uses AI. Check for mistakes.
# Allow health checks or public endpoints if needed
# For now, we assume everything under /agent/ needs protection
# But let's just check for the header.

if request.method == "OPTIONS":
return await call_next(request)

auth_header = request.headers.get("X-Capiscio-JWS")

# If no header, we might let it pass but mark as unverified?
# The mandate says: "Returns 401 (missing) or 403 (invalid)."
if not auth_header:
return JSONResponse(
{"error": "Missing X-Capiscio-JWS header. This endpoint is protected by CapiscIO."},
status_code=401
)
Comment on lines +40 to +43
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation detected on lines 40-42. These lines have extra indentation (one extra space) compared to the surrounding code at the same level. They should align with the if statement on line 39.

Suggested change
return JSONResponse(
{"error": "Missing X-Capiscio-JWS header. This endpoint is protected by CapiscIO."},
status_code=401
)
return JSONResponse(
{"error": "Missing X-Capiscio-JWS header. This endpoint is protected by CapiscIO."},
status_code=401
)

Copilot uses AI. Check for mistakes.

start_time = time.perf_counter()
try:
# Read the body for integrity check
body_bytes = await request.body()

# Verify the JWS with body
payload = self.guard.verify_inbound(auth_header, body=body_bytes)

# Reset the receive channel so downstream can read the body
async def receive() -> Dict[str, Any]:
return {"type": "http.request", "body": body_bytes, "more_body": False}
request._receive = receive
Comment on lines +53 to +56
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing the private attribute request._receive is fragile and not part of the public API. This implementation detail may break with future Starlette/FastAPI versions. Consider using a more robust approach, such as storing the body in request.state for downstream handlers to access if needed, or using Starlette's built-in body caching mechanisms.

Suggested change
# Reset the receive channel so downstream can read the body
async def receive() -> Dict[str, Any]:
return {"type": "http.request", "body": body_bytes, "more_body": False}
request._receive = receive
# Store the body in request.state for downstream access
request.state.body_bytes = body_bytes
# Downstream handlers can access the body via request.state.body_bytes

Copilot uses AI. Check for mistakes.

# Inject claims into request.state
request.state.agent = payload
request.state.agent_id = payload.get("iss")

except VerificationError as e:
return JSONResponse({"error": f"Access Denied: {str(e)}"}, status_code=403)

verification_duration = (time.perf_counter() - start_time) * 1000

response = await call_next(request)

# Add Server-Timing header (standard for performance metrics)
# Syntax: metric_name;dur=123.4;desc="Description"
response.headers["Server-Timing"] = f"capiscio-auth;dur={verification_duration:.3f};desc=\"CapiscIO Verification\""

return response
Loading
Loading