Metadata-Version: 2.4
Name: interdict-db
Version: 0.4.0
Summary: Runtime safety layer for AI agents touching Postgres.
Project-URL: Homepage, https://interdict.vercel.app
Project-URL: Repository, https://github.com/prisharai/Interdict
Project-URL: Issues, https://github.com/prisharai/Interdict/issues
Author-email: Prisha Rai <pr482@cornell.edu>
License: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Database
Classifier: Topic :: Security
Requires-Python: >=3.11
Requires-Dist: asyncpg<0.31,>=0.29
Requires-Dist: keyring<27,>=25
Requires-Dist: mcp>=1.28.0
Requires-Dist: pglast<8,>=6
Requires-Dist: pyyaml>=6.0.3
Description-Content-Type: text/markdown

<div align="center">
<h1>Interdict: Runtime Safety for Agent-Written SQL</h1>

![Status: Alpha](https://img.shields.io/badge/status-alpha-orange?style=for-the-badge)
[![PyPI](https://img.shields.io/pypi/v/interdict-db?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/interdict-db/)
[![Python](https://img.shields.io/pypi/pyversions/interdict-db?style=for-the-badge&logo=python&logoColor=white&label=)](https://pypi.org/project/interdict-db/)
[![CI](https://img.shields.io/github/actions/workflow/status/prisharai/Interdict/ci.yml?branch=main&style=for-the-badge&label=CI&logo=github)](https://github.com/prisharai/Interdict/actions/workflows/ci.yml)
[![Homepage](https://img.shields.io/badge/Homepage-0f766e?style=for-the-badge&logo=google-chrome&logoColor=white)](https://interdict.vercel.app/)
[![Docs](https://img.shields.io/badge/Docs-0f766e?style=for-the-badge&logo=readthedocs&logoColor=white)](https://interdict.vercel.app/get-started.html)
[![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)](LICENSE)
</div>

---

> [!IMPORTANT]
> **Interdict is an alpha developer preview** and under active development.
> It is designed for agent-operated Postgres workflows, not as a replacement
> for least-privilege database roles, backups, or human review of production
> policies.

<p align="center">
  <a href="#installation">Install</a> |
  <a href="#quickstart">Quickstart</a> |
  <a href="#how-it-works">How it works</a> |
  <a href="#benchmarks">Benchmarks</a> |
  <a href="#configuration">Configuration</a> |
  <a href="#honest-limits">Limits</a> |
  <a href="#documentation">Docs</a> |
  <a href="#contributing">Contributing</a> |
  <a href="#security">Security</a>
</p>

**Interdict** is the safety layer between AI agents and Postgres. Agents can now
issue real SQL against real databases; ordinary permissions answer "may this
role touch this table?", but not "how much will this statement change?" or "can
I undo it if the agent is wrong?" Interdict answers those questions before
damage is done.

It parses SQL into a Postgres AST, applies deterministic policy, measures every
write without executing it, holds high-impact changes for operator approval,
and records supported writes so a human can approve reverting them.
Blocks return structured explanations and repair hints so the agent can correct
itself and retry.

> **Platforms.** Interdict requires **Python 3.11+** and a reachable
> **Postgres** database. The current public adapter is an MCP server; the safety
> engine is transport-agnostic.

## Installation

```bash
pip install interdict-db
```

The PyPI distribution is named `interdict-db`; the command it installs is
`interdict`. The separate `interdict` package on PyPI is an unrelated legacy
dictionary utility.

Working on Interdict itself? Install the local development environment instead:

```bash
uv sync --group dev --python 3.11
docker compose up -d
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for the full local validation loop.

## Quickstart

The normal setup is three commands:

```bash
pip install interdict-db
interdict setup --name production
interdict connect claude --profile production
```

`interdict setup` is a guided terminal wizard. It tests the connection,
discovers tables, creates a least-privilege policy, checks the database role,
initializes the control store, stores credentials in the operating-system
keychain when available, and creates a named profile. It never places a
database URL or approval credential in the MCP configuration.

The connector configures the agent to launch Interdict automatically. You do
not run a second server window. Verify the connection by asking Claude to call
`interdict_status`, then ask it to work with the database normally.

Other supported clients use the same profile:

```bash
interdict connect codex --profile production
interdict connect cursor --profile production
interdict connect custom --profile production
```

For a safe read, Interdict checks the SQL and sends it to Postgres. For a
dangerous or large write, Interdict measures the blast radius and holds the
exact SQL for a human decision. Approvals happen in **your terminal**, never in
the agent chat:

```bash
interdict approvals --profile production
interdict approve latest --profile production
interdict deny latest --profile production
```

Use the exact approval ID instead of `latest` when more than one request is
pending. Holds expire after 30 minutes so a stale measurement cannot be acted
on. A supported successful write returns an `undo_id`; a human can request the
newest bounded revert with:

```bash
interdict revert latest --profile production
```

Interdict then tells the agent which approved tool call can complete the
action. The approval credential never enters the model conversation.

### Multiple databases

Configure each database once and select it explicitly:

```bash
interdict setup --name staging
interdict setup --name analytics
interdict profiles
interdict profile use production
```

The human chooses the active profile. Interdict does not expose an MCP tool
that lets an agent switch itself to a different database.

No database handy for local evaluation? Start the seeded Pagila database and
choose the **Development** preset in the wizard:

```bash
docker compose up -d postgres
interdict setup --name local-pagila
```

## Production setup

Production mode refuses to connect an agent when the database boundary is
unsafe. The application connection must be a non-owner, non-superuser role
limited to the selected tables. Approvals, undo evidence, and the durable audit
copy must use a separate database and role.

If the URL entered during `interdict setup` is too powerful, the wizard saves
the profile as `needs-dba`, generates one owner-only SQL file for review, and
stops. It does **not** grant itself privileges or connect the agent. A database
administrator reviews and applies the script, then the customer reruns setup
using the restricted role and checks it with:

```bash
interdict doctor --profile production
interdict connect claude --profile production
```

Advanced operators may still use environment variables and `interdict init`,
but customers do not need to hand-write YAML or long MCP commands for the
standard workflow.

For an upgrade from the older in-application `adb_undo` schema, run
`interdict migrate-control`. It is idempotent and copy-only: it does not delete
the old records. Verify the copy and your backups before manually revoking or
archiving the legacy schema.

## Benchmarks

Interdict keeps a hard latency budget: the pass-through path must stay under
**5 ms added p99**, and CI fails if the gate is exceeded.

| What | Result |
|---|---|
| Cost added per statement, warm path | **2.6 us p50 / 2.7 us p99** |
| End-to-end overhead vs raw asyncpg | **measurement-noise floor**; CI gate requires added p99 < 5 ms |
| Dangerous statements missed, red corpus | **0%** of 40 |
| Safe statements wrongly blocked, green corpus | **0%** of 18 |
| Blast-radius measurement | exact row counts, live |
| Undo round-trip | ~4 ms, conflict-checked restore |
| Automated tests | **378**, run in CI on every commit |

The benchmark methodology, caveats, and raw tables live in
[benchmarks/RESULTS.md](benchmarks/RESULTS.md). CI runs lint, the full pytest
suite against seeded Postgres 16, and the latency gate on every push and pull
request.

## Configuration

Named profiles are the recommended configuration interface. The following
variables remain available for advanced/manual deployments:

| Variable | Default | Purpose |
|---|---|---|
| `AGENT_DB_DSN` | local dev DSN | Target Postgres connection string. |
| `AGENT_CONTROL_DSN` | unset | Separate control database for approvals, undo evidence, and durable audit events; required in production. |
| `AGENT_SAFETY_PROFILE` | `production` | `production` fails startup on unsafe privileges; `development` relaxes deployment topology. |
| `AGENT_POLICY` | `policies/default.yaml` | Database-agnostic safety policy. |
| `AGENT_OPERATOR_TOKEN` | unset | Required to approve held writes; use at least 32 random characters. |
| `AGENT_OPERATOR_ID` | unset | Stable identity recorded for the human operator; required in production. |
| `AGENT_APPROVAL_TTL_SECONDS` | `1800` | How long a held write stays approvable. |
| `AGENT_AUDIT_LOG` | `~/.interdict/audit.jsonl` | Async audit log with raw SQL redacted and hashes retained. |

`policies/pagila.yaml` shows a stricter allowlist-style policy for the bundled
development database.

## Honest limits

- Interdict only governs SQL sent through its adapter. Do not give the agent a
  raw database DSN, cloud-admin token, shell with production credentials, or a
  second unguarded database tool.
- Undo is bounded compensation, not a backup system. Interdict blocks automatic
  undo for unsupported statement shapes, user triggers, cascading foreign-key
  actions, oversized captures, and conflicts detected during restore.
- Keep encrypted backups and point-in-time recovery in a different failure
  domain from the production volume, and regularly test restoration. A backup
  on the same volume does not protect against volume deletion.
- Least privilege, dev/staging separation, reviewed migrations, CI/CD gates,
  provider deletion protection, and human incident procedures remain required.
- The MCP adapter cannot intercept destructive cloud API calls such as deleting
  a Railway volume. The agent must not possess credentials scoped to those
  actions; enforce that boundary in your cloud IAM system.

## Development

```bash
uv sync --group dev --python 3.11
docker compose up -d
uv run ruff check .
uv run black --check .
uv run pytest
uv run python -m benchmarks.ci_latency_gate
```

The seeded database mirrors the GitHub Actions service container:
`postgresql://postgres:postgres@localhost:5433/pagila`.

## Documentation

- [Homepage](https://interdict.vercel.app/) -- product overview and demo.
- [Get started](https://interdict.vercel.app/get-started.html) -- complete
  installation, profile, agent connection, and approval guide.
- [Benchmark results](benchmarks/RESULTS.md) -- measured latency and correctness
  results.
- [Research notes](research/README.md) -- study harness and validation material.

## Repository Layout

```text
engine/      safety core: parse, classify, policy, measure, undo, audit
adapters/    MCP server
policies/    YAML policies
corpus/      red and green query sets
benchmarks/  latency harness and CI gate
tests/       correctness, race, fault-injection, evasion, and MCP tests
examples/    local demo script
website/     landing page source for interdict.vercel.app
research/    study harness, figures, and paper artifacts
```

## Contributing

Contributions are welcome for bug fixes, documentation, examples, tests, and
tightly scoped improvements. Please read [CONTRIBUTING.md](CONTRIBUTING.md)
before opening a pull request and follow the
[Code of Conduct](CODE_OF_CONDUCT.md).

## Security

Please do not report security vulnerabilities through public issues or pull
requests. Follow [SECURITY.md](SECURITY.md) for private reporting guidance.

## License

Interdict is licensed under the [MIT License](LICENSE).
