Patchpass V1
Deploy / Build (push) Successful in 28s
Deploy / Test & Lint (push) Failing after 29s
Deploy / Build and Push Docker Image (push) Has been skipped

This commit is contained in:
Space-Banane
2026-07-18 20:14:44 +02:00
commit 7e05dd918c
101 changed files with 15183 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
# PatchPass Agent 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, 110 pending/agent (default 5), expiry 60s12h (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.