gatehouse-ai 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gatehouse_ai-0.1.0/.claude/settings.json +3 -0
- gatehouse_ai-0.1.0/.claude/settings.local.json +22 -0
- gatehouse_ai-0.1.0/.github/workflows/ci.yml +30 -0
- gatehouse_ai-0.1.0/.gitignore +21 -0
- gatehouse_ai-0.1.0/CHANGELOG.md +46 -0
- gatehouse_ai-0.1.0/CONTRIBUTING.md +59 -0
- gatehouse_ai-0.1.0/GATEHOUSE_CLAUDE.md +157 -0
- gatehouse_ai-0.1.0/LICENSE +21 -0
- gatehouse_ai-0.1.0/PKG-INFO +216 -0
- gatehouse_ai-0.1.0/README.md +180 -0
- gatehouse_ai-0.1.0/docs/decisions.md +246 -0
- gatehouse_ai-0.1.0/docs/demo.gif +0 -0
- gatehouse_ai-0.1.0/docs/mvcp-mapping.md +123 -0
- gatehouse_ai-0.1.0/docs/policy-reference.md +210 -0
- gatehouse_ai-0.1.0/docs/writing-policies.md +276 -0
- gatehouse_ai-0.1.0/examples/travel-agent/README.md +57 -0
- gatehouse_ai-0.1.0/examples/travel-agent/agent.py +123 -0
- gatehouse_ai-0.1.0/examples/travel-agent/agents.yaml +14 -0
- gatehouse_ai-0.1.0/examples/travel-agent/policy.yaml +44 -0
- gatehouse_ai-0.1.0/examples/travel-agent/travel.test.yaml +85 -0
- gatehouse_ai-0.1.0/examples/travel-agent/travel_server.py +69 -0
- gatehouse_ai-0.1.0/gatehouse/__init__.py +57 -0
- gatehouse_ai-0.1.0/gatehouse/adapters/__init__.py +9 -0
- gatehouse_ai-0.1.0/gatehouse/adapters/decorator.py +80 -0
- gatehouse_ai-0.1.0/gatehouse/adapters/mcp.py +109 -0
- gatehouse_ai-0.1.0/gatehouse/approvals.py +263 -0
- gatehouse_ai-0.1.0/gatehouse/audit.py +153 -0
- gatehouse_ai-0.1.0/gatehouse/cli.py +460 -0
- gatehouse_ai-0.1.0/gatehouse/dispositions.py +90 -0
- gatehouse_ai-0.1.0/gatehouse/gate.py +201 -0
- gatehouse_ai-0.1.0/gatehouse/policy.py +576 -0
- gatehouse_ai-0.1.0/gatehouse/py.typed +0 -0
- gatehouse_ai-0.1.0/gatehouse/registry.py +70 -0
- gatehouse_ai-0.1.0/gatehouse/server.py +262 -0
- gatehouse_ai-0.1.0/gatehouse/testing.py +150 -0
- gatehouse_ai-0.1.0/gatehouse/ui/assets/index-C1DzDQEq.css +1 -0
- gatehouse_ai-0.1.0/gatehouse/ui/assets/index-CccY418k.js +9 -0
- gatehouse_ai-0.1.0/gatehouse/ui/index.html +18 -0
- gatehouse_ai-0.1.0/gatehouse/usage.py +128 -0
- gatehouse_ai-0.1.0/pyproject.toml +77 -0
- gatehouse_ai-0.1.0/tests/test_approvals.py +232 -0
- gatehouse_ai-0.1.0/tests/test_audit.py +131 -0
- gatehouse_ai-0.1.0/tests/test_cli.py +405 -0
- gatehouse_ai-0.1.0/tests/test_decorator.py +163 -0
- gatehouse_ai-0.1.0/tests/test_dispositions.py +60 -0
- gatehouse_ai-0.1.0/tests/test_gate.py +116 -0
- gatehouse_ai-0.1.0/tests/test_limits.py +259 -0
- gatehouse_ai-0.1.0/tests/test_mcp_proxy.py +215 -0
- gatehouse_ai-0.1.0/tests/test_policy.py +376 -0
- gatehouse_ai-0.1.0/tests/test_registry.py +83 -0
- gatehouse_ai-0.1.0/tests/test_server.py +193 -0
- gatehouse_ai-0.1.0/tests/test_testing.py +206 -0
- gatehouse_ai-0.1.0/ui/index.html +17 -0
- gatehouse_ai-0.1.0/ui/package-lock.json +2430 -0
- gatehouse_ai-0.1.0/ui/package.json +24 -0
- gatehouse_ai-0.1.0/ui/src/Agents.tsx +89 -0
- gatehouse_ai-0.1.0/ui/src/App.tsx +139 -0
- gatehouse_ai-0.1.0/ui/src/Approvals.tsx +185 -0
- gatehouse_ai-0.1.0/ui/src/LiveFeed.tsx +104 -0
- gatehouse_ai-0.1.0/ui/src/Policies.tsx +153 -0
- gatehouse_ai-0.1.0/ui/src/api.ts +32 -0
- gatehouse_ai-0.1.0/ui/src/index.css +37 -0
- gatehouse_ai-0.1.0/ui/src/main.tsx +10 -0
- gatehouse_ai-0.1.0/ui/src/shared.tsx +104 -0
- gatehouse_ai-0.1.0/ui/src/types.ts +100 -0
- gatehouse_ai-0.1.0/ui/tsconfig.json +16 -0
- gatehouse_ai-0.1.0/ui/vite.config.ts +19 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(.venv/bin/pytest)",
|
|
5
|
+
"Bash(.venv/bin/ruff check *)",
|
|
6
|
+
"Bash(.venv/bin/ruff format *)",
|
|
7
|
+
"Bash(.venv/bin/mypy gatehouse *)",
|
|
8
|
+
"Bash(git add *)",
|
|
9
|
+
"Bash(git commit *)",
|
|
10
|
+
"Bash(git commit -q -m 'feat: add disposition types and declarative policy engine *)",
|
|
11
|
+
"Bash(.venv/bin/pytest tests/test_server.py)",
|
|
12
|
+
"Bash(.venv/bin/pytest tests/test_decorator.py)",
|
|
13
|
+
"Bash(.venv/bin/python *)",
|
|
14
|
+
"Bash(.venv/bin/gatehouse test *)",
|
|
15
|
+
"Bash(perl -pi -e 's/^version = \"0\\\\.1\\\\.0\\\\.dev0\"$/version = \"0.1.0\"/' pyproject.toml)",
|
|
16
|
+
"Bash(perl -pi -e 's/^__version__ = \"0\\\\.1\\\\.0\\\\.dev0\"$/__version__ = \"0.1.0\"/' gatehouse/__init__.py)",
|
|
17
|
+
"Bash(.venv/bin/pip install *)",
|
|
18
|
+
"Bash(.venv/bin/gatehouse --version)",
|
|
19
|
+
"Bash(git tag *)"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
checks:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
cache: pip
|
|
21
|
+
- name: Install
|
|
22
|
+
run: pip install -e ".[dev]"
|
|
23
|
+
- name: Lint
|
|
24
|
+
run: ruff check .
|
|
25
|
+
- name: Format
|
|
26
|
+
run: ruff format --check .
|
|
27
|
+
- name: Type-check
|
|
28
|
+
run: mypy gatehouse tests
|
|
29
|
+
- name: Test
|
|
30
|
+
run: pytest
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# OS
|
|
2
|
+
.DS_Store
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
|
|
11
|
+
# Environments
|
|
12
|
+
.venv/
|
|
13
|
+
|
|
14
|
+
# Tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
node_modules/
|
|
20
|
+
gatehouse-audit.jsonl
|
|
21
|
+
gatehouse-approvals/
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Gatehouse are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] — 2026-07-09
|
|
8
|
+
|
|
9
|
+
First release: the complete local gate.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Policy engine** — declarative YAML rules resolving every proposed
|
|
14
|
+
action to one binding disposition (`allow`, `deny`, `require_approval`,
|
|
15
|
+
`allow_with_flag`); agent/tool matching with wildcards; parameter
|
|
16
|
+
constraints (bounds, allowlists, patterns); `where` selection predicates
|
|
17
|
+
distinct from enforced `params`; recurring time windows; rate limits and
|
|
18
|
+
cumulative budgets scoped per session or per rolling clock window.
|
|
19
|
+
Deny by default; most restrictive candidate wins; a constraint that
|
|
20
|
+
cannot be checked counts as breached.
|
|
21
|
+
- **Agent registry** — declared agents with a human owner, autonomy tier
|
|
22
|
+
(`read_only` / `supervised` / `governed` / `human_only`), explicit tool
|
|
23
|
+
grant, and credentials reference. Unregistered agents are denied before
|
|
24
|
+
policy is consulted; tiers cap what any rule may grant.
|
|
25
|
+
- **Audit log** — append-only JSONL with disposition, reason, matched
|
|
26
|
+
rules, policy version and SHA-256 digest, latency, and a parameters
|
|
27
|
+
digest (never verbatim arguments). Usage counters are rebuilt from the
|
|
28
|
+
log on startup.
|
|
29
|
+
- **Approval queue** — held actions with TTL; expiry fails closed;
|
|
30
|
+
resolutions (who, why, time-to-decision) are appended audit records.
|
|
31
|
+
- **Policy test harness** — `*.test.yaml` scenarios with expected
|
|
32
|
+
dispositions; ordered scenarios share usage state so limit behaviour is
|
|
33
|
+
testable.
|
|
34
|
+
- **CLI** — `check` (advisory, records nothing; exit codes 0/2/1),
|
|
35
|
+
`test`, `audit tail|stats`, `approvals`, `approve`, `deny`, `ui`, `run`.
|
|
36
|
+
- **Web dashboard** — live disposition feed over WebSocket, approval cards
|
|
37
|
+
with countdowns, agent registry view with activity sparklines, read-only
|
|
38
|
+
policy view with test results. Localhost only, no auth.
|
|
39
|
+
- **MCP proxy** (`gatehouse run`, `mcp` extra) — gates any MCP server
|
|
40
|
+
transparently; one session id per connection.
|
|
41
|
+
- **`@gated` decorator** — direct Python integration; refusals raise
|
|
42
|
+
`ActionDeniedError` carrying the full evaluation.
|
|
43
|
+
- **Travel-agent demo** — a scripted agent walking all four dispositions
|
|
44
|
+
through mock MCP tools, with policy tests.
|
|
45
|
+
|
|
46
|
+
[0.1.0]: https://github.com/wkitcheah/gatehouse/releases/tag/v0.1.0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for looking under the hood. Gatehouse is small on purpose; keeping
|
|
4
|
+
it understandable is a feature.
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
python3.11 -m venv .venv && source .venv/bin/activate # 3.11 or 3.12
|
|
10
|
+
pip install -e ".[dev]"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The dashboard frontend only needs Node if you change it: source in `ui/`,
|
|
14
|
+
built assets committed under `gatehouse/ui/` (`cd ui && npm install &&
|
|
15
|
+
npm run build`). Commit the rebuilt assets with the source change.
|
|
16
|
+
|
|
17
|
+
## Before every commit
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pytest
|
|
21
|
+
ruff check . && ruff format --check .
|
|
22
|
+
mypy gatehouse tests
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
All three must pass — CI runs exactly these on Python 3.11 and 3.12.
|
|
26
|
+
|
|
27
|
+
## What we hold the line on
|
|
28
|
+
|
|
29
|
+
- **Deny by default; explicit allow; every disposition explainable.** Any
|
|
30
|
+
change to evaluation semantics must keep reasons exact and defensible.
|
|
31
|
+
- **The policy engine stays stateless** and the audit log append-only.
|
|
32
|
+
State lives in the gate; advisory paths (`check`, the test harness)
|
|
33
|
+
record nothing.
|
|
34
|
+
- **Runtime dependencies stay minimal** (pydantic, PyYAML, click). Anything
|
|
35
|
+
heavier belongs in an extra (`ui`, `mcp`). Open an issue before adding a
|
|
36
|
+
dependency.
|
|
37
|
+
- **Core makes no network calls.** Integrations live in `gatehouse/adapters/`.
|
|
38
|
+
- **Every rule type and tier gets positive and negative tests.** New policy
|
|
39
|
+
semantics need engine tests *and* an end-to-end exercise; a behaviour
|
|
40
|
+
that only a unit test has seen is not done.
|
|
41
|
+
- **Examples run as written.** If a doc shows a command and its output,
|
|
42
|
+
both must be real.
|
|
43
|
+
|
|
44
|
+
## Decisions
|
|
45
|
+
|
|
46
|
+
Changes that affect policy semantics (precedence, defaults, tier
|
|
47
|
+
interactions, what counts toward a limit) get an entry in
|
|
48
|
+
[`docs/decisions.md`](docs/decisions.md): what was decided and why. If
|
|
49
|
+
you're unsure whether your change qualifies, it does.
|
|
50
|
+
|
|
51
|
+
One boundary worth knowing: tamper-evident audit mechanisms (hash chains,
|
|
52
|
+
cross-signing, cryptographic binding of records to execution) are out of
|
|
53
|
+
scope for this project. The audit log is ordinary structured JSONL by
|
|
54
|
+
design; please don't send PRs adding cryptographic evidence schemes.
|
|
55
|
+
|
|
56
|
+
## Commits
|
|
57
|
+
|
|
58
|
+
Conventional commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`,
|
|
59
|
+
`chore:`, `ci:`), small and self-contained, each one green.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Gatehouse — a runtime policy gate for AI agents
|
|
2
|
+
## Reference implementation of the Minimum Viable Control Plane (MVCP)
|
|
3
|
+
|
|
4
|
+
You are helping me build Gatehouse, an open-source runtime governance layer for AI agents. This file is the project's standing context; read it fully before doing anything. **This is a public portfolio project** — code quality, documentation, and conceptual clarity matter as much as function, because its audience includes hiring managers, practitioners, and readers of my newsletter.
|
|
5
|
+
|
|
6
|
+
## What Gatehouse is
|
|
7
|
+
|
|
8
|
+
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. The evaluation returns one binding disposition: **allow**, **deny**, **require_approval** (held for a human), or **allow_with_flag** (proceeds, but marked for monitoring). Every evaluation is written to a structured audit log.
|
|
9
|
+
|
|
10
|
+
The conceptual grounding is published work:
|
|
11
|
+
- My article "From Prevention to Control" (April 2026): the Agentic Control Plane — single choke point, agent registry, identity for non-human actors, policy engines before every action, autonomy tiering, verification gates, observability as evidence.
|
|
12
|
+
- My article "From the Control Gap to the Agency Gap" (May 2026): the MVCP's five questions — Identity, Authority, Visibility, Containment, Recovery.
|
|
13
|
+
- MAS's SAFR white paper (July 2026): independent industry validation; our disposition vocabulary deliberately echoes its four outcomes, with attribution in the README.
|
|
14
|
+
|
|
15
|
+
Gatehouse is the working answer to the MVCP questions, and the docs must map features to those questions explicitly (`docs/mvcp-mapping.md`).
|
|
16
|
+
|
|
17
|
+
## Hard constraints (never violate)
|
|
18
|
+
|
|
19
|
+
1. **Patent-safe scope.** I have unrelated pending IP in tamper-evident attestation. Gatehouse must use only conventional, textbook mechanisms. Explicitly out of scope, do not implement even if it seems like an improvement: hash-chained or cryptographically tamper-evident logs; cross-device or cross-process co-signing/attestation of records; making the audit record a cryptographic precondition of execution; content-addressed evidence binding. The audit log is ordinary structured JSONL. If a feature request drifts toward "tamper-evident," stop and flag it to me instead.
|
|
20
|
+
2. **Clean-room hygiene.** No code, configuration, or examples derived from any employer's systems. All examples use fictional scenarios (the demo domain is a fictional travel-booking agent, not finance-specific and not any real company).
|
|
21
|
+
3. **No network calls in core.** The gate must run fully local. Integrations (webhooks) live in optional adapters.
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
agent ──proposed action──▶ Gatehouse ──disposition──▶ tool / blocked / held
|
|
27
|
+
│
|
|
28
|
+
┌────────────────┼────────────────┐
|
|
29
|
+
agent registry policy engine audit log
|
|
30
|
+
(identity) (authority) (visibility/recovery)
|
|
31
|
+
│
|
|
32
|
+
approval queue
|
|
33
|
+
(containment)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Package layout (`gatehouse-ai` on PyPI, import name `gatehouse`)
|
|
37
|
+
- `gatehouse/registry.py` — Agent Registry: agents are declared in config with owner, allowed tools, autonomy tier, and credentials reference. Unregistered agent → automatic deny. ("You don't exist unless you're registered.")
|
|
38
|
+
- `gatehouse/policy.py` — policy engine. Declarative YAML policies: per-agent and per-tool rules; parameter constraints (numeric limits, allowlists, regex); conditions (time windows, rate limits, cumulative limits per session); autonomy tiers mapping to default dispositions:
|
|
39
|
+
- `read_only` → non-mutating tools allowed, everything else denied
|
|
40
|
+
- `supervised` → mutating actions require_approval
|
|
41
|
+
- `governed` → allowed within constraints, breaches deny or escalate
|
|
42
|
+
- `human_only` → always require_approval
|
|
43
|
+
- `gatehouse/dispositions.py` — evaluation result types; every disposition carries the matched rules, the policy file digest and version, and a human-readable reason string.
|
|
44
|
+
- `gatehouse/audit.py` — JSONL audit log: timestamp, agent id, tool, parameters digest, disposition, matched rules, policy version, latency, approval outcome if any. Plus `gatehouse audit tail` and `gatehouse audit stats` CLI views.
|
|
45
|
+
- `gatehouse/approvals.py` — pending-approval queue with TTL; approve/deny via CLI (`gatehouse approve <id>`) and an optional webhook adapter for chat-ops.
|
|
46
|
+
- `gatehouse/adapters/mcp.py` — the flagship integration: an MCP proxy. Gatehouse wraps any MCP server; the agent connects to Gatehouse instead, and every tool call is gated transparently. This makes Gatehouse usable with real agent stacks immediately.
|
|
47
|
+
- `gatehouse/adapters/decorator.py` — `@gated(agent="...")` decorator for direct Python integration.
|
|
48
|
+
- `gatehouse/testing.py` — policy test harness: scenario YAML files declare (agent, tool, params) → expected disposition; `gatehouse test policies/` runs them like unit tests. Policies without tests are policies waiting to be bypassed.
|
|
49
|
+
- `cli.py` — `gatehouse run` (proxy mode), `gatehouse check` (one-shot evaluation), `gatehouse test`, `gatehouse audit`, `gatehouse approve|deny`, `gatehouse ui` (launch dashboard).
|
|
50
|
+
- `gatehouse/server.py` + `ui/` — **web dashboard**, launched with `gatehouse ui` at localhost. FastAPI backend exposing the audit stream, approval queue, registry, and policies over REST + WebSocket; frontend is a single-page app (React + Vite + Tailwind) built into static assets and served by the backend, so end users never run npm. Four views:
|
|
51
|
+
1. **Live feed** — dispositions streaming in real time as the agent acts, color-coded (allow green, deny red, held amber, flagged blue), each expandable to show matched rules, policy version, and reason string.
|
|
52
|
+
2. **Approvals** — pending actions as cards with full context and Approve/Deny buttons plus TTL countdown; resolving a card unblocks the waiting agent instantly (WebSocket). This is the demo centerpiece: containment made visible.
|
|
53
|
+
3. **Agents** — the registry: each agent's identity, autonomy tier, allowed tools, and recent activity sparkline.
|
|
54
|
+
4. **Policies** — read-only policy viewer with the test-harness results per policy (green/red), so "policies without tests" is visible shame.
|
|
55
|
+
Design: clean, dense, dark-theme dashboard aesthetic consistent with a governance product — no gradients-and-emoji AI-slop styling; this UI will appear in screenshots, GIFs, and conference slides. WebSocket for live updates; no polling. No auth in v0.1 (localhost only, stated clearly in docs).
|
|
56
|
+
|
|
57
|
+
### Demo (`examples/travel-agent/`)
|
|
58
|
+
A small scripted agent (no LLM required to run; optional LLM mode) that books fictional flights and hotels through mock MCP tools. The demo script walks the four dispositions: a permitted search (allow), a booking over the price limit (deny, with the reason shown), a cancellation (require_approval — terminal waits, second window approves), and a booking to a flagged destination (allow_with_flag). This demo is the README GIF and the newsletter material.
|
|
59
|
+
|
|
60
|
+
## Engineering conventions
|
|
61
|
+
- Python 3.11+, `pyproject.toml`; runtime deps minimal: pydantic, PyYAML, click. MCP SDK only inside the adapter extra (`pip install gatehouse-ai[mcp]`). Ask before adding anything else.
|
|
62
|
+
- Full type hints; mypy and ruff clean; pytest with high coverage on `policy.py` and `dispositions.py` — every rule type and tier gets positive and negative tests.
|
|
63
|
+
- GitHub Actions CI: lint, type-check, tests on 3.11/3.12. Badge in README.
|
|
64
|
+
- Docs: `README.md` (90-second pitch, architecture diagram, quickstart in 3 commands, GIF), `docs/mvcp-mapping.md`, `docs/policy-reference.md`, `docs/writing-policies.md` (tutorial), `CONTRIBUTING.md`, `CHANGELOG.md`.
|
|
65
|
+
- License: MIT placeholder — **flagged for my attorney's confirmation before the repo goes public** (MIT vs Apache-2.0 patent-grant implications).
|
|
66
|
+
- Conventional commits; keep history clean and honest — this repo's commit log is part of the portfolio.
|
|
67
|
+
|
|
68
|
+
## Build order
|
|
69
|
+
1. Core types + policy engine + dispositions, with tests (the heart; get it right first).
|
|
70
|
+
2. Registry + autonomy tiers + audit log.
|
|
71
|
+
3. CLI (`check`, `test`, `audit`) + policy test harness.
|
|
72
|
+
4. Approval queue + CLI approvals.
|
|
73
|
+
5. Web dashboard: FastAPI server + REST/WebSocket API, then the four UI views (live feed and approvals first — they're the demo).
|
|
74
|
+
6. MCP proxy adapter + travel-agent demo, driven end-to-end through the dashboard.
|
|
75
|
+
7. Docs, screen recording of the dashboard demo, CI polish, v0.1.0 tag.
|
|
76
|
+
|
|
77
|
+
The demo recording for the README: terminal on the left running the travel agent, dashboard on the right — a deny appears in the live feed with its reason, then a cancellation lands in Approvals, the card is clicked, and the terminal proceeds. That fifteen seconds is the whole pitch.
|
|
78
|
+
|
|
79
|
+
## Repository & attribution conventions
|
|
80
|
+
- Sole authorship: all commits authored as me, using my personal name and personal email (`git config user.name / user.email` set locally in this repo before the first commit). Never add co-author trailers of any kind to commit messages.
|
|
81
|
+
- This CLAUDE.md and any internal working notes are development-environment files, not project files: keep them out of version control via `.git/info/exclude` (not `.gitignore`, which is itself tracked and visible).
|
|
82
|
+
- All tracked content — commit messages, code comments, docs, `docs/decisions.md` — is written in neutral first-person engineering voice with no references to development tooling or assistants.
|
|
83
|
+
- Commit in small, reviewed increments with conventional-commit messages as each piece is completed and tested — an honest, natural history. No backdating, no squashing into artificial narratives.
|
|
84
|
+
|
|
85
|
+
## Build status (updated 2026-07-09 — keep this section current)
|
|
86
|
+
|
|
87
|
+
ALL SEVEN STEPS of the build order are DONE; **v0.1.0 is tagged**
|
|
88
|
+
(2026-07-09). 182 tests green, ruff + strict mypy clean, CI workflow in
|
|
89
|
+
`.github/workflows/ci.yml`. Docs complete: README (badge, demo section
|
|
90
|
+
with recording placeholder, attribution), docs/mvcp-mapping.md,
|
|
91
|
+
docs/policy-reference.md, docs/writing-policies.md (all commands
|
|
92
|
+
verified), CONTRIBUTING.md, CHANGELOG.md.
|
|
93
|
+
|
|
94
|
+
**Release verification (2026-07-09) — both passed:**
|
|
95
|
+
- *Packaging*: sdist + wheel built (`python -m build` from repo root,
|
|
96
|
+
output in gitignored `dist/`); wheel verified to contain
|
|
97
|
+
`gatehouse/ui/*` assets and `py.typed`; installed with `[ui,mcp]` into
|
|
98
|
+
a fresh venv outside the repo; CLI, demo policy tests, wheel-served
|
|
99
|
+
dashboard, and the full travel demo (incl. MCP proxy + approval round
|
|
100
|
+
trip) all worked from the installed artifact. `dist/` was rebuilt from
|
|
101
|
+
the final tagged tree and is upload-ready (`twine upload dist/*`).
|
|
102
|
+
- *Visual QA*: Playwright screenshots of all four dashboard views against
|
|
103
|
+
live demo data. Two defects found and fixed (rebuilt assets committed):
|
|
104
|
+
`break-all` → `break-words` in the live-feed detail grid, and stat
|
|
105
|
+
tiles no longer count approval-resolution records (a held action showed
|
|
106
|
+
HELD 2 once resolved). The v0.1.0 tag was re-pointed (local, pre-push)
|
|
107
|
+
to include both fixes; HEAD == v0.1.0 == the tree `dist/` was built
|
|
108
|
+
from.
|
|
109
|
+
|
|
110
|
+
Remaining before the repo goes public, all user actions — **the public
|
|
111
|
+
push is deliberately parked (user decision 2026-07-09)**: (1) attorney
|
|
112
|
+
confirms the license — LICENSE and pyproject say MIT; the patent-grant
|
|
113
|
+
trade-off was discussed on 2026-07-09 (Apache §3 grant vs MIT silence, in
|
|
114
|
+
light of the pending tamper-evident-attestation IP) and **Wai Kit is
|
|
115
|
+
inclined toward MIT**, so the files as they stand are likely final and
|
|
116
|
+
the attorney step is confirmation, not a rework.
|
|
117
|
+
(2) GitHub URL is CONFIRMED as `github.com/wkitcheah/gatehouse` and set
|
|
118
|
+
everywhere; the v0.1.0 tag was re-pointed (locally, pre-push) to include
|
|
119
|
+
it. (3) DONE 2026-07-11: demo recorded (18s, dashboard over terminal),
|
|
120
|
+
converted to `docs/demo.gif` (2.3 MB, 1000px, path-leak-free) and
|
|
121
|
+
embedded in the README; two path leaks in demo output were fixed first
|
|
122
|
+
(agent.py hints, proxy status line). (4) when unparked: push + verify CI
|
|
123
|
+
+ PyPI publish (`twine upload dist/*`; dist/ is current with the tag). Deferred features: demo's optional LLM mode;
|
|
124
|
+
webhook adapter for chat-ops approvals; session-as-lease (v0.2 candidate,
|
|
125
|
+
analysed in docs/decisions.md).
|
|
126
|
+
|
|
127
|
+
Step 6 additions: `gatehouse/adapters/` — `@gated` decorator
|
|
128
|
+
(ActionDeniedError carries the evaluation) and the MCP proxy
|
|
129
|
+
(`gatehouse run --agent X -p ... -r ... -- <server cmd>`; deny → error
|
|
130
|
+
result with reason, require_approval blocks async on the queue; one
|
|
131
|
+
session id minted per proxy run). `examples/travel-agent/` — scripted
|
|
132
|
+
agent walks all four dispositions, demo policy + registry +
|
|
133
|
+
travel.test.yaml (12 scenarios incl. salami-slicing). mypy override
|
|
134
|
+
relaxes untyped-decorator checks for gatehouse.adapters.mcp only.
|
|
135
|
+
|
|
136
|
+
What exists: dispositions + policy engine (`where` selects / `params`
|
|
137
|
+
enforces; rate + cumulative limits with session scoping), registry, JSONL
|
|
138
|
+
audit (params as digests; usage replayed from log on restart), Gate
|
|
139
|
+
(+ pure `decide()` for advisory paths), approval queue (file-per-request,
|
|
140
|
+
TTL fails closed, resolutions appended to audit), policy test harness
|
|
141
|
+
(`*.test.yaml`), CLI (`check|test|audit|approvals|approve|deny|ui`), FastAPI
|
|
142
|
+
server (REST + WS push) and the React dashboard (four views; source in
|
|
143
|
+
`ui/`, built assets committed under `gatehouse/ui/`; deps in `[ui]` extra).
|
|
144
|
+
|
|
145
|
+
Notable choices beyond the original brief, all recorded with rationale in
|
|
146
|
+
`docs/decisions.md`: most-restrictive-wins precedence; tiers as ceilings;
|
|
147
|
+
unverifiable constraint = breach; identity before authority; caller-supplied
|
|
148
|
+
`session_id` (MCP adapter should derive it from the connection); advisory
|
|
149
|
+
evaluation never records; `check` exit codes 0/2/1 = proceed/approval/deny.
|
|
150
|
+
|
|
151
|
+
Environment: venv at `.venv`; before each commit run
|
|
152
|
+
`.venv/bin/pytest && .venv/bin/ruff check . && .venv/bin/ruff format --check . && .venv/bin/mypy gatehouse tests`.
|
|
153
|
+
Frontend rebuild: `cd ui && npm run build`. License remains MIT placeholder
|
|
154
|
+
pending attorney review before the repo goes public.
|
|
155
|
+
|
|
156
|
+
## Working style
|
|
157
|
+
Work incrementally, keep tests green, and keep the public-audience bar in mind: clear names beat clever ones, docstrings explain *why*, and every example must run as written. When a design choice affects the policy semantics (rule precedence, deny-by-default behavior, tier interactions), surface it to me and record it in `docs/decisions.md` — those decisions are newsletter material. Default posture everywhere: **deny by default; explicit allow; every disposition explainable.**
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cheah Wai Kit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gatehouse-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A runtime policy gate for AI agents: every tool call is evaluated against declarative policy before it executes.
|
|
5
|
+
Project-URL: Homepage, https://github.com/wkitcheah/gatehouse
|
|
6
|
+
Author-email: Cheah Wai Kit <wkitcheah@users.noreply.github.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: ai-agents,audit,governance,guardrails,policy
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: click>=8.1
|
|
19
|
+
Requires-Dist: pydantic>=2.0
|
|
20
|
+
Requires-Dist: pyyaml>=6.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: fastapi>=0.111; extra == 'dev'
|
|
23
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
24
|
+
Requires-Dist: mcp>=1.2; extra == 'dev'
|
|
25
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
28
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
29
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'dev'
|
|
30
|
+
Provides-Extra: mcp
|
|
31
|
+
Requires-Dist: mcp>=1.2; extra == 'mcp'
|
|
32
|
+
Provides-Extra: ui
|
|
33
|
+
Requires-Dist: fastapi>=0.111; extra == 'ui'
|
|
34
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'ui'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# Gatehouse
|
|
38
|
+
|
|
39
|
+
[](https://github.com/wkitcheah/gatehouse/actions/workflows/ci.yml)
|
|
40
|
+
|
|
41
|
+
**A runtime policy gate for AI agents.**
|
|
42
|
+
|
|
43
|
+
Gatehouse sits between an AI agent and its tools. Every action the agent
|
|
44
|
+
proposes — a tool call, an API invocation — is intercepted and evaluated
|
|
45
|
+
against declarative policy *before* it executes. Every evaluation resolves to
|
|
46
|
+
one binding disposition:
|
|
47
|
+
|
|
48
|
+
| Disposition | Meaning |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| `allow` | The action proceeds. |
|
|
51
|
+
| `deny` | The action is blocked, with a reason. |
|
|
52
|
+
| `require_approval` | The action is held until a human approves it. |
|
|
53
|
+
| `allow_with_flag` | The action proceeds, but is marked for monitoring. |
|
|
54
|
+
|
|
55
|
+
Every evaluation is written to a structured audit log. The default posture is
|
|
56
|
+
**deny by default, explicit allow, every disposition explainable**.
|
|
57
|
+
|
|
58
|
+
> **Status:** v0.1.0 — the complete local gate: engine, registry, audit,
|
|
59
|
+
> approvals, CLI, dashboard, MCP proxy, and a runnable demo. Young software;
|
|
60
|
+
> interfaces may still move between minor versions.
|
|
61
|
+
|
|
62
|
+
## The demo in one paragraph
|
|
63
|
+
|
|
64
|
+
A scripted travel agent books fictional flights through mock MCP tools with
|
|
65
|
+
Gatehouse in between: a permitted search streams into the dashboard as
|
|
66
|
+
`allow`, an over-budget booking comes back `deny` with the exact reason, a
|
|
67
|
+
watchlisted destination proceeds as `allow_with_flag`, and a cancellation
|
|
68
|
+
stops the terminal until you click **Approve** in the dashboard — then it
|
|
69
|
+
completes instantly. Run it yourself:
|
|
70
|
+
[examples/travel-agent](examples/travel-agent/README.md).
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
## Features
|
|
75
|
+
|
|
76
|
+
- **Policy engine** — declarative YAML rules with agent/tool matching,
|
|
77
|
+
parameter constraints (bounds, allowlists, regex), selection predicates
|
|
78
|
+
(`where` selects, `params` enforces), time windows, and rate/cumulative
|
|
79
|
+
limits with per-session or rolling clock scopes. Deny by default; when
|
|
80
|
+
rules disagree, the most restrictive disposition wins.
|
|
81
|
+
- **Agent registry** — agents are declared with a human owner, an autonomy
|
|
82
|
+
tier (`read_only` / `supervised` / `governed` / `human_only`), an explicit
|
|
83
|
+
tool grant, and a credentials *reference*, never a value. Unregistered
|
|
84
|
+
agents are denied before policy is consulted. Tiers are ceilings: no rule
|
|
85
|
+
can grant past them.
|
|
86
|
+
- **Audit log** — append-only JSONL, one record per evaluation: disposition,
|
|
87
|
+
reason, matched rules, policy version and digest, latency. Parameters are
|
|
88
|
+
stored as a digest, not verbatim. Usage counters (budgets, rate limits) are
|
|
89
|
+
rebuilt from the log on restart, so restarting the gate never resets them.
|
|
90
|
+
- **Approval queue** — `require_approval` actions are held with a TTL and
|
|
91
|
+
resolved from another process; expiry fails closed. Every resolution is an
|
|
92
|
+
appended audit record naming who decided and why.
|
|
93
|
+
- **Policy test harness** — scenario files declare action → expected
|
|
94
|
+
disposition; scenarios share usage state so limit behaviour is testable.
|
|
95
|
+
Policies without tests are policies waiting to be bypassed.
|
|
96
|
+
- **CLI** — `gatehouse check` (one-shot advisory evaluation, records
|
|
97
|
+
nothing), `gatehouse test`, `gatehouse audit tail|stats`,
|
|
98
|
+
`gatehouse approvals` / `approve` / `deny`, `gatehouse ui`.
|
|
99
|
+
- **Web dashboard** — live disposition feed over WebSocket, approval cards
|
|
100
|
+
with TTL countdowns, the agent registry with activity sparklines, and a
|
|
101
|
+
read-only policy view with test results. Localhost only, no auth in v0.1.
|
|
102
|
+
|
|
103
|
+
## Quickstart
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install -e ".[ui]" # from a checkout (PyPI: gatehouse-ai, once published)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Declare an agent and a policy:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
# agents.yaml
|
|
113
|
+
agents:
|
|
114
|
+
- id: trip-planner
|
|
115
|
+
owner: you@example.test
|
|
116
|
+
tier: governed
|
|
117
|
+
allowed_tools: [search_flights, book_flight]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
# policy.yaml
|
|
122
|
+
version: 1
|
|
123
|
+
tools:
|
|
124
|
+
search_flights: {mutating: false}
|
|
125
|
+
book_flight: {mutating: true}
|
|
126
|
+
rules:
|
|
127
|
+
- id: allow-search
|
|
128
|
+
agents: [trip-planner]
|
|
129
|
+
tools: [search_flights]
|
|
130
|
+
disposition: allow
|
|
131
|
+
- id: book-within-budget
|
|
132
|
+
agents: [trip-planner]
|
|
133
|
+
tools: [book_flight]
|
|
134
|
+
disposition: allow
|
|
135
|
+
params:
|
|
136
|
+
price: {max: 500}
|
|
137
|
+
limits:
|
|
138
|
+
- {cumulative: price, max: 2000, per: session}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Ask the gate what would happen:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
$ gatehouse check trip-planner book_flight -p policy.yaml -r agents.yaml --param price=812
|
|
145
|
+
deny trip-planner → book_flight
|
|
146
|
+
reason: rule 'book-within-budget': parameter 'price' = 812 exceeds the maximum 500.0
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Gate any MCP server without changing the agent or the server — the agent
|
|
150
|
+
connects to Gatehouse instead:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pip install "gatehouse-ai[mcp]"
|
|
154
|
+
gatehouse run --agent trip-planner -p policy.yaml -r agents.yaml -- <your MCP server command>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Or gate plain Python calls directly:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from gatehouse import ApprovalQueue, AuditLog, Gate, PolicyEngine, Registry
|
|
161
|
+
from gatehouse.adapters import gated
|
|
162
|
+
|
|
163
|
+
gate = Gate(
|
|
164
|
+
Registry.from_file("agents.yaml"),
|
|
165
|
+
PolicyEngine.from_file("policy.yaml"),
|
|
166
|
+
AuditLog("gatehouse-audit.jsonl"),
|
|
167
|
+
approvals=ApprovalQueue("gatehouse-approvals"),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
@gated(gate, agent="trip-planner")
|
|
171
|
+
def book_flight(price: float, destination: str) -> str:
|
|
172
|
+
... # only runs if the gate says so; refusals raise ActionDeniedError
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Watch it live:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
gatehouse ui -p policy.yaml -r agents.yaml # http://127.0.0.1:8422
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Documentation
|
|
182
|
+
|
|
183
|
+
- [Writing policies](docs/writing-policies.md) — the tutorial; every command
|
|
184
|
+
runs as written.
|
|
185
|
+
- [Policy reference](docs/policy-reference.md) — every field, and the
|
|
186
|
+
evaluation semantics.
|
|
187
|
+
- [MVCP mapping](docs/mvcp-mapping.md) — how each feature answers the five
|
|
188
|
+
control-plane questions: identity, authority, visibility, containment,
|
|
189
|
+
recovery.
|
|
190
|
+
- [Design decisions](docs/decisions.md) — why tiers are ceilings, why an
|
|
191
|
+
uncheckable constraint is a breach, what a "session" is, and every other
|
|
192
|
+
choice that shapes the semantics, with rationale.
|
|
193
|
+
|
|
194
|
+
## Background and attribution
|
|
195
|
+
|
|
196
|
+
The conceptual grounding is the Agentic Control Plane ("From Prevention to
|
|
197
|
+
Control") and the Minimum Viable Control Plane ("From the Control Gap to
|
|
198
|
+
the Agency Gap"). The four-disposition vocabulary deliberately echoes the
|
|
199
|
+
four outcomes described in the Monetary Authority of Singapore's SAFR
|
|
200
|
+
white paper (July 2026) — convergent framings of the same idea: agent
|
|
201
|
+
actions should resolve to a small, closed set of binding outcomes.
|
|
202
|
+
|
|
203
|
+
## Development
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
pip install -e ".[dev]"
|
|
207
|
+
pytest && ruff check . && mypy gatehouse tests
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The frontend source lives in `ui/`; built assets are committed under
|
|
211
|
+
`gatehouse/ui/` so installed users never need npm. Rebuild with
|
|
212
|
+
`cd ui && npm install && npm run build`.
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT
|