# CoSig

[![CI](https://github.com/skyforest/cosig/actions/workflows/ci.yml/badge.svg)](https://github.com/skyforest/cosig/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/skyforest/cosig/branch/main/graph/badge.svg)](https://codecov.io/gh/skyforest/cosig)
[![PyPI version](https://badge.fury.io/py/cosig.svg)](https://badge.fury.io/py/cosig)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)

> **⚠️ ALPHA RELEASE (v0.2.0a1)**
> CoSig is in early development. APIs may change. Security audit recommended before production use.
> See [KNOWN_ISSUES.md](KNOWN_ISSUES.md) for current limitations and known issues.

**WebAuthn co-signing library for MCP servers** — require human approval via hardware token (YubiKey) for AI tool calls.

## What is CoSig?

CoSig adds a security layer to MCP (Model Context Protocol) servers. When an AI agent wants to execute a sensitive operation, the human must **co-sign** the request by tapping their hardware security key (YubiKey, Touch ID, Windows Hello).

**Use case:** Add YubiKey approval to [FastMCP](https://github.com/jlowin/fastmcp) tools using a simple decorator.

**Security guarantees:**
- ✅ **Unauthorized execution blocked** — AI can't run tools without human tap
- ✅ **Replay attacks prevented** — Each signature has a unique counter
- ✅ **Non-repudiation** — Cryptographic proof the user approved
- ✅ **Audit trail** — Every action logged
- ✅ **Enterprise-grade encryption** — AES-256-GCM with HMAC tamper detection
- ✅ **Immutable audit logs** — Blockchain-style hash chaining prevents tampering

## Demo

See CoSig in action:

[![CoSig Demo](https://img.youtube.com/vi/7AeMJ3ViV5E/0.jpg)](https://www.youtube.com/watch?v=7AeMJ3ViV5E)

Watch the video demonstration to see how YubiKey approval works with MCP servers.

## Server Requirements

**⚠️ Important:** CoSig requires a CoSig Cloud server to handle WebAuthn approvals.

You have two options:

1. **Self-hosted**: Deploy [cosig-cloud](https://github.com/skyforest/cosig-cloud) on your own infrastructure
2. **Enterprise hosting**: Contact us about CoSig Enterprise for managed hosting

The CoSig Cloud server provides:
- WebAuthn approval flow UI
- Credential verification
- Audit log storage
- API endpoints for approval status

See the [cosig-cloud repository](https://github.com/skyforest/cosig-cloud) for deployment instructions.

## Quick Start

### Installation

```bash
pip install cosig fastmcp
```

### FastMCP Integration

```python
# my_mcp_server.py
from fastmcp import FastMCP
from cosig import require_approval
import asyncpg

mcp = FastMCP("Enterprise Server")

# Dangerous operation - requires YubiKey approval
@mcp.tool()
@require_approval()
async def delete_customer_data(customer_id: str) -> str:
    """Delete customer PII - requires YubiKey tap for GDPR compliance"""
    conn = await asyncpg.connect('postgresql://...')
    await conn.execute('DELETE FROM customers WHERE id = $1', customer_id)
    await conn.close()
    return f"Deleted customer {customer_id}"

# Another sensitive operation
@mcp.tool()
@require_approval()
async def deploy_to_production(service: str, version: str) -> str:
    """Deploy to production - requires SRE approval via YubiKey"""
    result = await deploy_service(service, version, environment="production")
    return f"Deployed {service} v{version} to production"

# Safe operation - no approval needed
@mcp.tool()
async def get_customer_info(customer_id: str) -> str:
    """Read-only operation - no approval required"""
    conn = await asyncpg.connect('postgresql://...')
    data = await conn.fetchrow('SELECT * FROM customers WHERE id = $1', customer_id)
    await conn.close()
    return str(data)
```

### How It Works

1. **First call** → Tool raises `ApprovalRequiredError` with URL
2. **User opens URL** → Approval page shows tool call details
3. **User taps YubiKey** → WebAuthn verification
4. **Client retries** → Tool executes successfully

```python
# In your MCP client:
try:
    result = await mcp.call_tool("delete_customer_data", {"customer_id": "cust_123"})
except ApprovalRequiredError as e:
    print(f"Approval required: {e.approval_url}")
    # User taps YubiKey at the URL
    # Then retry:
    result = await mcp.call_tool("delete_customer_data", {"customer_id": "cust_123"})
    print(result)  # "Deleted customer cust_123"
```

## Real-World Use Cases

### Financial Services
```python
from fastmcp import FastMCP
from cosig import require_approval

mcp = FastMCP("Financial Services")

@mcp.tool()
@require_approval()
async def release_hold(account_id: str, hold_id: str) -> str:
    """Release payment hold - requires compliance officer YubiKey approval"""
    # Release hold...
    return f"Released hold {hold_id}"

@mcp.tool()
@require_approval()
async def modify_credit_limit(customer_id: str, new_limit: float) -> str:
    """Modify customer credit limit - requires manager YubiKey approval"""
    # Update limit...
    return f"Updated limit to ${new_limit}"
```

### Healthcare
```python
@mcp.tool()
@require_approval()
async def access_patient_records(patient_id: str) -> str:
    """Access PHI - requires HIPAA-compliant YubiKey approval"""
    # Log access, retrieve records...
    return "Records retrieved"

@mcp.tool()
@require_approval()
async def edit_medical_billing_code(patient_id: str, old_code: str, new_code: str) -> str:
    """Edit medical billing code - requires billing manager YubiKey approval"""
    # Update billing code...
    return f"Updated code from {old_code} to {new_code}"
```

### Infrastructure & DevOps
```python
@mcp.tool()
@require_approval()
async def scale_production_cluster(cluster_name: str, node_count: int) -> str:
    """Scale production Kubernetes cluster - requires SRE YubiKey approval"""
    # Scale cluster...
    return f"Scaled to {node_count} nodes"

@mcp.tool()
@require_approval()
async def modify_firewall_rules(rule_id: str, action: str) -> str:
    """Modify production firewall - requires security team YubiKey approval"""
    # Update firewall...
    return "Firewall updated"

@mcp.tool()
@require_approval()
async def rotate_api_keys(service_name: str) -> str:
    """Rotate production API keys - requires YubiKey approval"""
    # Rotate keys...
    return "Keys rotated"
```

### Data & Analytics
```python
@mcp.tool()
@require_approval()
async def export_customer_data(dataset_name: str, destination: str) -> str:
    """Export customer PII - requires data governance YubiKey approval"""
    # Export data...
    return "Data exported"

@mcp.tool()
@require_approval()
async def run_expensive_query(query: str) -> str:
    """Run expensive analytics query - requires YubiKey approval to prevent cost overruns"""
    # Execute query...
    return "Query completed"
```

### Higher Education Administration
```python
@mcp.tool()
@require_approval()
async def release_course_signup_hold(student_id: str, hold_id: str) -> str:
    """Release course registration hold - requires registrar YubiKey approval"""
    # Release hold in student information system...
    return f"Released hold {hold_id} for student {student_id}"

@mcp.tool()
@require_approval()
async def access_student_records(student_id: str) -> str:
    """Access student records - requires FERPA-compliant YubiKey approval"""
    # Log access, retrieve records...
    return "Student records retrieved"

@mcp.tool()
@require_approval()
async def release_student_record(student_id: str, recipient: str) -> str:
    """Release student transcript - requires registrar YubiKey approval"""
    # Release transcript to authorized recipient...
    return f"Released transcript for {student_id} to {recipient}"
```

## Configuration

Configure CoSig programmatically in your application:

```python
from cosig import configure

configure(
    cosig_url="https://cosig.example.com",     # CoSig approval server URL
    api_key="your-api-key",                     # API key for authentication
    timeout=300,                                # Approval timeout in seconds
)
```

For production deployments:
- Use HTTPS and set the CoSig URL to your domain
- Store API keys in a secrets manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault)
- See [SECURITY.md](SECURITY.md) for enterprise security architecture details

## How It Works

```
┌─────────────┐     1. Create Plan      ┌─────────────┐
│   AI Agent  │ ───────────────────────▶│   CoSig     │
│             │                         │             │
│ "Delete     │                         │ Plan:       │
│  customer   │                         │ - Action    │
│  cust_123"  │                         │ - Status:   │
└─────────────┘                         │   PENDING   │
                                        └──────┬──────┘
                                               │
                                     2. Challenge
                                               ▼
┌─────────────┐     3. Tap YubiKey     ┌─────────────┐
│    Human    │ ◀──────────────────────│   Browser   │
│ (Compliance │                        │             │
│  Officer)   │                        │ "Approve    │
└──────┬──────┘                        │  deletion?" │
       │                               └─────────────┘
       │ 4. Signed Credential
       ▼
┌─────────────┐     5. Execute         ┌─────────────┐
│   CoSig     │ ───────────────────────▶│  Database   │
│             │                         │             │
│ Verifies:   │                         │ DELETE FROM │
│ - Signature │                         │ customers   │
│ - Counter   │                         │ WHERE...    │
│ - Binding   │                         └─────────────┘
└─────────────┘
       │
       │ 6. Audit Log
       ▼
┌─────────────────────────────────────────────────────┐
│ Audit Trail                                         │
│ [2025-12-22 10:00:00] alice approved deletion      │
│ [2025-12-22 10:00:01] Deleted customer cust_123    │
│ YubiKey: credential_abc123, sign_count: 42         │
└─────────────────────────────────────────────────────┘
```

## Why CoSig?

### For Compliance Teams
- **GDPR/CCPA**: Prove human approved data deletion
- **SOX**: Audit trail for financial operations
- **HIPAA**: Log PHI access with non-repudiable proof
- **PCI-DSS**: Require approval for sensitive operations

### For Security Teams
- **Zero Trust**: AI can't act without human approval
- **Phishing-resistant**: Hardware token can't be stolen remotely
- **Replay prevention**: Each signature is unique
- **Audit trail**: Every action logged with cryptographic proof

### For DevOps Teams
- **Production safety**: Prevent accidental deployments
- **Cost control**: Approve expensive operations
- **Change management**: Require approval for infrastructure changes

## Development

```bash
# Clone the repo
git clone https://github.com/skyforest/cosig
cd cosig

# Install dev dependencies
make dev

# Run tests
make test

# Lint and type check
make lint

# Format code
make format
```

## Requirements

- Python 3.12+
- FIDO2 hardware key (YubiKey 5, Titan, SoloKey, etc.)
- Modern browser with WebAuthn support

## License

BSD-3-Clause - See [LICENSE](LICENSE) for details.
