Metadata-Version: 2.4
Name: autopil
Version: 0.1.2
Summary: Platform Intelligence Layer — context governance for autonomous agents
Author-email: Anil Solleti <anil@vibrantcapital.ai>
License: Business Source License 1.1
        
        Parameters
        
        Licensor:             Anil Solleti / VibrantCapital.ai
        Licensed Work:        AutoPIL 0.1.0 and later versions
                              The Licensed Work is (c) 2026 Anil Solleti / VibrantCapital.ai
        Additional Use Grant: You may use the Licensed Work for any purpose other than
                              a Production Use that is a Competing Use. A "Competing Use"
                              means offering the Licensed Work or a substantially similar
                              product as a commercial managed service or SaaS product to
                              third parties.
        Change Date:          2030-03-25
        Change License:       MIT License
        
        For information about alternative licensing arrangements, contact:
        anil@vibrantcapital.ai
        
        ---
        
        Business Source License 1.1
        
        Terms
        
        The Licensor hereby grants you the right to copy, modify, create derivative
        works, redistribute, and make non-production use of the Licensed Work. The
        Licensor may make an Additional Use Grant, above, permitting limited production
        use.
        
        Effective on the Change Date, or the fourth anniversary of the first publicly
        available distribution of a specific version of the Licensed Work under this
        License, whichever comes first, the Licensor hereby grants you rights under
        the terms of the Change License, and the rights granted in the paragraph
        above terminate.
        
        If your use of the Licensed Work does not comply with the requirements
        currently in effect as described in this License, you must purchase a
        commercial license from the Licensor, its affiliated entities, or authorized
        resellers, or you must refrain from using the Licensed Work.
        
        All copies of the original and modified Licensed Work, and derivative works
        of the Licensed Work, are subject to this License. This License applies
        separately for each version of the Licensed Work and the Change Date may vary
        for each version of the Licensed Work released by Licensor.
        
        You must conspicuously display this License on each original or modified copy
        of the Licensed Work. If you receive the Licensed Work in original or modified
        form from a third party, the terms and conditions set forth in this License
        apply to your use of that work.
        
        Any use of the Licensed Work in violation of this License will automatically
        terminate your rights under this License for the current and all future
        versions of the Licensed Work.
        
        This License does not grant you any right in any trademark or logo of Licensor
        or its affiliates (provided that you may use a trademark or logo of Licensor
        as expressly required by this License).
        
        TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
        AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
        EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
        TITLE.
        
Project-URL: Homepage, https://autopil.ai
Project-URL: Repository, https://github.com/anil-solleti/autopil
Project-URL: Issues, https://github.com/anil-solleti/autopil/issues
Project-URL: Changelog, https://github.com/anil-solleti/autopil/releases
Keywords: ai,agents,governance,context,policy,langgraph,langchain,llamaindex,bedrock,gemini,openai,mcp,model-context-protocol,audit,audit-trail,audit-log,enterprise,compliance,ai-governance,policy-enforcement,access-control,hipaa,finra,soc2,regulated-industries,financial-services,healthcare,insurance,agentic-ai,llm,generative-ai
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Healthcare Industry
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Security
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Office/Business
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyyaml>=6.0
Provides-Extra: server
Requires-Dist: fastapi>=0.115; extra == "server"
Requires-Dist: uvicorn>=0.30; extra == "server"
Provides-Extra: langgraph
Requires-Dist: langgraph>=0.2; extra == "langgraph"
Requires-Dist: langchain-core>=0.3; extra == "langgraph"
Requires-Dist: langchain-community>=0.3; extra == "langgraph"
Provides-Extra: postgres
Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
Provides-Extra: otel
Requires-Dist: opentelemetry-sdk>=1.24; extra == "otel"
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.24; extra == "otel"
Provides-Extra: otel-http
Requires-Dist: opentelemetry-sdk>=1.24; extra == "otel-http"
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.24; extra == "otel-http"
Provides-Extra: catalog-unity
Requires-Dist: databricks-sdk>=0.20; extra == "catalog-unity"
Requires-Dist: cryptography>=42.0; extra == "catalog-unity"
Provides-Extra: catalog-collibra
Requires-Dist: requests>=2.32; extra == "catalog-collibra"
Requires-Dist: cryptography>=42.0; extra == "catalog-collibra"
Provides-Extra: catalog-alation
Requires-Dist: requests>=2.32; extra == "catalog-alation"
Requires-Dist: cryptography>=42.0; extra == "catalog-alation"
Provides-Extra: catalog-purview
Requires-Dist: msal>=1.28; extra == "catalog-purview"
Requires-Dist: requests>=2.32; extra == "catalog-purview"
Requires-Dist: cryptography>=42.0; extra == "catalog-purview"
Provides-Extra: catalog-informatica
Requires-Dist: requests>=2.32; extra == "catalog-informatica"
Requires-Dist: cryptography>=42.0; extra == "catalog-informatica"
Provides-Extra: catalog-immuta
Requires-Dist: requests>=2.32; extra == "catalog-immuta"
Requires-Dist: cryptography>=42.0; extra == "catalog-immuta"
Provides-Extra: catalog-datahub
Requires-Dist: requests>=2.32; extra == "catalog-datahub"
Requires-Dist: cryptography>=42.0; extra == "catalog-datahub"
Provides-Extra: catalog
Requires-Dist: databricks-sdk>=0.20; extra == "catalog"
Requires-Dist: msal>=1.28; extra == "catalog"
Requires-Dist: requests>=2.32; extra == "catalog"
Requires-Dist: cryptography>=42.0; extra == "catalog"
Provides-Extra: all
Requires-Dist: fastapi>=0.115; extra == "all"
Requires-Dist: uvicorn>=0.30; extra == "all"
Requires-Dist: langgraph>=0.2; extra == "all"
Requires-Dist: langchain-core>=0.3; extra == "all"
Requires-Dist: langchain-community>=0.3; extra == "all"
Requires-Dist: psycopg2-binary>=2.9; extra == "all"
Requires-Dist: opentelemetry-sdk>=1.24; extra == "all"
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.24; extra == "all"
Requires-Dist: databricks-sdk>=0.20; extra == "all"
Requires-Dist: msal>=1.28; extra == "all"
Requires-Dist: requests>=2.32; extra == "all"
Requires-Dist: cryptography>=42.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-rerunfailures>=13.0; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: fastapi>=0.115; extra == "dev"
Requires-Dist: uvicorn>=0.30; extra == "dev"
Requires-Dist: opentelemetry-sdk>=1.24; extra == "dev"
Requires-Dist: cryptography>=42.0; extra == "dev"
Requires-Dist: requests>=2.32; extra == "dev"
Dynamic: license-file

# AutoPIL — Platform Intelligence Layer

<!-- mcp-name: io.github.anil-solleti/autopil -->

Context governance middleware for autonomous AI agents.

AutoPIL sits between your agents and your data sources, enforcing who can retrieve what, under what conditions — and logging every access decision with an immutable audit trail.

```
Agent → @guard.protect(...) → Policy Engine → Data Source
                    ↓                ↓
              Audit Log       AlertEngine → webhook / email
                    ↓
              OTEL Spans + Metrics → Datadog / Grafana / Splunk
```

---

## Why AutoPIL

As enterprises deploy multi-agent AI systems, the gap that emerges isn't in the LLMs — it's in what context they're allowed to retrieve. Most governance tools operate at the output layer (content filtering, hallucination detection). AutoPIL operates at the **retrieval layer**, before sensitive data ever enters the agent's context window.

Key problems it solves:

- An underwriting agent accessing another customer's credit data
- A customer service bot retrieving internal risk models it has no business seeing
- One agent in a pipeline hijacking another agent's session context
- No audit trail of what data was retrieved and when
- No alerting when denial spikes or policy violations occur
- No observability in existing monitoring tooling

---

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│                       Your Agent                        │
└────────────────────────┬────────────────────────────────┘
                         │  @guard.protect(...)
┌────────────────────────▼────────────────────────────────┐
│                    ContextGuard                         │
│  1. Cross-agent session isolation check                 │
│  2. Policy evaluation (allow / deny)          ◄──────── PolicyEngine
│  3. OTEL span + metrics emitted                         │
│  4. Execute retrieval (if allowed)                      │
│  5. Hash context for provenance                         │
│  6. Record immutable audit event              ──────────►  AlertEngine
└──────────────┬────────────────────────┬─────────────────┘
               │                        │
┌──────────────▼──────┐   ┌─────────────▼───────────────┐
│   Storage Backend   │   │   OTEL Collector             │
│   SQLite (default)  │   │   Datadog / Grafana / Splunk │
│   Postgres (prod)   │   └─────────────────────────────┘
└─────────────────────┘
```

**Enforcement mechanisms:**

| Mechanism | Description |
|---|---|
| Source allowlist | Agent can only access explicitly permitted data sources |
| Source denylist | Certain sources are always blocked, regardless of allowlist |
| Sensitivity ceiling | Each role has a maximum data sensitivity level it can access |
| Task scoping | Roles can be restricted to specific task types (`allowed_tasks` / `denied_tasks`) |
| Cross-agent isolation | A session belongs to the first agent that used it — others are denied |
| Session lifecycle | Sessions expire by TTL or can be revoked; expired/revoked sessions are denied |
| Default deny | No matching policy = access denied |

**Integration modes:**

| Mode | How it works | Best for |
|---|---|---|
| Python decorator | `@guard.protect(...)` wraps retrieval functions directly | LangGraph, LangChain, custom Python agents |
| REST API | `POST /v1/context/evaluate` from any language | Polyglot stacks, TypeScript, Java, Go |
| MCP server | `evaluate_context` / `record_action` as native MCP tools | Claude Desktop, GPT-4, Gemini, any MCP-compatible agent |
| FastAPI / ASGI middleware | `AutoPILMiddleware` intercepts requests at the API layer | Enforcing governance without touching agent code |
| AWS Bedrock Agents | `BedrockGuard.wrap_invoke_agent()` | Bedrock-hosted agents |
| LangChain / LangGraph | `LangChainGuard.wrap_tool()` | LangChain tools and graphs |
| LlamaIndex | `LlamaIndexGuard.wrap_query_engine()` | LlamaIndex query engines |
| OpenAI Agents SDK | `OpenAIAgentsGuard` — `@guard.function_tool()` decorator | OpenAI Assistants and function tools |
| Gemini | `GeminiGuard.call_if_allowed()` | Gemini-based agents |

**Integration channels — `source_type` values:**

| Channel | Class | `source_type` |
|---|---|---|
| REST API | `ContextGuard` + FastAPI | `rest` |
| MCP | `MCPGuard` / mcp server | `mcp` |
| Python SDK | `ContextGuard` | `sdk` |
| FastAPI / ASGI | `AutoPILMiddleware` | `api` |
| AWS Bedrock Agents | `BedrockGuard` | `bedrock` |
| LangChain / LangGraph | `LangChainGuard` | `langchain` |
| LlamaIndex | `LlamaIndexGuard` | `llamaindex` |
| OpenAI Agents SDK | `OpenAIAgentsGuard` | `openai_agents` |
| Gemini | `GeminiGuard` | `gemini` |

Every audit event is stamped with the `source_type` of the channel that produced it. The `/v1/audit/stats` endpoint returns a `by_source_type` breakdown showing totals, allowed, and denied counts per channel.

---

## Quickstart

### Install

```bash
pip install -e .
```

For the REST server:

```bash
pip install -e ".[server]"
```

For OpenTelemetry (gRPC — Datadog, Splunk):

```bash
pip install -e ".[otel]"
```

For OpenTelemetry (HTTP — Grafana Cloud):

```bash
pip install -e ".[otel-http]"
```

For the MCP server (Claude Desktop, GPT-4, Gemini, any MCP-compatible agent):

```bash
pip install -e ".[mcp]"
```

For Postgres:

```bash
pip install -e ".[postgres]"
```

### Define a policy

```yaml
# policies/my_policy.yaml
policies:
  - name: analyst_policy
    agent_role: analyst
    allowed_sources:
      - reports
      - market_data
    denied_sources:
      - executive_comms
    max_sensitivity: high
    allowed_tasks:       # optional — restrict to specific task types
      - summarization
      - qa
    denied_tasks:        # optional — block specific task types
      - extraction
```

### Wrap your retrieval functions

```python
from autopil import ContextGuard, SensitivityLevel

guard = ContextGuard(
    policy_path="policies/my_policy.yaml",
    audit_db="autopil.db",
)

@guard.protect(
    agent_role="analyst",
    user_id="user_123",
    source_id="reports",
    sensitivity_level=SensitivityLevel.HIGH,
    session_id="session_abc",
)
def retrieve_reports(query: str) -> list:
    return vectorstore.search(query)

# Authorized — returns results
results = retrieve_reports("Q3 performance")

# Unauthorized — raises PermissionError, logs the denial
results = retrieve_reports_from_exec_comms("board notes")
```

For async agents, use `protect_async` instead:

```python
@guard.protect_async(
    agent_role="analyst",
    user_id="user_123",
    source_id="reports",
    sensitivity_level=SensitivityLevel.HIGH,
    session_id="session_abc",
)
async def retrieve_reports_async(query: str) -> list:
    return await vectorstore.asearch(query)
```

`protect_async` uses `contextvars.ContextVar` for `last_event_id`, so concurrent asyncio Tasks never overwrite each other's state.

### View the audit trail

```python
events = guard.get_audit_trail("session_abc")
for e in events:
    print(e.decision, e.source_id, e.context_hash)
```

---

## Framework Integrations

### LangChain

```python
from autopil import LangChainGuard

guard = LangChainGuard(policy_path="policies/", audit_db="autopil.db")
safe_tool = guard.wrap_tool(my_langchain_tool, agent_role="analyst", source_id="reports")
```

### LlamaIndex

```python
from autopil import LlamaIndexGuard

guard = LlamaIndexGuard(policy_path="policies/", audit_db="autopil.db")
safe_engine = guard.wrap_query_engine(query_engine, agent_role="analyst", source_id="docs")
result = await safe_engine.aquery("Q3 summary")
```

### AWS Bedrock Agents

```python
from autopil import BedrockGuard

guard = BedrockGuard(policy_path="policies/", audit_db="autopil.db")
safe_invoke = guard.wrap_invoke_agent(agent_role="analyst", source_id="bedrock_kb")
response = safe_invoke(agentId=..., sessionId=..., inputText="...")
```

### FastAPI / ASGI Middleware

```python
from autopil import AutoPILMiddleware, RouteRule

app.add_middleware(
    AutoPILMiddleware,
    guard=guard,
    rules=[RouteRule(path="/v1/retrieve/{doc_id}", agent_role="analyst",
                     source_id="reports", sensitivity_level="high")],
)
```

Denied requests receive a `403` JSON response. Set `on_deny="log"` on a `RouteRule` for shadow/dry-run mode — the request passes through but the denial is still recorded.

---

## Running the API Server

### First start

On first start, AutoPIL creates a `default` tenant and prints a one-time superadmin API key:

```
  AutoPIL API Server
  Policy   : policies/
  Database : sqlite @ autopil_audit.db

  ⚠  First start — created 'default' tenant and superadmin key:
     apl_<your-key-here>
     Save this — it will not be shown again.

  Docs     : http://localhost:8000/docs
```

Use this key in the `X-API-Key` header for all API requests.

### Local development (two terminals)

**Terminal 1 — backend:**
```bash
cd packages/core
python3.11 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
python3 serve.py --policy policies/
```

**Terminal 2 — dashboard:**
```bash
cd packages/dashboard
npm install
npm run dev
```

Open `http://localhost:5173` for the dashboard (Vite proxies API calls to `:8000` automatically).
Open `http://localhost:8000/docs` for the interactive API docs.

### With OpenTelemetry

```bash
export OTEL_SERVICE_NAME=autopil
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.mycompany.com:4317
python3 serve.py --policy policies/
```

The server prints `OTEL: <endpoint>` on startup when OTEL is configured.

### With Postgres

```bash
export DATABASE_URL=postgresql://user:pass@localhost:5432/autopil
python serve.py --policy policies/
```

### Docker

```bash
docker compose up --build
```

The server starts on `http://localhost:8000`.

- **Dashboard**: http://localhost:8000
- **API docs**: http://localhost:8000/docs

---

## Alerting

AutoPIL includes a rules engine that monitors the audit log and fires notifications when anomalies are detected. Rules are created via the REST API and evaluated synchronously on every audit event.

### Rule types

| Type | Fires when |
|---|---|
| `denial_spike` | X denials occur within N minutes |
| `new_source` | A `source_id` is accessed that has never appeared in the audit log before |
| `isolation_violation` | A cross-agent session isolation violation is recorded |
| `high_deny_rate` | Denial percentage exceeds a threshold within a time window |

### Create an alert rule

```bash
curl -X POST http://localhost:8000/v1/alerts/rules \
  -H "X-API-Key: apl_yourkey" \
  -H "Content-Type: application/json" \
  -d '{
    "name":             "denial spike",
    "rule_type":        "denial_spike",
    "threshold":        10,
    "window_minutes":   15,
    "cooldown_minutes": 30,
    "webhook_url":      "https://hooks.slack.com/services/..."
  }'
```

Rules can deliver to a **webhook URL** (HTTP POST with JSON payload) or an **email address** (requires `SMTP_HOST` env var). Both can be set on the same rule.

### Alert endpoints

```
POST   /v1/alerts/rules                — create alert rule
GET    /v1/alerts/rules                — list rules for this tenant
PUT    /v1/alerts/rules/{rule_id}      — update a rule
DELETE /v1/alerts/rules/{rule_id}      — delete a rule
GET    /v1/alerts/fired                — recent alert deliveries
```

---

## OpenTelemetry

AutoPIL emits OTEL spans and metrics from every policy evaluation. When `OTEL_EXPORTER_OTLP_ENDPOINT` or `OTEL_TRACES_EXPORTER` is set, the integration is active. When neither is set, there is zero overhead — all OTEL instruments are no-ops.

### Span

Every evaluation emits an `autopil.evaluate` span with attributes:

| Attribute | Value |
|---|---|
| `autopil.agent_role` | The role making the request |
| `autopil.source_id` | The data source requested |
| `autopil.tenant_id` | Tenant identifier |
| `autopil.session_id` | Session identifier |
| `autopil.sensitivity_level` | Sensitivity level (if provided) |
| `autopil.decision` | `ALLOW` or `DENY` |
| `autopil.policy_name` | Policy that matched |
| `autopil.reason` | Human-readable decision reason |
| `autopil.latency_ms` | Evaluation latency in milliseconds |

DENY decisions set `StatusCode.ERROR` on the span.

### Metrics

| Metric | Type | Description |
|---|---|---|
| `autopil.evaluate.count` | Counter | Total evaluations, tagged with `decision` and `tenant_id` |
| `autopil.evaluate.denied.count` | Counter | Denied evaluations, tagged with `decision` |
| `autopil.evaluate.duration` | Histogram (ms) | Evaluation latency |

### Configuration

| Variable | Description |
|---|---|
| `OTEL_SERVICE_NAME` | Service name in traces (default: `autopil`) |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint (`grpc://` or `https://`) |
| `OTEL_EXPORTER_OTLP_HEADERS` | Comma-separated `key=value` auth headers |
| `OTEL_TRACES_EXPORTER` | Set to `console` for local span output |
| `OTEL_METRICS_EXPORTER` | Set to `console` for local metrics output |

---

## SDKs

### TypeScript / Node

```bash
npm install @vibrantcapital/autopil
```

```typescript
import { AutoPilClient } from "@vibrantcapital/autopil";

const client = new AutoPilClient({
  baseUrl: "https://api.autopil.ai",
  apiKey:  process.env.AUTOPIL_API_KEY!,
});

const result = await client.context.evaluate({
  query:      "Q3 earnings summary",
  agent_role: "analyst",
  user_id:    "u_123",
  source_id:  "financial_reports",
});

if (result.decision === "DENY") {
  throw new Error(`Access denied: ${result.reason}`);
}
```

Zero runtime dependencies. Requires Node 18+. See [`sdk/typescript/`](sdk/typescript/) for the full reference.

### Go

```bash
go get github.com/vibrantcapital/autopil-go
```

Stdlib only — no third-party dependencies. Covers all resources: Context, Audit, Sessions, Policies. See [`sdk/go/`](sdk/go/) for the full reference.

### Java

```xml
<dependency>
  <groupId>ai.vibrantcapital</groupId>
  <artifactId>autopil-java</artifactId>
  <version>0.1.0</version>
</dependency>
```

Jackson only. Builder-pattern models for all resources. See [`sdk/java/`](sdk/java/) for the full reference.

---

## MCP Server

AutoPIL ships an MCP (Model Context Protocol) server that exposes governance as native tools for any MCP-compatible agent — Claude, GPT-4, Gemini, or any custom agent that speaks MCP. No code changes required in the agent; the governance layer sits between the model and your data.

### Install

```bash
pip install -e ".[mcp]"
```

### Start the server

```bash
python mcp_serve.py --policy policies/ --db autopil.db
# or via the installed console script:
autopil-mcp --policy policies/ --db autopil.db
```

### Claude Desktop configuration

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "autopil": {
      "command": "python",
      "args": ["/path/to/mcp_serve.py", "--policy", "/path/to/policies/"],
      "env": {}
    }
  }
}
```

### Available tools

| Tool | Description |
|---|---|
| `evaluate_context` | Policy check before retrieval — returns ALLOW or DENY with reason and event_id |
| `record_action` | Link a downstream decision to the audit event that authorized it |
| `query_audit_log` | Query recent audit events (filterable by role, decision, session) |
| `get_audit_stats` | Aggregate stats — totals, deny rate, top roles |
| `list_policies` | List active policies (filterable by industry, agent_role) |
| `reload_policies` | Hot-reload policies from disk without restarting |
| `get_session_status` | Session summary — owner, event count, sources accessed |

### System prompt pattern

```
Before accessing any data source, call evaluate_context with agent_role, user_id,
source_id, sensitivity_level, and session_id. Only proceed if decision is ALLOW.
After retrieval, call record_action with the event_id to log what you did.
```

See [`docs/mcp_integration.md`](docs/mcp_integration.md) for the full reference.

---

## API Authentication

All `/v1/*` endpoints require an API key in the `X-API-Key` header:

```bash
curl -H "X-API-Key: apl_yourkey" http://localhost:8000/v1/policies
```

### Key management

```bash
# Create a new key (scoped to your tenant)
curl -X POST http://localhost:8000/v1/keys \
  -H "X-API-Key: apl_yourkey" \
  -H "Content-Type: application/json" \
  -d '{"name": "production_agent"}'

# List keys for your tenant
curl http://localhost:8000/v1/keys \
  -H "X-API-Key: apl_yourkey"

# Revoke a key
curl -X DELETE http://localhost:8000/v1/keys/{key_id} \
  -H "X-API-Key: apl_yourkey"
```

Keys are hashed (SHA-256) at rest. The plaintext is returned once at creation and never stored.

---

## Multi-Tenancy

AutoPIL supports multiple tenants with row-level data isolation. Each tenant's audit events, policies, alert rules, API keys, and stats are fully isolated.

### Create a tenant (superadmin required)

```bash
curl -X POST http://localhost:8000/v1/admin/tenants \
  -H "X-API-Key: apl_superadminkey" \
  -H "Content-Type: application/json" \
  -d '{"name": "acme_corp"}'
```

Response includes `tenant_id` and `admin_key` — the initial key for that tenant. Save it; it won't be shown again.

---

## Project Structure

This is a monorepo with three packages managed via npm workspaces.

```
autopil/
├── packages/
│   ├── core/                        # Python backend (FastAPI + policy engine)
│   │   ├── autopil/
│   │   │   ├── __init__.py          # Public API: ContextGuard, Decision, SensitivityLevel
│   │   │   ├── models.py            # Dataclasses: ContextRequest, AuditEvent, AgentAction, enums
│   │   │   ├── policy_engine.py     # YAML policy loader — file or recursive directory
│   │   │   ├── audit_log.py         # Shim → SQLiteAuditLog
│   │   │   ├── guard.py             # ContextGuard decorator — core enforcement + action lineage
│   │   │   ├── alerting.py          # AlertEngine — threshold rules + webhook/email delivery
│   │   │   ├── telemetry.py         # OpenTelemetry spans and metrics (optional)
│   │   │   ├── cli.py               # autopil-serve entry point
│   │   │   ├── mcp_server.py        # MCP server — dispatch_tool(), create_mcp_server(), run_stdio_server()
│   │   │   ├── db/
│   │   │   │   ├── __init__.py      # create_stores() factory (7-tuple)
│   │   │   │   ├── base.py          # Abstract base classes for all store types
│   │   │   │   ├── sqlite.py        # SQLite backend (default)
│   │   │   │   └── postgres.py      # Postgres backend (production)
│   │   │   └── api/
│   │   │       ├── app.py           # FastAPI application factory
│   │   │       ├── schemas.py       # Pydantic request/response models
│   │   │       └── static/          # Built dashboard assets (copied at Docker build time)
│   │   ├── policies/                        # 45 pre-built policies across 4 industries
│   │   │   ├── financial_services/
│   │   │   │   ├── consumer_banking.yaml    # loan_underwriter, customer_service, fraud_analyst, collections_agent
│   │   │   │   ├── wealth.yaml              # wealth_advisor, investment_analyst, kyc_agent, intake_agent
│   │   │   │   ├── risk_compliance.yaml     # compliance_officer, aml_investigator, credit_risk_analyst, compliance_agent
│   │   │   │   └── operations.yaml          # settlement_agent, ops_support, data_pipeline_agent, risk_agent
│   │   │   ├── healthcare/
│   │   │   │   ├── clinical_operations.yaml # clinical_summary_agent, medication_review_agent, care_gap_agent, triage_agent
│   │   │   │   ├── revenue_cycle.yaml       # prior_auth_agent, claims_coding_agent, denial_management_agent, patient_billing_agent
│   │   │   │   └── compliance_privacy.yaml  # hipaa_audit_agent, consent_management_agent, breach_detection_agent
│   │   │   ├── telecom/
│   │   │   │   ├── network_operations.yaml  # network_fault_agent, capacity_planning_agent, change_management_agent
│   │   │   │   ├── customer_experience.yaml # cx_support_agent, churn_prediction_agent, provisioning_agent
│   │   │   │   └── fraud_assurance.yaml     # subscription_fraud_agent, revenue_assurance_agent, wangiri_detection_agent
│   │   │   └── logistics/
│   │   │       ├── supply_chain.yaml        # demand_forecast_agent, procurement_agent, inventory_reconciliation_agent
│   │   │       ├── fleet_operations.yaml    # route_optimization_agent, driver_compliance_agent, maintenance_scheduling_agent
│   │   │       └── customs_compliance.yaml  # trade_compliance_agent, sanctions_screening_agent, import_export_agent
│   │   ├── scripts/
│   │   │   └── simulate_data.py             # Populate dashboard with realistic multi-industry demo data
│   │   ├── examples/
│   │   │   ├── langgraph_demo.py            # Single agent with LangGraph
│   │   │   └── multi_agent_demo.py          # Cross-agent isolation in action
│   │   ├── tests/
│   │   │   ├── conftest.py                  # Shared fixtures
│   │   │   ├── test_policy_engine.py        # 23 policy evaluation tests
│   │   │   ├── test_audit_log.py            # 14 audit persistence tests
│   │   │   ├── test_guard.py                # 16 ContextGuard enforcement tests
│   │   │   ├── test_api.py                  # 86 REST API endpoint tests
│   │   │   ├── test_auth.py                 # 15 API key authentication tests
│   │   │   ├── test_tenants.py              # 13 multi-tenancy and isolation tests
│   │   │   ├── test_alerting.py             # 21 alerting rules and delivery tests
│   │   │   ├── test_telemetry.py            # 17 OTEL span and metrics tests
│   │   │   ├── test_policy_editor.py        # 12 policy CRUD and versioning tests
│   │   │   ├── test_multi_industry.py       # 65 multi-industry tests
│   │   │   ├── test_mcp_server.py           # 42 MCP server tool dispatch tests
│   │   │   └── test_postgres.py             # 11 Postgres backend tests (skipped unless DATABASE_URL set)
│   │   ├── Dockerfile                       # 3-stage build: dashboard → Python builder → runtime
│   │   ├── pyproject.toml
│   │   ├── mcp_serve.py             # MCP server entry point (autopil-mcp console script)
│   │   └── serve.py
│   │
│   ├── dashboard/                   # React + TypeScript control plane UI
│   │   ├── src/
│   │   │   ├── components/          # UI components (tabs, layout, drawers, charts)
│   │   │   ├── hooks/               # usePoll — background data refresh
│   │   │   ├── lib/                 # api.ts (authenticated fetch), utils.ts
│   │   │   ├── types/               # api.ts — TypeScript interfaces for all API shapes
│   │   │   ├── App.tsx              # Root component — auth, tab routing, data loading
│   │   │   ├── main.tsx             # React 18 entry point
│   │   │   └── index.css            # Dark theme design system
│   │   ├── vite.config.ts           # Vite config — dev proxy to :8000, configurable outDir
│   │   └── package.json
│   │
│   └── website/                     # Marketing site (static HTML)
│
├── sdk/
│   ├── typescript/                  # @vibrantcapital/autopil — TypeScript/Node SDK
│   │   ├── src/
│   │   │   ├── client.ts            # AutoPilClient with typed resource groups
│   │   │   ├── types.ts             # TypeScript interfaces for all shapes
│   │   │   ├── errors.ts            # AutoPilError
│   │   │   └── index.ts             # Public exports
│   │   └── test/
│   │       └── client.test.ts
│   ├── go/                          # github.com/vibrantcapital/autopil-go — Go SDK (stdlib only)
│   └── java/                        # ai.vibrantcapital:autopil-java:0.1.0 — Java SDK (Jackson only)
├── docker-compose.yml               # Postgres + AutoPIL container (uses packages/core/Dockerfile)
└── package.json                     # npm workspaces root
```

---

## Policy Reference

A policy file is a YAML document with a top-level `policies` list. Each entry maps to one agent role.

```yaml
policies:
  - name: my_policy          # Unique identifier, appears in audit log
    agent_role: analyst      # Must match agent_role passed to guard.protect()
    allowed_sources:         # Empty list [] = allow all non-denied sources (admin wildcard)
      - reports
      - market_data
    denied_sources:          # Always blocked, takes priority over allowed_sources
      - executive_comms
    max_sensitivity: high    # Ceiling: low | medium | high | restricted
    allowed_tasks: []        # Optional — empty = allow all non-denied tasks
    denied_tasks: []         # Optional — blocks specific task_type values
    # industry and category are auto-injected from directory path when loading from a
    # policies/<industry>/<category>.yaml structure — no need to add them manually
```

When loading from a directory (`--policy policies/`), AutoPIL recursively finds all `*.yaml` files and injects `industry` and `category` from the path. Point the server at a directory to enable multi-industry support:

```bash
python serve.py --policy policies/
```

**Evaluation order** (first match wins):

1. Is `source_id` in `denied_sources`? → **DENY**
2. Is `task_type` in `denied_tasks`? → **DENY**
3. Is `allowed_tasks` non-empty and `task_type` not in it? → **DENY**
4. Is `allowed_sources` non-empty and `source_id` not in it? → **DENY**
5. Does `sensitivity_level` exceed `max_sensitivity`? → **DENY**
6. No issues found → **ALLOW**
7. No policy matched for this `agent_role` → **DENY** (default deny)

Policies can also be managed via the REST API (`POST /v1/policies`, `PUT /v1/policies/{name}`) — changes are versioned and appear in `GET /v1/policies/{name}/history`.

---

## REST API Reference

Base URL: `http://localhost:8000`

All `/v1/*` routes require `X-API-Key` header. `/health` is exempt.

### Context

```
POST /v1/context/evaluate
```

```json
{
  "query": "Q3 loss rates",
  "agent_role": "loan_underwriter",
  "user_id": "user_001",
  "source_id": "credit_scores",
  "sensitivity_level": "high",
  "session_id": "optional-uuid",
  "task_type": "optional-task-label"
}
```

### Sessions

```
GET    /v1/sessions                         — list active sessions for this tenant
GET    /v1/sessions/{id}                    — session status (active | expired | revoked)
DELETE /v1/sessions/{id}                    — revoke a session immediately
```

### Audit

```
GET /v1/audit/sessions/{session_id}         — full audit trail for a session
GET /v1/audit/events?decision=DENY&limit=100
GET /v1/audit/export?format=csv             — bulk export (CSV or JSON)
GET /v1/audit/stats
POST /v1/audit/lineage                      — record an agent action against an audit event
GET  /v1/audit/lineage/{event_id}           — retrieve event + all linked downstream actions
```

### Policies

```
GET    /v1/policies                          — list (optional: ?industry=&category=)
POST   /v1/policies
PUT    /v1/policies/{name}
DELETE /v1/policies/{name}
GET    /v1/policies/{name}/history
POST   /v1/policies/reload
```

### Alert rules

```
POST   /v1/alerts/rules
GET    /v1/alerts/rules
PUT    /v1/alerts/rules/{rule_id}
DELETE /v1/alerts/rules/{rule_id}
GET    /v1/alerts/fired
```

### Keys

```
POST   /v1/keys
GET    /v1/keys
DELETE /v1/keys/{key_id}
```

### Tenant management (superadmin)

```
POST   /v1/admin/tenants
GET    /v1/admin/tenants
DELETE /v1/admin/tenants/{id}
```

---

## Storage Backends

### SQLite (default)

Zero configuration. Used for development and single-instance deployments.

```bash
python serve.py --policy policies/ --db /data/autopil.db
```

### Postgres (production)

```bash
pip install "autopil[postgres]"
export DATABASE_URL=postgresql://user:pass@host:5432/autopil
python3 serve.py --policy policies/
```

Schema is created automatically on first start. Supports horizontal scaling.

### Docker Compose (Postgres)

```bash
docker compose up --build
```

The default `docker-compose.yml` starts AutoPIL with a `postgres:16-alpine` service wired via `DATABASE_URL`.

---

## Docker Reference

### Build

The Dockerfile is a 3-stage build: it compiles the React dashboard, installs the Python package, then assembles the runtime image with the dashboard assets served by the API.

```bash
docker build -f packages/core/Dockerfile -t autopil:0.1.0 .
```

### Run

```bash
docker run -d \
  -p 8000:8000 \
  -v $(pwd)/packages/core/policies:/app/policies:ro \
  -v autopil-data:/data \
  -e AUTOPIL_POLICY=/app/policies \
  autopil:0.1.0
```

### Environment variables

| Variable | Default | Description |
|---|---|---|
| `DATABASE_URL` | _(unset)_ | `postgresql://...` — uses Postgres if set, otherwise SQLite |
| `AUTOPIL_POLICY` | `/app/policies` | Policy file or directory path |
| `AUTOPIL_DB` | `/data/autopil_audit.db` | SQLite audit DB path |
| `AUTOPIL_HOST` | `0.0.0.0` | Bind host |
| `AUTOPIL_PORT` | `8000` | Listen port |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | _(unset)_ | OTLP collector URL — enables OTEL when set |
| `OTEL_SERVICE_NAME` | `autopil` | Service name in traces |
| `AUTOPIL_SESSION_TTL_MINUTES` | _(unset)_ | Default session TTL in minutes. Unset = sessions never expire. |
| `SMTP_HOST` | _(unset)_ | SMTP host for alert email delivery |

---

## Running the Examples

### LangGraph demo — loan underwriting agent

```bash
python examples/langgraph_demo.py
```

Runs a 3-node LangGraph pipeline that makes 4 retrieval attempts (2 allowed, 2 denied) and prints the full audit trail.

### Multi-agent demo — cross-agent isolation

```bash
python examples/multi_agent_demo.py
```

Runs a 4-agent wealth onboarding pipeline and demonstrates cross-agent isolation violations being blocked while the legitimate pipeline still reaches an APPROVED decision.

---

## Running the Test Suite

### Python

```bash
pip install -e ".[dev]"
pytest tests/ -v --ignore=tests/test_postgres.py
```

312 tests, all passing (11 skipped without Postgres):

| File | Tests | What it covers |
|---|---|---|
| `test_policy_engine.py` | 23 | Allow/deny paths, sensitivity ceiling, admin wildcard, task scoping |
| `test_audit_log.py` | 14 | Persistence, session isolation, context hash |
| `test_guard.py` | 16 | Decorator enforcement, audit creation, cross-agent isolation |
| `test_api.py` | 86 | REST endpoints, filtering, export, lineage, session lifecycle, task scoping |
| `test_auth.py` | 15 | API key creation, listing, revocation, auth enforcement |
| `test_tenants.py` | 13 | Tenant lifecycle, data isolation, superadmin enforcement |
| `test_alerting.py` | 21 | Rule CRUD, all rule types, cooldown, delivery, API integration |
| `test_telemetry.py` | 17 | OTEL spans, metrics, guard integration, API integration |
| `test_policy_editor.py` | 12 | Policy CRUD, version history, restore, reload |
| `test_multi_industry.py` | 65 | Directory loading, path metadata, 45-policy real set, industry/category filters, multi-industry evaluate, task scoping |
| `test_mcp_server.py` | 42 | All 7 MCP tools — evaluate, audit query/stats, list policies, record action, reload, session status, server wiring |
| `test_postgres.py` | 11 | Postgres backend (runs when `DATABASE_URL` is set) |

### TypeScript SDK

```bash
cd sdk/typescript
npm install
npm test
```

28 tests covering all resource groups, error handling, query params, and URL normalisation.

### Go SDK

```bash
cd sdk/go
go test ./...
```

### Java SDK

```bash
cd sdk/java
mvn test
```

---

## Sensitivity Levels

Ordered from least to most sensitive:

| Level | Value | Example data |
|---|---|---|
| Low | `low` | Public product catalogs, FAQ knowledge bases |
| Medium | `medium` | Account summaries, transaction history |
| High | `high` | Credit scores, identity records, loan history |
| Restricted | `restricted` | Regulatory filings, internal risk models, audit logs |

---

## License

AutoPIL is licensed under the [Business Source License 1.1](LICENSE).

**What this means in practice:**

- **Free to use** for development, research, evaluation, and any non-commercial purpose
- **Free for internal production use** — deploy it inside your own organization without restriction
- **Commercial restriction** — you may not offer AutoPIL or a substantially similar product as a managed service or SaaS to third parties without a commercial license
- **Converts to MIT on March 25, 2030** — the full codebase becomes open source automatically on that date

For commercial licensing or enterprise deployments: **anil@vibrantcapital.ai**

---

## Version

`0.1.0` — initial release
