Files
Space-Banane 7e05dd918c
Deploy / Build (push) Successful in 28s
Deploy / Test & Lint (push) Failing after 29s
Deploy / Build and Push Docker Image (push) Has been skipped
Patchpass V1
2026-07-18 20:14:44 +02:00

6.2 KiB
Raw Permalink Blame History

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

# 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.