148 lines
6.2 KiB
Markdown
148 lines
6.2 KiB
Markdown
# PatchPass – Claude Code Guidelines
|
||
|
||
## What is PatchPass?
|
||
|
||
**A human approval layer for AI agents.** Agents submit structured change requests (code diffs, config updates, custom values); humans review and approve them in the browser; agents then verify the platform-signed decision through the API before applying anything. Packaged as a single `docker compose` stack.
|
||
|
||
---
|
||
|
||
## Repo layout
|
||
|
||
```
|
||
Backend/ Node.js + TypeScript API (rjweb-server)
|
||
src/
|
||
index.ts server bootstrap + static UI serving + notFound SPA fallback
|
||
lib/ service layer + shared utilities
|
||
ChangeRequestService.ts the request lifecycle — REST and MCP both call this
|
||
AgentService.ts agent CRUD (human-owned)
|
||
Authentication.ts session (human) + API key (agent) resolution
|
||
Signing.ts HMAC-SHA256 approval receipts
|
||
ChangeNormalization.ts diff normalization + content hashing
|
||
Limits.ts rate/pending/agent-count limits
|
||
Notifications.ts, WsHub.ts realtime notifications
|
||
SystemCrons.ts expiry + auto-delete jobs
|
||
DataManager.ts global (admin) settings
|
||
mcp/ MCP JSON-RPC server + tools
|
||
middlewares/, RouteAuth.ts
|
||
routes/
|
||
api/ human-facing REST (auth, account, agents, change-requests, notifications, admin, global)
|
||
v1/ agent-facing REST (/v1/change-requests)
|
||
mcp.ts MCP endpoint (/mcp)
|
||
ws.ts WebSocket (/api/ws/notifications)
|
||
tests/ Vitest integration tests
|
||
prisma/schema.prisma
|
||
UI/ React + TypeScript + Tailwind (Vite), builds to UI/build
|
||
Dockerfile
|
||
docker-compose.yml
|
||
.gitea/workflows/deploy.yml
|
||
```
|
||
|
||
---
|
||
|
||
## Stack
|
||
|
||
| Layer | Technology |
|
||
|---|---|
|
||
| Backend | Node.js + TypeScript (strict), rjweb-server |
|
||
| ORM | Prisma 7 (`@prisma/adapter-pg`) |
|
||
| Database | PostgreSQL |
|
||
| Frontend | React + Tailwind CSS (Vite) |
|
||
| Package manager | pnpm (Backend and UI are separate pnpm workspaces) |
|
||
| Backend tests | Vitest |
|
||
|
||
---
|
||
|
||
## Development workflow
|
||
|
||
### Branch strategy
|
||
|
||
```
|
||
feature/<name> → dev → main
|
||
```
|
||
|
||
### Running locally
|
||
|
||
```bash
|
||
# Backend
|
||
cd Backend
|
||
pnpm dev # esbuild + node, http://localhost:5000
|
||
|
||
# UI (separate terminal)
|
||
cd UI
|
||
pnpm dev # vite on :3000, proxies /api and /v1 to :5000
|
||
```
|
||
|
||
### Before committing
|
||
|
||
1. **Backend lint:** `cd Backend && pnpm lint`
|
||
2. **Backend tests:** `cd Backend && pnpm test`
|
||
3. **UI lint:** `cd UI && pnpm lint`
|
||
4. **UI build check:** `cd UI && pnpm build`
|
||
|
||
Never commit code that fails lint or tests.
|
||
|
||
---
|
||
|
||
## Key rules
|
||
|
||
### The service layer is the single source of truth
|
||
`ChangeRequestService.ts` contains ALL request lifecycle logic and authorization. The REST routes (`routes/v1`, `routes/api`) and the MCP tools (`lib/mcp/tools`) are thin adapters that call it. **Do not duplicate authorization or business logic** in a route or tool — add it to the service.
|
||
|
||
### Request state machine
|
||
States: `PENDING`, `CHANGES_REQUESTED`, `APPROVED`, `REJECTED`, `EXPIRED`, `CONSUMED`, `CANCELLED`.
|
||
- Decisions are only allowed on `PENDING`.
|
||
- Agent updates are only allowed on `PENDING` / `CHANGES_REQUESTED`.
|
||
- `REQUEST_CHANGES` requires a comment (≤500 chars) and resets to `PENDING` on agent resubmit (`resubmitted=true`).
|
||
- No mutation after `APPROVED`, `REJECTED`, `EXPIRED`, `CONSUMED`, or `CANCELLED`.
|
||
- Approvals are single-use: `consume` moves `APPROVED → CONSUMED`.
|
||
|
||
### Signing
|
||
Approve/reject decisions are signed with HMAC-SHA256 over a fixed canonical field order (see `Signing.ts`). The signature binds the decision to `content_hash`. Never reorder the canonical fields — it breaks every existing signature.
|
||
|
||
### Content hashing
|
||
`computeContentHash` hashes `{title, description, normalizedChanges}` with key-sorted canonical JSON and LF-normalized diffs. Equivalent inputs (CRLF vs LF, key order) must hash identically; any semantic change must change the hash. Covered by tests.
|
||
|
||
### Limits
|
||
`Limits.ts` centralizes: 15 requests/agent/hour, 5 agents/human, 1–10 pending/agent (default 5), expiry 60s–12h (default 30 min), auto-delete ≥7 days. Enforce through these helpers, not ad hoc.
|
||
|
||
### Auth
|
||
- Humans: session cookie (`patchpass_session`), bcrypt passwords, optional TOTP 2FA.
|
||
- Agents: one API key each, `x-api-key` header.
|
||
Resolution is in `Authentication.ts`; routes use `requireSession` / `requireAgent` / `requireAdmin` from `RouteAuth.ts`. Disabled humans/agents are rejected.
|
||
|
||
### Admin
|
||
First registered user is `ADMIN`. Global settings (`registration_enabled`, `requests_enabled`) live in `DataManager.ts`. Admin viewing another user's request payload is explicit and audited (`recordAudit`). Never remove the last admin.
|
||
|
||
### UTC & pagination
|
||
All timestamps are UTC ISO strings at the API boundary. History and admin logs are paginated.
|
||
|
||
---
|
||
|
||
## Database migrations – MANDATORY rules
|
||
|
||
> **Never use `prisma db push` or `prisma db pull` to evolve the schema.**
|
||
|
||
When you change `prisma/schema.prisma`:
|
||
|
||
1. Create a migration: `cd Backend && pnpm migrate` (`prisma migrate dev`), give a short slug.
|
||
2. Commit the generated `prisma/migrations/<timestamp>_<name>/migration.sql` with the schema change.
|
||
3. Never hand-edit an applied `migration.sql` — create a new migration.
|
||
4. Production/CI: `pnpm migrate:deploy`.
|
||
|
||
Prisma 7: the datasource URL lives in `prisma.config.ts` via `env("DATABASE_URL")`, not in `schema.prisma`. The client uses the `PrismaPg` adapter — do not remove it. Run `pnpm generate` for TS types without a migration.
|
||
|
||
---
|
||
|
||
## General coding rules
|
||
|
||
- TypeScript strict mode is on. Avoid `any` without reason (it is a lint warning).
|
||
- Do not use `console.*` in the Backend. Use `createLogger(component)` from `lib/logger.ts`.
|
||
- UI state uses React built-ins (Context + hooks) — no external state library.
|
||
- Follow existing conventions: route handlers in `routes/`, shared logic in `lib/`, MCP tools in `lib/mcp/tools/`.
|
||
|
||
---
|
||
|
||
## Keeping CLAUDE.md and AGENTS.md in sync – MANDATORY
|
||
|
||
`CLAUDE.md` (Claude Code) and `AGENTS.md` (all other AI agents) must always reflect the same rules. Any rule added, removed, or amended in one must be mirrored in the other **in the same commit**. If they drift, fix the gap before continuing.
|