Metadata-Version: 2.4
Name: gatehouse-ai
Version: 0.1.0
Summary: A runtime policy gate for AI agents: every tool call is evaluated against declarative policy before it executes.
Project-URL: Homepage, https://github.com/wkitcheah/gatehouse
Author-email: Cheah Wai Kit <wkitcheah@users.noreply.github.com>
License: MIT
License-File: LICENSE
Keywords: ai-agents,audit,governance,guardrails,policy
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: click>=8.1
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: fastapi>=0.111; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: mcp>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: types-pyyaml; extra == 'dev'
Requires-Dist: uvicorn[standard]>=0.30; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == 'mcp'
Provides-Extra: ui
Requires-Dist: fastapi>=0.111; extra == 'ui'
Requires-Dist: uvicorn[standard]>=0.30; extra == 'ui'
Description-Content-Type: text/markdown

# Gatehouse

[![CI](https://github.com/wkitcheah/gatehouse/actions/workflows/ci.yml/badge.svg)](https://github.com/wkitcheah/gatehouse/actions/workflows/ci.yml)

**A runtime policy gate for AI agents.**

Gatehouse sits between an AI agent and its tools. Every action the agent
proposes — a tool call, an API invocation — is intercepted and evaluated
against declarative policy *before* it executes. Every evaluation resolves to
one binding disposition:

| Disposition | Meaning |
| --- | --- |
| `allow` | The action proceeds. |
| `deny` | The action is blocked, with a reason. |
| `require_approval` | The action is held until a human approves it. |
| `allow_with_flag` | The action proceeds, but is marked for monitoring. |

Every evaluation is written to a structured audit log. The default posture is
**deny by default, explicit allow, every disposition explainable**.

> **Status:** v0.1.0 — the complete local gate: engine, registry, audit,
> approvals, CLI, dashboard, MCP proxy, and a runnable demo. Young software;
> interfaces may still move between minor versions.

## The demo in one paragraph

A scripted travel agent books fictional flights through mock MCP tools with
Gatehouse in between: a permitted search streams into the dashboard as
`allow`, an over-budget booking comes back `deny` with the exact reason, a
watchlisted destination proceeds as `allow_with_flag`, and a cancellation
stops the terminal until you click **Approve** in the dashboard — then it
completes instantly. Run it yourself:
[examples/travel-agent](examples/travel-agent/README.md).

![The travel-agent demo: an allowed search, a booking denied with its reason, a flagged destination, and a held cancellation approved from the dashboard — the terminal proceeding the moment the human decides.](docs/demo.gif)

## Features

- **Policy engine** — declarative YAML rules with agent/tool matching,
  parameter constraints (bounds, allowlists, regex), selection predicates
  (`where` selects, `params` enforces), time windows, and rate/cumulative
  limits with per-session or rolling clock scopes. Deny by default; when
  rules disagree, the most restrictive disposition wins.
- **Agent registry** — agents are declared with a human owner, an autonomy
  tier (`read_only` / `supervised` / `governed` / `human_only`), an explicit
  tool grant, and a credentials *reference*, never a value. Unregistered
  agents are denied before policy is consulted. Tiers are ceilings: no rule
  can grant past them.
- **Audit log** — append-only JSONL, one record per evaluation: disposition,
  reason, matched rules, policy version and digest, latency. Parameters are
  stored as a digest, not verbatim. Usage counters (budgets, rate limits) are
  rebuilt from the log on restart, so restarting the gate never resets them.
- **Approval queue** — `require_approval` actions are held with a TTL and
  resolved from another process; expiry fails closed. Every resolution is an
  appended audit record naming who decided and why.
- **Policy test harness** — scenario files declare action → expected
  disposition; scenarios share usage state so limit behaviour is testable.
  Policies without tests are policies waiting to be bypassed.
- **CLI** — `gatehouse check` (one-shot advisory evaluation, records
  nothing), `gatehouse test`, `gatehouse audit tail|stats`,
  `gatehouse approvals` / `approve` / `deny`, `gatehouse ui`.
- **Web dashboard** — live disposition feed over WebSocket, approval cards
  with TTL countdowns, the agent registry with activity sparklines, and a
  read-only policy view with test results. Localhost only, no auth in v0.1.

## Quickstart

```bash
pip install -e ".[ui]"      # from a checkout (PyPI: gatehouse-ai, once published)
```

Declare an agent and a policy:

```yaml
# agents.yaml
agents:
  - id: trip-planner
    owner: you@example.test
    tier: governed
    allowed_tools: [search_flights, book_flight]
```

```yaml
# policy.yaml
version: 1
tools:
  search_flights: {mutating: false}
  book_flight: {mutating: true}
rules:
  - id: allow-search
    agents: [trip-planner]
    tools: [search_flights]
    disposition: allow
  - id: book-within-budget
    agents: [trip-planner]
    tools: [book_flight]
    disposition: allow
    params:
      price: {max: 500}
    limits:
      - {cumulative: price, max: 2000, per: session}
```

Ask the gate what would happen:

```bash
$ gatehouse check trip-planner book_flight -p policy.yaml -r agents.yaml --param price=812
deny  trip-planner → book_flight
  reason: rule 'book-within-budget': parameter 'price' = 812 exceeds the maximum 500.0
```

Gate any MCP server without changing the agent or the server — the agent
connects to Gatehouse instead:

```bash
pip install "gatehouse-ai[mcp]"
gatehouse run --agent trip-planner -p policy.yaml -r agents.yaml -- <your MCP server command>
```

Or gate plain Python calls directly:

```python
from gatehouse import ApprovalQueue, AuditLog, Gate, PolicyEngine, Registry
from gatehouse.adapters import gated

gate = Gate(
    Registry.from_file("agents.yaml"),
    PolicyEngine.from_file("policy.yaml"),
    AuditLog("gatehouse-audit.jsonl"),
    approvals=ApprovalQueue("gatehouse-approvals"),
)

@gated(gate, agent="trip-planner")
def book_flight(price: float, destination: str) -> str:
    ...   # only runs if the gate says so; refusals raise ActionDeniedError
```

Watch it live:

```bash
gatehouse ui -p policy.yaml -r agents.yaml    # http://127.0.0.1:8422
```

## Documentation

- [Writing policies](docs/writing-policies.md) — the tutorial; every command
  runs as written.
- [Policy reference](docs/policy-reference.md) — every field, and the
  evaluation semantics.
- [MVCP mapping](docs/mvcp-mapping.md) — how each feature answers the five
  control-plane questions: identity, authority, visibility, containment,
  recovery.
- [Design decisions](docs/decisions.md) — why tiers are ceilings, why an
  uncheckable constraint is a breach, what a "session" is, and every other
  choice that shapes the semantics, with rationale.

## Background and attribution

The conceptual grounding is the Agentic Control Plane ("From Prevention to
Control") and the Minimum Viable Control Plane ("From the Control Gap to
the Agency Gap"). The four-disposition vocabulary deliberately echoes the
four outcomes described in the Monetary Authority of Singapore's SAFR
white paper (July 2026) — convergent framings of the same idea: agent
actions should resolve to a small, closed set of binding outcomes.

## Development

```bash
pip install -e ".[dev]"
pytest && ruff check . && mypy gatehouse tests
```

The frontend source lives in `ui/`; built assets are committed under
`gatehouse/ui/` so installed users never need npm. Rebuild with
`cd ui && npm install && npm run build`.

## License

MIT
