Skip to main content

Platform Intelligence Layer — context governance for autonomous agents

Project description

AutoPIL — Platform Intelligence Layer

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 (SQLite / Postgres)

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

Architecture

┌─────────────────────────────────────────────────────────┐
│                       Your Agent                        │
└────────────────────────┬────────────────────────────────┘
                         │  @guard.protect(...)
┌────────────────────────▼────────────────────────────────┐
│                    ContextGuard                         │
│  1. Cross-agent session isolation check                 │
│  2. Policy evaluation (allow / deny)                    │
│  3. Execute retrieval (if allowed)                      │
│  4. Hash context for provenance                         │
│  5. Record immutable audit event                        │
└──────────────┬────────────────────────┬─────────────────┘
               │                        │
┌──────────────▼──────┐   ┌─────────────▼───────────────┐
│   PolicyEngine      │   │     Storage Backend          │
│   YAML-based rules  │   │   SQLite (default)           │
│   Deny by default   │   │   Postgres (production)      │
└─────────────────────┘   └─────────────────────────────┘

Five 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
Cross-agent isolation A session belongs to the first agent that used it — others are denied
Default deny No matching policy = access denied

Quickstart

Install

pip install -e .

For Postgres support:

pip install -e ".[postgres]"

Define a policy

# policies/my_policy.yaml
policies:
  - name: analyst_policy
    agent_role: analyst
    allowed_sources:
      - reports
      - market_data
    denied_sources:
      - executive_comms
    max_sensitivity: high

Wrap your retrieval functions

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")

View the audit trail

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

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/financial_services.yaml
  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

python serve.py --policy policies/financial_services.yaml

With Postgres

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

Docker

docker compose up --build

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

  • Dashboard: http://localhost:8000
  • API docs: http://localhost:8000/docs

API Authentication

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

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

Key management

# 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, API keys, and stats are fully isolated.

Create a tenant (superadmin required)

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.

Tenant admin routes

Method Path Description
POST /v1/admin/tenants Create tenant + initial admin key
GET /v1/admin/tenants List all tenants
DELETE /v1/admin/tenants/{id} Deactivate a tenant

All admin routes require a superadmin key (is_superadmin=true).


Project Structure

autopil/
├── autopil/
│   ├── __init__.py          # Public API: ContextGuard, Decision, SensitivityLevel
│   ├── models.py            # Dataclasses: ContextRequest, AuditEvent, enums
│   ├── policy_engine.py     # YAML policy loader and evaluator
│   ├── audit_log.py         # Shim → SQLiteAuditLog
│   ├── guard.py             # ContextGuard decorator — core enforcement
│   ├── db/
│   │   ├── __init__.py      # create_stores() factory
│   │   ├── base.py          # AuditLogBase, KeyStoreBase, TenantStoreBase ABCs
│   │   ├── sqlite.py        # SQLite backend (default)
│   │   └── postgres.py      # Postgres backend (production)
│   └── api/
│       ├── app.py           # FastAPI application factory
│       ├── key_store.py     # Shim → SQLiteKeyStore
│       ├── schemas.py       # Pydantic request/response models
│       └── static/
│           └── index.html   # Control plane dashboard UI
├── policies/
│   ├── financial_services.yaml   # Loan underwriting roles
│   └── wealth_onboarding.yaml    # 4-agent onboarding pipeline roles
├── 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     # 14 policy evaluation tests
│   ├── test_audit_log.py         # 14 audit persistence tests
│   ├── test_guard.py             # 16 ContextGuard enforcement tests
│   ├── test_api.py               # 20 REST API endpoint tests
│   ├── test_auth.py              # 15 API key authentication tests
│   ├── test_tenants.py           # 13 multi-tenancy and isolation tests
│   └── test_postgres.py          # 11 Postgres backend tests (skipped unless DATABASE_URL set)
├── Dockerfile
├── docker-compose.yml
└── serve.py

Policy Reference

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

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

Evaluation order (first match wins):

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

REST API Reference

Base URL: http://localhost:8000

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

Evaluate a context request

POST /v1/context/evaluate
X-API-Key: apl_yourkey
{
  "query": "Q3 loss rates",
  "agent_role": "loan_underwriter",
  "user_id": "user_001",
  "source_id": "credit_scores",
  "sensitivity_level": "high",
  "session_id": "optional-uuid"
}

Response:

{
  "decision": "ALLOW",
  "policy_name": "loan_underwriter_policy",
  "reason": "Access granted",
  "event_id": "uuid",
  "session_id": "uuid",
  "timestamp": "2026-03-25T06:00:00"
}

Key management

POST   /v1/keys              — create key (scoped to calling tenant)
GET    /v1/keys              — list keys for calling tenant
DELETE /v1/keys/{key_id}     — revoke a key

Tenant management (superadmin)

POST   /v1/admin/tenants         — create tenant + initial admin key
GET    /v1/admin/tenants         — list all tenants
DELETE /v1/admin/tenants/{id}    — deactivate tenant

Audit

GET /v1/audit/sessions/{session_id}
GET /v1/audit/events?decision=DENY&agent_role=analyst&limit=100
GET /v1/audit/stats

Policies

GET  /v1/policies
POST /v1/policies/reload

Storage Backends

SQLite (default)

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

python serve.py --policy policies/financial_services.yaml --db /data/autopil.db

Postgres (production)

pip install autopil[postgres]
export DATABASE_URL=postgresql://user:pass@host:5432/autopil
python serve.py

Schema is created automatically on first start. Supports horizontal scaling — multiple instances can share one Postgres database.

Docker Compose (Postgres)

docker compose up --build

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


Docker Reference

Build

docker build -t autopil:0.1.0 .

Run with environment overrides

docker run -d \
  -p 8000:8000 \
  -v $(pwd)/policies:/app/policies:ro \
  -v autopil-data:/data \
  -e AUTOPIL_POLICY=/app/policies/wealth_onboarding.yaml \
  autopil:0.1.0

Environment variables

Variable Default Description
DATABASE_URL (unset) postgresql://... — uses Postgres if set, otherwise SQLite
AUTOPIL_POLICY /app/policies/financial_services.yaml Policy file path inside container
AUTOPIL_DB /data/autopil_audit.db SQLite audit DB path (ignored if DATABASE_URL is set)
AUTOPIL_HOST 0.0.0.0 Bind host
AUTOPIL_PORT 8000 Listen port

Running the Examples

LangGraph demo — loan underwriting agent

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

python examples/multi_agent_demo.py

Runs a 4-agent wealth onboarding pipeline (intake → KYC → risk → compliance) and demonstrates 4 isolation violations being blocked while the legitimate pipeline still reaches an APPROVED decision.


Running the Test Suite

pip install -e ".[dev]"
pytest tests/ -v

92 tests, all passing. Coverage:

File Tests What it covers
test_policy_engine.py 14 Allow/deny paths, sensitivity ceiling, admin wildcard, policy reload
test_audit_log.py 14 Persistence, session isolation, session owner, context hash
test_guard.py 16 Decorator enforcement, audit creation, context hash, cross-agent isolation
test_api.py 20 All REST endpoints, filtering, stats, policy reload
test_auth.py 15 API key creation, listing, revocation, auth enforcement
test_tenants.py 13 Tenant lifecycle, data isolation, superadmin enforcement

Postgres tests (11) run automatically when DATABASE_URL is set.


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.

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

BSL is not an OSI-approved open source license. It is a source-available license designed to allow broad community use while protecting against cloud vendors and competitors commoditizing the work before the project can sustain itself commercially.

For commercial licensing, partnership inquiries, or enterprise deployments: anil@vibrantcapital.ai


Version

0.1.0 — initial release

Project details


Download files

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

Source Distribution

autopil-0.1.0.tar.gz (49.9 kB view details)

Uploaded Source

Built Distribution

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

autopil-0.1.0-py3-none-any.whl (39.3 kB view details)

Uploaded Python 3

File details

Details for the file autopil-0.1.0.tar.gz.

File metadata

  • Download URL: autopil-0.1.0.tar.gz
  • Upload date:
  • Size: 49.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for autopil-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fcf29f69909287e84d299efb608306f6ca5db9d3fe518739624fc7a9cf1a4deb
MD5 de2c6413e62bff646046a854424d68d1
BLAKE2b-256 3c01ba70839bd256c90c12954ce42f1904b451e4d5b7df4bdde173b72ddbf828

See more details on using hashes here.

Provenance

The following attestation bundles were made for autopil-0.1.0.tar.gz:

Publisher: publish.yml on anil-solleti/autopil

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file autopil-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: autopil-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 39.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for autopil-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 375a9911c30a9c767834baac6746ac5ea1591baa1c68bcf2f4a962921bd9c0e4
MD5 746564397d91d8b63001ad50a3795afa
BLAKE2b-256 64332a253e20dcc77b5db6cef772462e9f5cfcdf99725897bb3c611495e2b10b

See more details on using hashes here.

Provenance

The following attestation bundles were made for autopil-0.1.0-py3-none-any.whl:

Publisher: publish.yml on anil-solleti/autopil

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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