AI agent rules for the KeelStack Engine — a production-grade Node.js + TypeScript backend with strict 8-layer architecture, failure safety patterns, and full-stack extension support.
These rules exist because AI agents (Cursor, Claude, Copilot) write better, safer code when they know exactly what they can and cannot touch.
Without rules, AI will:
- Instantiate Stripe clients wherever it feels like
- Read process.env directly instead of going through a config layer
- Bypass policy guards in modules
- Skip idempotency on mutating routes
- Call OpenAI directly instead of through a cost-controlled client
- Wire a frontend to the backend incorrectly (missing auth headers, no idempotency keys, wrong job-polling pattern)
These rules prevent all of that.
Copy .cursorrules into the root of your KeelStack Engine project:
curl -o .cursorrules \
https://raw.githubusercontent.com/KeelStack-me/keelstack-cursorrules/main/.cursorrulesOr clone and copy manually:
git clone https://github.com/KeelStack-me/keelstack-cursorrules.git
cp keelstack-cursorrules/.cursorrules your-project/.cursorrulesCursor auto-loads .cursorrules from the project root on next open.
| Layer | AI Write? | Why |
|---|---|---|
| 01-Core | ❌ NO | Security-critical. Middleware, guards, errors. Human only. |
| 02-Common | ✅ YES | DTOs, types, utilities. Low risk. |
| 03-Policies | ❌ NO | Business rules. Wrong policy = revenue leak or data loss. |
| 04-Modules | ✅ YES | Feature modules. Auth/billing changes need human review. |
| 05-Infra | ❌ NO | DB schema + gateways. Schema changes require migrations. |
| 06-Background | ✅ YES | New job types safe. RetryableJobRunner = human review only. |
| 07-AI | ❌ NO | LLMClient controls costs. AI must not modify its own limits. |
| 08-Web | ✅ YES | Routes safe to add. Must use full middleware chain. |
Any change touching a ❌ layer requires a
// @vibe-safe: HUMAN-CROSS-REVIEW comment in the file and PR.
- Never modify
src/01-Corewithout explicit human instruction - Never bypass
src/03-Policieschecks - Never read
process.envdirectly — useRuntimeConfig.ts - Never instantiate Stripe, Redis, or DB clients outside
src/05-Infra - Never call LLM APIs directly — always use the
llmClientsingleton - Never add a POST/PUT/PATCH route without considering idempotency
- Never process webhooks without
webhookDeduplicationGuard - Never use
isProcessed()+markProcessed()— race condition confirmed - Never delete
// @vibe-safecomments - Never commit secrets, API keys, or credentials to source
Two mechanisms — use the correct one per context:
// Webhook routes
router.post('/webhooks/:provider',
webhookDeduplicationGuard(store, 'stripe'), handler);
// Mutating API routes
router.post('/resource',
idempotencyMiddleware({ store, namespace: 'billing.sub' }), handler);Both use tryClaimKey() (atomic SET NX). If the handler fails after claiming the key, releaseKey() must be called before throwing — this prevents blocked retries and silent data loss.
// Critical paths — use RetryableJobRunner
// Fire-and-forget only — use raw QueueWorker
// Handler contract
return { ok: true }; // success
return { ok: false, errorMessage: 'reason' }; // retryable failure
throw new NonRetryableJobError('bad input'); // must not retryAll job handlers must be idempotent — repeated execution must produce no duplicate side effects.
// All LLM calls via llmClient singleton — never directly
await llmClient.complete({
model: '4o-mini', // cheapest model by default
feature: 'report_gen', // required for cost attribution
messages: [...],
});Set LLM_PROVIDER=stub in .env for local dev — no API key needed.
The rules cover full frontend wiring for any framework (Next.js, React SPA, Vue, SvelteKit, Flutter):
Auth:
// Login → { accessToken, refreshToken }
// accessToken → memory only (never localStorage)
// refreshToken → httpOnly + Secure cookie
// Attach: Authorization: Bearer <accessToken>Idempotency keys (per user action):
const key = `${tenantId}-${feature}-${crypto.randomUUID()}`;
// 'Idempotency-Key': key on every POST/PUT/PATCHAsync jobs (202 + poll):
// POST /api/v1/tasks → 202 { jobId, pollUrl }
// GET {pollUrl} → { status: 'queued'|'processing'|'done'|'failed' }
// Poll every 1.5s until terminal state. Cap at 60 attempts.LLM budget display:
// GET /api/v1/llm/budget → { tokensUsedThisHour, budgetPerHour }
// Show as tokens or % — never raw API costReference implementation: keelstack-ui-starter (Next.js 14 · TypeScript · TanStack Query · Tailwind · Axios)
- Mutating routes use
idempotencyMiddleware -
releaseKey()called on handler failure after key claim - Webhook routes use
webhookDeduplicationGuard— neverisProcessed()+markProcessed() - Critical jobs use
RetryableJobRunner, notQueueWorker - All LLM calls via
llmClientwithfeaturefield + default model set - No
process.envreads outsideRuntimeConfig.ts - No DB / Redis / Stripe instantiation outside
05-Infra -
schema.tschanges have matching migration - New routes have
@openapiannotations - No reverse-direction layer imports
- All unit + E2E tests pass
- No secrets committed to source
- No backend
src/files imported into frontend - API base URL from env var, not hardcoded
-
accessTokenin memory,refreshTokenin httpOnly cookie -
Idempotency-Keyon all POST/PUT/PATCH requests - 202 responses trigger poll loop with max retry cap
-
failedjob state handled with retry option in UI -
CORS_ORIGINset in.env, not hardcoded in code - No secrets in
NEXT_PUBLIC_env vars
I built KeelStack Engine after repeatedly rebuilding the same backend foundation — auth, Stripe, background jobs, AI cost controls — across different projects. Every time, the hardest bugs were in production:
- Duplicate Stripe charges from double-click submits
- Webhooks double-processing on Stripe retries
- Background jobs vanishing silently on worker crash
- One user burning the entire OpenAI budget in an afternoon
These .cursorrules encode what I learned. They tell the AI to stay inside safe boundaries so you can move fast without breaking production.
These are architectural guardrails, not a security boundary.
Real security lives in the code:
- Argon2id password hashing (OWASP 2023 parameters)
- Timing-safe comparisons
- Idempotency via atomic
SET NX(Redis in prod, in-memory in dev) - Webhook signature verification
- 597 unit tests · 93.13% coverage · CI-enforced at 90% threshold
Always review AI-generated code before merging.
Never trust .cursorrules alone to prevent a security issue.
These rules ship bundled with KeelStack Engine:
- Production Node.js + TypeScript + Express backend
- Auth, Stripe billing, background jobs, LLM cost controls — all wired, tested, and failure-safe
- 597 unit tests · 93.13% coverage · 0 known vulnerabilities
- 15 AI prompts for Cursor, Claude, and Copilot
- One-time purchase · $29 early builder price
MIT — copy, adapt, use freely.
If you use these rules in your own project, you don't need to credit KeelStack. But if they save you from a production incident, I'd love to hear about it.