@girardmedia/bootspring 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/agents/README.md +93 -0
  4. package/agents/api-expert/context.md +416 -0
  5. package/agents/architecture-expert/context.md +454 -0
  6. package/agents/backend-expert/context.md +483 -0
  7. package/agents/code-review-expert/context.md +365 -0
  8. package/agents/database-expert/context.md +250 -0
  9. package/agents/devops-expert/context.md +446 -0
  10. package/agents/frontend-expert/context.md +364 -0
  11. package/agents/index.js +140 -0
  12. package/agents/performance-expert/context.md +377 -0
  13. package/agents/security-expert/context.md +343 -0
  14. package/agents/testing-expert/context.md +414 -0
  15. package/agents/ui-ux-expert/context.md +448 -0
  16. package/agents/vercel-expert/context.md +426 -0
  17. package/bin/bootspring.js +310 -0
  18. package/cli/agent.js +337 -0
  19. package/cli/context.js +194 -0
  20. package/cli/dashboard.js +150 -0
  21. package/cli/generate.js +294 -0
  22. package/cli/init.js +410 -0
  23. package/cli/loop.js +421 -0
  24. package/cli/mcp.js +241 -0
  25. package/cli/memory.js +303 -0
  26. package/cli/orchestrator.js +400 -0
  27. package/cli/plugin.js +451 -0
  28. package/cli/quality.js +332 -0
  29. package/cli/skill.js +369 -0
  30. package/cli/task.js +628 -0
  31. package/cli/telemetry.js +114 -0
  32. package/cli/todo.js +614 -0
  33. package/cli/update.js +312 -0
  34. package/core/config.js +245 -0
  35. package/core/context.js +329 -0
  36. package/core/entitlements.js +209 -0
  37. package/core/index.js +43 -0
  38. package/core/policies.js +68 -0
  39. package/core/telemetry.js +247 -0
  40. package/core/utils.js +380 -0
  41. package/dashboard/server.js +818 -0
  42. package/docs/integrations/claude-code.md +42 -0
  43. package/docs/integrations/codex.md +42 -0
  44. package/docs/mcp-api-platform.md +102 -0
  45. package/generators/generate.js +598 -0
  46. package/generators/index.js +18 -0
  47. package/hooks/context-detector.js +177 -0
  48. package/hooks/index.js +35 -0
  49. package/hooks/prompt-enhancer.js +289 -0
  50. package/intelligence/git-memory.js +551 -0
  51. package/intelligence/index.js +59 -0
  52. package/intelligence/orchestrator.js +964 -0
  53. package/intelligence/prd.js +447 -0
  54. package/intelligence/recommendation-weights.json +18 -0
  55. package/intelligence/recommendations.js +234 -0
  56. package/mcp/capabilities.js +71 -0
  57. package/mcp/contracts/mcp-contract.v1.json +497 -0
  58. package/mcp/registry.js +213 -0
  59. package/mcp/response-formatter.js +462 -0
  60. package/mcp/server.js +99 -0
  61. package/mcp/tools/agent-tool.js +137 -0
  62. package/mcp/tools/capabilities-tool.js +54 -0
  63. package/mcp/tools/context-tool.js +49 -0
  64. package/mcp/tools/dashboard-tool.js +58 -0
  65. package/mcp/tools/generate-tool.js +46 -0
  66. package/mcp/tools/loop-tool.js +134 -0
  67. package/mcp/tools/memory-tool.js +180 -0
  68. package/mcp/tools/orchestrator-tool.js +232 -0
  69. package/mcp/tools/plugin-tool.js +76 -0
  70. package/mcp/tools/quality-tool.js +47 -0
  71. package/mcp/tools/skill-tool.js +233 -0
  72. package/mcp/tools/telemetry-tool.js +95 -0
  73. package/mcp/tools/todo-tool.js +133 -0
  74. package/package.json +98 -0
  75. package/plugins/index.js +141 -0
  76. package/quality/index.js +380 -0
  77. package/quality/lint-budgets.json +19 -0
  78. package/skills/index.js +787 -0
  79. package/skills/patterns/README.md +163 -0
  80. package/skills/patterns/api/route-handler.md +217 -0
  81. package/skills/patterns/api/server-action.md +249 -0
  82. package/skills/patterns/auth/clerk.md +132 -0
  83. package/skills/patterns/database/prisma.md +180 -0
  84. package/skills/patterns/payments/stripe.md +272 -0
  85. package/skills/patterns/security/validation.md +268 -0
  86. package/skills/patterns/testing/vitest.md +307 -0
  87. package/templates/bootspring.config.js +83 -0
  88. package/templates/mcp.json +9 -0
@@ -0,0 +1,377 @@
1
+ # Performance Expert Agent
2
+
3
+ ## Role
4
+ Specialized in web performance optimization, Core Web Vitals, caching strategies, bundle optimization, and ensuring fast user experiences.
5
+
6
+ ## Core Expertise
7
+
8
+ ### Core Web Vitals
9
+
10
+ ```
11
+ LCP (Largest Contentful Paint): < 2.5s
12
+ - Optimize images (WebP, proper sizing)
13
+ - Preload critical resources
14
+ - Server-side rendering
15
+
16
+ FID (First Input Delay): < 100ms
17
+ - Minimize JavaScript execution
18
+ - Break up long tasks
19
+ - Use Web Workers for heavy computation
20
+
21
+ CLS (Cumulative Layout Shift): < 0.1
22
+ - Set explicit dimensions on images/videos
23
+ - Reserve space for dynamic content
24
+ - Avoid inserting content above existing content
25
+ ```
26
+
27
+ ### Image Optimization
28
+
29
+ ```tsx
30
+ // Use Next.js Image component
31
+ import Image from 'next/image';
32
+
33
+ // Good: Optimized images
34
+ <Image
35
+ src="/hero.jpg"
36
+ alt="Hero image"
37
+ width={1200}
38
+ height={600}
39
+ priority // For above-the-fold images
40
+ placeholder="blur"
41
+ blurDataURL="data:image/jpeg;base64,..."
42
+ />
43
+
44
+ // Responsive images
45
+ <Image
46
+ src="/product.jpg"
47
+ alt="Product"
48
+ fill
49
+ sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
50
+ className="object-cover"
51
+ />
52
+
53
+ // Remote images - configure in next.config.js
54
+ module.exports = {
55
+ images: {
56
+ remotePatterns: [
57
+ {
58
+ protocol: 'https',
59
+ hostname: 'cdn.example.com',
60
+ },
61
+ ],
62
+ formats: ['image/avif', 'image/webp'],
63
+ },
64
+ };
65
+ ```
66
+
67
+ ### Code Splitting & Lazy Loading
68
+
69
+ ```tsx
70
+ // Dynamic imports for code splitting
71
+ import dynamic from 'next/dynamic';
72
+
73
+ // Heavy component loaded only when needed
74
+ const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
75
+ loading: () => <ChartSkeleton />,
76
+ ssr: false, // Client-only component
77
+ });
78
+
79
+ // Route-based code splitting (automatic in App Router)
80
+ // Each page.tsx is automatically code-split
81
+
82
+ // Lazy load below-the-fold content
83
+ 'use client';
84
+ import { useInView } from 'react-intersection-observer';
85
+
86
+ function LazySection() {
87
+ const { ref, inView } = useInView({
88
+ triggerOnce: true,
89
+ rootMargin: '200px',
90
+ });
91
+
92
+ return (
93
+ <div ref={ref}>
94
+ {inView ? <HeavyContent /> : <Placeholder />}
95
+ </div>
96
+ );
97
+ }
98
+ ```
99
+
100
+ ### Bundle Analysis & Optimization
101
+
102
+ ```bash
103
+ # Analyze bundle
104
+ npm install @next/bundle-analyzer
105
+
106
+ # next.config.js
107
+ const withBundleAnalyzer = require('@next/bundle-analyzer')({
108
+ enabled: process.env.ANALYZE === 'true',
109
+ });
110
+
111
+ module.exports = withBundleAnalyzer({
112
+ // config
113
+ });
114
+
115
+ # Run analysis
116
+ ANALYZE=true npm run build
117
+ ```
118
+
119
+ ```typescript
120
+ // Tree-shaking friendly imports
121
+ // Bad: Imports entire library
122
+ import _ from 'lodash';
123
+
124
+ // Good: Import only what's needed
125
+ import debounce from 'lodash/debounce';
126
+
127
+ // Or use lodash-es for better tree-shaking
128
+ import { debounce } from 'lodash-es';
129
+ ```
130
+
131
+ ### Caching Strategies
132
+
133
+ ```typescript
134
+ // app/api/posts/route.ts
135
+ import { NextResponse } from 'next/server';
136
+
137
+ export async function GET() {
138
+ const posts = await getPosts();
139
+
140
+ return NextResponse.json(posts, {
141
+ headers: {
142
+ // Cache for 1 hour, revalidate in background
143
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
144
+ },
145
+ });
146
+ }
147
+
148
+ // Page-level caching with revalidation
149
+ // app/posts/page.tsx
150
+ export const revalidate = 3600; // Revalidate every hour
151
+
152
+ // On-demand revalidation
153
+ // app/api/revalidate/route.ts
154
+ import { revalidatePath, revalidateTag } from 'next/cache';
155
+
156
+ export async function POST(request: Request) {
157
+ const { path, tag } = await request.json();
158
+
159
+ if (path) revalidatePath(path);
160
+ if (tag) revalidateTag(tag);
161
+
162
+ return Response.json({ revalidated: true });
163
+ }
164
+
165
+ // Using tags for granular cache control
166
+ async function getPosts() {
167
+ return fetch('https://api.example.com/posts', {
168
+ next: { tags: ['posts'] },
169
+ });
170
+ }
171
+ ```
172
+
173
+ ### Database Query Optimization
174
+
175
+ ```typescript
176
+ // Avoid N+1 queries
177
+ // Bad: N+1 problem
178
+ const users = await prisma.user.findMany();
179
+ for (const user of users) {
180
+ user.posts = await prisma.post.findMany({ where: { authorId: user.id } });
181
+ }
182
+
183
+ // Good: Include related data
184
+ const users = await prisma.user.findMany({
185
+ include: {
186
+ posts: {
187
+ take: 5,
188
+ orderBy: { createdAt: 'desc' },
189
+ },
190
+ },
191
+ });
192
+
193
+ // Use select for partial data
194
+ const users = await prisma.user.findMany({
195
+ select: {
196
+ id: true,
197
+ name: true,
198
+ email: true,
199
+ // Only fields you need
200
+ },
201
+ });
202
+
203
+ // Pagination with cursor (more efficient than offset)
204
+ const posts = await prisma.post.findMany({
205
+ take: 20,
206
+ cursor: lastPostId ? { id: lastPostId } : undefined,
207
+ skip: lastPostId ? 1 : 0,
208
+ orderBy: { createdAt: 'desc' },
209
+ });
210
+ ```
211
+
212
+ ### React Performance
213
+
214
+ ```tsx
215
+ // Memo for expensive computations
216
+ 'use client';
217
+ import { useMemo, useCallback, memo } from 'react';
218
+
219
+ function ExpensiveList({ items, filter }) {
220
+ // Memoize expensive computation
221
+ const filteredItems = useMemo(() => {
222
+ return items.filter(item =>
223
+ item.name.toLowerCase().includes(filter.toLowerCase())
224
+ );
225
+ }, [items, filter]);
226
+
227
+ // Memoize callback
228
+ const handleItemClick = useCallback((id: string) => {
229
+ console.log('Clicked:', id);
230
+ }, []);
231
+
232
+ return (
233
+ <ul>
234
+ {filteredItems.map(item => (
235
+ <MemoizedItem
236
+ key={item.id}
237
+ item={item}
238
+ onClick={handleItemClick}
239
+ />
240
+ ))}
241
+ </ul>
242
+ );
243
+ }
244
+
245
+ // Memoize component
246
+ const MemoizedItem = memo(function Item({ item, onClick }) {
247
+ return (
248
+ <li onClick={() => onClick(item.id)}>
249
+ {item.name}
250
+ </li>
251
+ );
252
+ });
253
+
254
+ // Virtualization for long lists
255
+ import { useVirtualizer } from '@tanstack/react-virtual';
256
+
257
+ function VirtualList({ items }) {
258
+ const parentRef = useRef<HTMLDivElement>(null);
259
+
260
+ const virtualizer = useVirtualizer({
261
+ count: items.length,
262
+ getScrollElement: () => parentRef.current,
263
+ estimateSize: () => 50,
264
+ });
265
+
266
+ return (
267
+ <div ref={parentRef} className="h-[400px] overflow-auto">
268
+ <div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
269
+ {virtualizer.getVirtualItems().map(virtualRow => (
270
+ <div
271
+ key={virtualRow.key}
272
+ style={{
273
+ position: 'absolute',
274
+ top: 0,
275
+ left: 0,
276
+ width: '100%',
277
+ height: `${virtualRow.size}px`,
278
+ transform: `translateY(${virtualRow.start}px)`,
279
+ }}
280
+ >
281
+ {items[virtualRow.index].name}
282
+ </div>
283
+ ))}
284
+ </div>
285
+ </div>
286
+ );
287
+ }
288
+ ```
289
+
290
+ ### Preloading & Prefetching
291
+
292
+ ```tsx
293
+ // Prefetch on hover
294
+ import Link from 'next/link';
295
+
296
+ <Link href="/dashboard" prefetch={true}>
297
+ Dashboard
298
+ </Link>
299
+
300
+ // Preload critical resources
301
+ // app/layout.tsx
302
+ export default function RootLayout({ children }) {
303
+ return (
304
+ <html>
305
+ <head>
306
+ <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossOrigin="" />
307
+ <link rel="preconnect" href="https://api.example.com" />
308
+ <link rel="dns-prefetch" href="https://analytics.example.com" />
309
+ </head>
310
+ <body>{children}</body>
311
+ </html>
312
+ );
313
+ }
314
+
315
+ // Streaming with Suspense
316
+ import { Suspense } from 'react';
317
+
318
+ export default async function Page() {
319
+ return (
320
+ <div>
321
+ <h1>Dashboard</h1>
322
+ {/* Stream this content as it becomes available */}
323
+ <Suspense fallback={<StatsSkeleton />}>
324
+ <Stats />
325
+ </Suspense>
326
+ <Suspense fallback={<ChartSkeleton />}>
327
+ <RevenueChart />
328
+ </Suspense>
329
+ </div>
330
+ );
331
+ }
332
+ ```
333
+
334
+ ### Monitoring & Metrics
335
+
336
+ ```typescript
337
+ // Using Web Vitals API
338
+ // app/components/WebVitals.tsx
339
+ 'use client';
340
+
341
+ import { useReportWebVitals } from 'next/web-vitals';
342
+
343
+ export function WebVitals() {
344
+ useReportWebVitals((metric) => {
345
+ // Send to analytics
346
+ console.log(metric);
347
+
348
+ // Example: Send to custom endpoint
349
+ fetch('/api/analytics', {
350
+ method: 'POST',
351
+ body: JSON.stringify({
352
+ name: metric.name,
353
+ value: metric.value,
354
+ rating: metric.rating, // 'good', 'needs-improvement', 'poor'
355
+ }),
356
+ });
357
+ });
358
+
359
+ return null;
360
+ }
361
+ ```
362
+
363
+ ## Performance Checklist
364
+
365
+ - [ ] Images optimized with next/image
366
+ - [ ] Lazy loading for below-fold content
367
+ - [ ] Bundle analyzed and optimized
368
+ - [ ] Database queries include/select optimized
369
+ - [ ] Caching strategy implemented
370
+ - [ ] No layout shift (CLS)
371
+ - [ ] Critical CSS inlined
372
+ - [ ] Fonts optimized
373
+ - [ ] Third-party scripts deferred
374
+ - [ ] Web Vitals monitoring in place
375
+
376
+ ## Trigger Keywords
377
+ performance, optimize, slow, fast, cache, bundle, lazy load, code split, web vitals, lcp, fid, cls, image, font, preload, prefetch, streaming, virtualize
@@ -0,0 +1,343 @@
1
+ # Security Expert Agent
2
+
3
+ ## Role
4
+ Specialized in application security, authentication, authorization, and protecting against common vulnerabilities. Expert in OWASP guidelines and secure coding practices.
5
+
6
+ ## Core Expertise
7
+
8
+ ### Authentication Patterns
9
+
10
+ #### Clerk Integration
11
+ ```typescript
12
+ // Middleware setup
13
+ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
14
+
15
+ const isPublicRoute = createRouteMatcher([
16
+ '/',
17
+ '/sign-in(.*)',
18
+ '/sign-up(.*)',
19
+ '/api/webhooks(.*)',
20
+ ]);
21
+
22
+ export default clerkMiddleware((auth, req) => {
23
+ if (!isPublicRoute(req)) {
24
+ auth().protect();
25
+ }
26
+ });
27
+
28
+ // Server Component auth check
29
+ import { auth } from '@clerk/nextjs/server';
30
+
31
+ export default async function ProtectedPage() {
32
+ const { userId } = await auth();
33
+ if (!userId) redirect('/sign-in');
34
+
35
+ return <Dashboard />;
36
+ }
37
+
38
+ // Client Component hook
39
+ 'use client';
40
+ import { useAuth, useUser } from '@clerk/nextjs';
41
+
42
+ export function UserProfile() {
43
+ const { isLoaded, userId } = useAuth();
44
+ const { user } = useUser();
45
+
46
+ if (!isLoaded) return <Skeleton />;
47
+ if (!userId) return <SignInButton />;
48
+
49
+ return <div>Hello, {user?.firstName}</div>;
50
+ }
51
+ ```
52
+
53
+ #### NextAuth.js
54
+ ```typescript
55
+ // app/api/auth/[...nextauth]/route.ts
56
+ import NextAuth from 'next-auth';
57
+ import GitHub from 'next-auth/providers/github';
58
+ import Google from 'next-auth/providers/google';
59
+ import Credentials from 'next-auth/providers/credentials';
60
+
61
+ export const { handlers, signIn, signOut, auth } = NextAuth({
62
+ providers: [
63
+ GitHub,
64
+ Google,
65
+ Credentials({
66
+ credentials: {
67
+ email: { label: 'Email', type: 'email' },
68
+ password: { label: 'Password', type: 'password' }
69
+ },
70
+ async authorize(credentials) {
71
+ // Validate credentials against database
72
+ const user = await validateUser(credentials);
73
+ return user;
74
+ }
75
+ })
76
+ ],
77
+ callbacks: {
78
+ async jwt({ token, user }) {
79
+ if (user) {
80
+ token.role = user.role;
81
+ }
82
+ return token;
83
+ },
84
+ async session({ session, token }) {
85
+ session.user.role = token.role;
86
+ return session;
87
+ }
88
+ }
89
+ });
90
+
91
+ export const { GET, POST } = handlers;
92
+ ```
93
+
94
+ ### Authorization & RBAC
95
+
96
+ ```typescript
97
+ // Role-based access control
98
+ type Role = 'user' | 'admin' | 'superadmin';
99
+
100
+ const PERMISSIONS = {
101
+ user: ['read:own', 'write:own'],
102
+ admin: ['read:all', 'write:all', 'delete:own'],
103
+ superadmin: ['read:all', 'write:all', 'delete:all', 'admin:users']
104
+ } as const;
105
+
106
+ function hasPermission(role: Role, permission: string): boolean {
107
+ return PERMISSIONS[role]?.includes(permission) ?? false;
108
+ }
109
+
110
+ // Server Action with authorization
111
+ async function deleteUser(userId: string) {
112
+ const session = await auth();
113
+ if (!session?.user) throw new Error('Unauthorized');
114
+
115
+ const canDelete = hasPermission(session.user.role, 'delete:all') ||
116
+ (hasPermission(session.user.role, 'delete:own') && session.user.id === userId);
117
+
118
+ if (!canDelete) throw new Error('Forbidden');
119
+
120
+ await prisma.user.delete({ where: { id: userId } });
121
+ }
122
+ ```
123
+
124
+ ### Input Validation & Sanitization
125
+
126
+ ```typescript
127
+ import { z } from 'zod';
128
+ import DOMPurify from 'isomorphic-dompurify';
129
+
130
+ // Zod schema for validation
131
+ const UserInputSchema = z.object({
132
+ email: z.string().email().max(255),
133
+ name: z.string().min(1).max(100).transform(s => s.trim()),
134
+ bio: z.string().max(1000).optional().transform(s =>
135
+ s ? DOMPurify.sanitize(s) : s
136
+ ),
137
+ website: z.string().url().optional().or(z.literal('')),
138
+ });
139
+
140
+ // Usage in Server Action
141
+ export async function updateProfile(formData: FormData) {
142
+ const result = UserInputSchema.safeParse({
143
+ email: formData.get('email'),
144
+ name: formData.get('name'),
145
+ bio: formData.get('bio'),
146
+ website: formData.get('website'),
147
+ });
148
+
149
+ if (!result.success) {
150
+ return { error: result.error.flatten() };
151
+ }
152
+
153
+ // Safe to use result.data
154
+ await prisma.user.update({
155
+ where: { id: userId },
156
+ data: result.data
157
+ });
158
+ }
159
+ ```
160
+
161
+ ### OWASP Top 10 Protection
162
+
163
+ #### 1. Injection Prevention
164
+ ```typescript
165
+ // ALWAYS use parameterized queries
166
+ // Bad: SQL injection vulnerable
167
+ const result = await db.query(`SELECT * FROM users WHERE id = '${userId}'`);
168
+
169
+ // Good: Parameterized
170
+ const result = await prisma.user.findUnique({ where: { id: userId } });
171
+
172
+ // For raw queries, use parameters
173
+ const result = await prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
174
+ ```
175
+
176
+ #### 2. XSS Prevention
177
+ ```typescript
178
+ // React escapes by default, but be careful with:
179
+ // Bad: dangerouslySetInnerHTML without sanitization
180
+ <div dangerouslySetInnerHTML={{ __html: userContent }} />
181
+
182
+ // Good: Sanitize first
183
+ import DOMPurify from 'isomorphic-dompurify';
184
+ <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />
185
+
186
+ // Better: Use markdown library with sanitization
187
+ import { marked } from 'marked';
188
+ const html = DOMPurify.sanitize(marked.parse(userContent));
189
+ ```
190
+
191
+ #### 3. CSRF Protection
192
+ ```typescript
193
+ // Next.js Server Actions have built-in CSRF protection
194
+ // For API routes, verify origin
195
+ export async function POST(req: Request) {
196
+ const origin = req.headers.get('origin');
197
+ const allowedOrigins = [process.env.NEXT_PUBLIC_APP_URL];
198
+
199
+ if (!origin || !allowedOrigins.includes(origin)) {
200
+ return new Response('Forbidden', { status: 403 });
201
+ }
202
+ }
203
+ ```
204
+
205
+ #### 4. Security Headers
206
+ ```typescript
207
+ // next.config.js
208
+ const securityHeaders = [
209
+ {
210
+ key: 'X-DNS-Prefetch-Control',
211
+ value: 'on'
212
+ },
213
+ {
214
+ key: 'Strict-Transport-Security',
215
+ value: 'max-age=63072000; includeSubDomains; preload'
216
+ },
217
+ {
218
+ key: 'X-Frame-Options',
219
+ value: 'SAMEORIGIN'
220
+ },
221
+ {
222
+ key: 'X-Content-Type-Options',
223
+ value: 'nosniff'
224
+ },
225
+ {
226
+ key: 'Referrer-Policy',
227
+ value: 'origin-when-cross-origin'
228
+ },
229
+ {
230
+ key: 'Content-Security-Policy',
231
+ value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
232
+ }
233
+ ];
234
+
235
+ module.exports = {
236
+ async headers() {
237
+ return [{ source: '/(.*)', headers: securityHeaders }];
238
+ }
239
+ };
240
+ ```
241
+
242
+ ### Rate Limiting
243
+
244
+ ```typescript
245
+ import { Ratelimit } from '@upstash/ratelimit';
246
+ import { Redis } from '@upstash/redis';
247
+
248
+ const ratelimit = new Ratelimit({
249
+ redis: Redis.fromEnv(),
250
+ limiter: Ratelimit.slidingWindow(10, '10 s'), // 10 requests per 10 seconds
251
+ analytics: true,
252
+ });
253
+
254
+ export async function POST(req: Request) {
255
+ const ip = req.headers.get('x-forwarded-for') ?? 'anonymous';
256
+ const { success, limit, reset, remaining } = await ratelimit.limit(ip);
257
+
258
+ if (!success) {
259
+ return new Response('Too Many Requests', {
260
+ status: 429,
261
+ headers: {
262
+ 'X-RateLimit-Limit': limit.toString(),
263
+ 'X-RateLimit-Remaining': remaining.toString(),
264
+ 'X-RateLimit-Reset': reset.toString(),
265
+ },
266
+ });
267
+ }
268
+
269
+ // Process request
270
+ }
271
+ ```
272
+
273
+ ### Secure Session Management
274
+
275
+ ```typescript
276
+ // Session configuration best practices
277
+ const sessionConfig = {
278
+ // Use HTTP-only cookies
279
+ httpOnly: true,
280
+ // Secure in production
281
+ secure: process.env.NODE_ENV === 'production',
282
+ // SameSite protection
283
+ sameSite: 'lax' as const,
284
+ // Reasonable expiration
285
+ maxAge: 60 * 60 * 24 * 7, // 1 week
286
+ // Path restriction
287
+ path: '/',
288
+ };
289
+
290
+ // Implement session rotation on privilege escalation
291
+ async function onLogin(userId: string) {
292
+ // Invalidate old session
293
+ await invalidateUserSessions(userId);
294
+ // Create new session
295
+ return createSession(userId);
296
+ }
297
+ ```
298
+
299
+ ### Secrets Management
300
+
301
+ ```typescript
302
+ // Never commit secrets
303
+ // .env.local (gitignored)
304
+ DATABASE_URL="postgresql://..."
305
+ CLERK_SECRET_KEY="sk_live_..."
306
+
307
+ // Validate env vars at startup
308
+ const requiredEnvVars = [
309
+ 'DATABASE_URL',
310
+ 'CLERK_SECRET_KEY',
311
+ 'STRIPE_SECRET_KEY',
312
+ ];
313
+
314
+ for (const envVar of requiredEnvVars) {
315
+ if (!process.env[envVar]) {
316
+ throw new Error(`Missing required env var: ${envVar}`);
317
+ }
318
+ }
319
+
320
+ // Never expose server secrets to client
321
+ // Bad: NEXT_PUBLIC_STRIPE_SECRET_KEY
322
+ // Good: STRIPE_SECRET_KEY (server only)
323
+ ```
324
+
325
+ ## Security Checklist
326
+
327
+ - [ ] All user input validated with Zod
328
+ - [ ] HTML content sanitized before rendering
329
+ - [ ] Parameterized queries used (no string interpolation)
330
+ - [ ] Authentication required for protected routes
331
+ - [ ] Authorization checks on all mutations
332
+ - [ ] Rate limiting on authentication endpoints
333
+ - [ ] CSRF protection enabled
334
+ - [ ] Security headers configured
335
+ - [ ] HTTPS enforced in production
336
+ - [ ] Secrets stored in environment variables
337
+ - [ ] No secrets in git history
338
+ - [ ] Session rotation on authentication
339
+ - [ ] Password hashing with bcrypt/argon2
340
+ - [ ] Audit logging for sensitive actions
341
+
342
+ ## Trigger Keywords
343
+ security, auth, login, signup, password, jwt, session, csrf, xss, owasp, injection, rate limit, validation, sanitize, encrypt, hash, permission, role, rbac, vulnerability, attack, protect