# PatchPass **A human approval layer for AI agents.** > Review changes. Approve intent. Let agents proceed. Agents submit structured change requests containing code diffs, configuration updates, or custom values. Humans review and approve them in the browser, then agents verify the platform-signed decision through the API before applying anything. --- ## How it works ``` Agent PatchPass Human │ create_request ───────────► │ │ │ (diffs/config/custom) │ normalize · hash · store │ │ ◄─── request_id + URL ────────│ ──── notify (WS) ──────────► │ reviews in browser │ │ │ APPROVE / REJECT / REQUEST_CHANGES │ get_request ──────────────► │ ◄──── signed decision ──────│ │ ◄─── state + receipt ─────────│ │ │ consume_approval ─────────► │ mark CONSUMED (single-use) │ │ ◄─── receipt (HMAC) ──────────│ │ │ apply changes │ │ ``` - **Decisions are platform-signed.** Approvals and rejections carry an HMAC-SHA256 receipt bound to the exact reviewed content (`content_hash`). If the agent changes anything after approval, the receipt no longer matches. - **Approvals are single-use.** An agent must `consume` an approval before proceeding; it cannot be replayed. - **Two ways in for agents:** a REST API (`/v1/...`) and an MCP server (`/mcp`), both authenticated with a per-agent API key and backed by the same service layer. ## Request states `PENDING` · `CHANGES_REQUESTED` · `APPROVED` · `REJECTED` · `EXPIRED` · `CONSUMED` · `CANCELLED` No update is possible after approval, rejection, expiry, consumption, or cancellation. A `REQUEST_CHANGES` decision requires a comment and returns the request to the agent, which can `update_request` to resubmit (highlighted in the UI as **UPDATED**). ## Limits | Limit | Value | |---|---| | Requests per agent per hour | 15 | | Agents per human | 5 | | Simultaneous pending requests per agent | 1–10 (human-configured, default 5) | | Request expiry | default 30 min, max 12 h | ## Tech stack | Layer | Technology | |---|---| | Backend | Node.js + TypeScript, [rjweb-server](https://server.rjweb.dev) | | ORM | Prisma 7 (`@prisma/adapter-pg`) | | Database | PostgreSQL | | Frontend | React + TypeScript + Tailwind CSS (Vite), served by the backend | | Realtime | rjweb WebSocket channels | | Tests | Vitest | ## Repository layout ``` Backend/ Node + TypeScript API (rjweb-server) + Prisma src/ index.ts server bootstrap + static UI serving lib/ service layer, auth, signing, limits, crons, MCP tools routes/ REST (/api, /v1), MCP (/mcp), WebSocket (/api/ws) tests/ Vitest integration tests prisma/schema.prisma UI/ React + Tailwind frontend (builds to UI/build) Dockerfile multi-stage build (UI + backend) docker-compose.yml backend + postgres .gitea/workflows/ CI: build · lint · test · push image ``` ## Running locally Prerequisites: Node 24+, pnpm, and Postgres (or Docker). ```bash # 1. Start Postgres (Docker) docker run -d --name patchpass-db \ -e POSTGRES_USER=patchpass -e POSTGRES_PASSWORD=patchpass -e POSTGRES_DB=patchpass \ -p 5433:5432 postgres:16-alpine # 2. Backend cd Backend cp ../example.env .env # then edit DATABASE_URL / INSTANCE_SECRET pnpm install pnpm generate pnpm migrate # apply migrations pnpm dev # http://localhost:5000 # 3. UI (separate terminal, for hot-reload dev) cd UI pnpm install pnpm dev # http://localhost:3000 (proxies /api to :5000) ``` For a production-style run, `cd UI && pnpm build` — the backend then serves `UI/build` at `/`. ### Tests ```bash cd Backend pnpm test # spins up against the test database (see vitest.config.ts) ``` ## Deploying ```bash cp example.env .env # set DATABASE_URL=...@db:5432/..., a strong INSTANCE_SECRET docker compose up -d --build ``` The backend applies migrations on boot and serves the UI. The first account to register becomes the **admin**. ## Connecting an agent Create an agent in the UI, then use its API key. **OpenClaw** — add to `~/.openclaw/openclaw.json`: ```json { "mcp": { "servers": { "patchpass": { "transport": "streamable-http", "url": "https://your-host/mcp", "headers": { "x-api-key": "pp_agent_..." } } } } } ``` **REST**: ```bash curl -X POST https://your-host/v1/change-requests \ -H "x-api-key: pp_agent_..." -H "Content-Type: application/json" \ -d '{"title":"Bump timeout","changes":[{"type":"config","path":"timeout","before":30,"after":60,"content_type":"integer"}]}' ``` Tell your agent: *before taking any consequential action, call `create_request`, wait for approval, then `consume_approval` before proceeding.* ## Privacy & data Humans can export all their data as JSON and delete their account (cascading to agents, requests, and notifications) from **Settings**. Agent icon URLs are fetched once for validation and never stored. See the in-app Privacy Policy and Terms of Service. ## License MIT-0