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):
- Is
source_idindenied_sources? → DENY - Is
allowed_sourcesnon-empty andsource_idnot in it? → DENY - Does
sensitivity_levelexceedmax_sensitivity? → DENY - No issues found → ALLOW
- No policy matched for this
agent_role→ DENY (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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fcf29f69909287e84d299efb608306f6ca5db9d3fe518739624fc7a9cf1a4deb
|
|
| MD5 |
de2c6413e62bff646046a854424d68d1
|
|
| BLAKE2b-256 |
3c01ba70839bd256c90c12954ce42f1904b451e4d5b7df4bdde173b72ddbf828
|
Provenance
The following attestation bundles were made for autopil-0.1.0.tar.gz:
Publisher:
publish.yml on anil-solleti/autopil
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
autopil-0.1.0.tar.gz -
Subject digest:
fcf29f69909287e84d299efb608306f6ca5db9d3fe518739624fc7a9cf1a4deb - Sigstore transparency entry: 1183404105
- Sigstore integration time:
-
Permalink:
anil-solleti/autopil@546ce1f1c1622f752bef597c041b0065139567a6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/anil-solleti
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@546ce1f1c1622f752bef597c041b0065139567a6 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
375a9911c30a9c767834baac6746ac5ea1591baa1c68bcf2f4a962921bd9c0e4
|
|
| MD5 |
746564397d91d8b63001ad50a3795afa
|
|
| BLAKE2b-256 |
64332a253e20dcc77b5db6cef772462e9f5cfcdf99725897bb3c611495e2b10b
|
Provenance
The following attestation bundles were made for autopil-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on anil-solleti/autopil
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
autopil-0.1.0-py3-none-any.whl -
Subject digest:
375a9911c30a9c767834baac6746ac5ea1591baa1c68bcf2f4a962921bd9c0e4 - Sigstore transparency entry: 1183404131
- Sigstore integration time:
-
Permalink:
anil-solleti/autopil@546ce1f1c1622f752bef597c041b0065139567a6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/anil-solleti
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@546ce1f1c1622f752bef597c041b0065139567a6 -
Trigger Event:
push
-
Statement type: