A modern, production-ready and security hardened SaaS starter template for building full-stack applications using Next.js 15, Convex, Clerk, and Clerk Billing. The easiest and most secure way to start accepting payments with beautiful UI and seamless integrations.
π Live Demo β Try the app in your browser!
This Secure Vibe Coding OS is part of the Secure Vibe Coding Masterclass by Dr. Allen Harper.
See two sample course modules, in the docs folder.
- π Next.js 15 with App Router - Latest React framework with server components
- β‘οΈ Turbopack - Ultra-fast development with hot module replacement
- π¨ TailwindCSS v4 - Modern utility-first CSS with custom design system
- π Clerk Authentication - Complete user management with social logins
- π³ Clerk Billing - Integrated subscription management and payments
- ποΈ Convex Real-time Database - Serverless backend with real-time sync
- π‘οΈ Protected Routes - Authentication-based route protection
- π Admin Pages - Environment-based admin role with Security Monitoring dashboard (admin-only access)
- π CSRF Protection - Built-in Cross-Site Request Forgery protection with HMAC-SHA256
- π Security Headers - Automatic security headers (CSP, X-Frame-Options, HSTS)
- π¦ Rate Limiting - IP-based rate limiting (5 requests/minute) to prevent abuse
- β Input Validation - Zod-based validation with XSS sanitization
- π‘οΈ Secure Error Handling - Environment-aware error responses (no data leakage)
- π Security Monitoring Dashboard - Admin-only real-time security event tracking with severity levels, event filtering, and comprehensive attack detection
- π° Payment Gating - Subscription-based content access
- π Beautiful 404 Page - Custom animated error page
- π Dark/Light Theme - System-aware theme switching
- π± Responsive Design - Mobile-first approach with modern layouts
- β¨ Custom Animations - React Bits and Framer Motion effects
- π§© shadcn/ui Components - Modern component library with Radix UI
- π Interactive Dashboard - Complete admin interface with charts
- οΏ½οΏ½ Webhook Integration - Automated user and payment sync
- π’ Vercel Ready - One-click deployment
- π SEO & LLM SEO Optimized Blog Engine - Markdown-based blog with AI crawler optimization, llms.txt, sitemap generation, and RSS feed
- Next.js 15 - React framework with App Router
- TailwindCSS v4 - Utility-first CSS framework
- shadcn/ui - Modern component library
- Radix UI - Accessible component primitives
- Framer Motion - Smooth animations and transitions
- Motion Primitives - Advanced animation components
- Lucide React & Tabler Icons - Beautiful icon libraries
- Recharts - Data visualization components
- React Bits - Custom animation components
- Convex - Real-time database and serverless functions
- Clerk - Authentication and user management
- Clerk Billing - Subscription billing and payments
- Svix - Webhook handling and validation
- TypeScript - Type safety throughout
- Vercel - Deployment platform
- Turbopack - Fast build tool
- Node.js 18+
- Clerk account for authentication and billing
- Convex account for database
If you have Claude Code installed, run the /install command for a guided, automated setup:
claude
# Then type: /installThis will:
- Create a Clerk application automatically (no account needed upfront)
- Generate all required secrets
- Configure
.env.localwith all environment variables - Set up Convex project interactively
- Create the webhook endpoint
- Set Convex environment variables
You'll receive a claim URL to create your Clerk account after setup completes.
After installation, your app runs locally at the provided URL (typically http://localhost:3000). Develop and customize your app here β no deployment is needed until you're ready to share it with others.
When you're ready for others to see your app, deploy to Vercel with a single command. This uses your existing Clerk and Convex development instances β the same ones you see locally:
claude
# Then type: /deploy-to-devThis is fully automated β no manual steps required (assuming gh and npx vercel are already authenticated). It uses your existing Clerk dev keys and Convex dev instance.
What gets automated:
- Private GitHub repo creation + remote reconfiguration
- Vercel project linking
- All environment variables set on Vercel (read from
.env.local) - Production deployment to Vercel
Prerequisites: gh auth login and npx vercel login must be done once beforehand. The command checks and tells you if either is missing.
Your app will be fully functional on Vercel with a small Clerk development badge. After the initial deployment, updating your site is just git push origin main β Vercel automatically rebuilds on every push.
When you want to collect real credit cards, remove the Clerk dev badge, and have users depend on your app, run the production deployment. Delay this step until you've validated your site and are confident you want to finalize it β it requires a Stripe account, a custom domain, and several manual configuration steps:
claude
# Then type: /deploy-to-prodPrerequisites (must have before running):
- A custom domain you own (e.g.,
myapp.com) β Clerk production requires this,*.vercel.appis not accepted - A Stripe account β for Clerk Billing integration
- Google OAuth credentials (optional) β for Google social login
| Manual Step | Where | Time |
|---|---|---|
| Create Clerk production instance | Clerk Dashboard (with your custom domain) | ~1 min |
| Copy & paste 2 Clerk production keys | Clerk Dashboard β API Keys | ~30 sec |
| Connect Stripe to Clerk | Clerk Dashboard β Billing | ~2 min |
| Google OAuth setup (optional) | Google Cloud Console + Clerk Dashboard | ~5-10 min |
What gets automated:
- GitHub repo creation (if not already done)
- Clerk production key validation + JWT template creation
- Frontend API URL derivation from production publishable key
- Convex production deploy key generation
- Convex function deployment to production
- Production webhook creation via Svix
- All Convex and Vercel production env vars
vercel.jsonwith Convex build command- Production deployment to Vercel
Alternatively, run the setup script directly:
npm install
node scripts/setup.mjs init --site-name="My App" --admin-email="you@example.com"
node scripts/setup.mjs convex-setup --project-name="My App"
node scripts/setup.mjs configure --clerk-sk="sk_test_..." --admin-email="you@example.com"- Download and set up the starter template:
# Download the template files to your project directory
# Then navigate to your project directory and install dependencies
git clone https://github.com/harperaa/secure-vibe-coding-OS.git [project-name]
cd [project-name]
npm install #or pnpm / yarn / bun- Set up your environment variables:
cp .env.example .env.local- Configure your environment variables (complete each step/setting in order in the
.env.localfile ):
# a. Site Branding (Name your site)
NEXT_PUBLIC_SITE_NAME=YOUR SITE NAME HERE
# b. Clerk Authentication & Billing
# Sign up and get these from your Clerk dashboard at https://dashboard.clerk.com
# Select "Create application", Name it, hit "Create application"
# Then on Overview page > scroll down to "2. Set your Clerk API keys" > copy/paste here.
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_your_clerk_publishable_key_here
CLERK_SECRET_KEY=sk_test_your_clerk_secret_key_here
# c. Setup your JWT template Clerk Frontend API URL in Clerk
# Dashboard > Configure > JWT Templates > Add new template > select convex from Template dropdown > save
# Copy the URL from the issuer field and paste it here, ensure it ends with .dev
NEXT_PUBLIC_CLERK_FRONTEND_API_URL=https://your-clerk-frontend-api-url.clerk.accounts.dev
# d. Convex Configuration
# Sign up and get these from your Convex dashboard at https://dashboard.convex.dev
# Create new project, then goto Settings > Project Settings > Lost Access
# Copy/paste/execute command: npx convex dev --configure=existing --team your_team_name --project your_project_name
CONVEX_DEPLOYMENT=your_convex_deployment_here
NEXT_PUBLIC_CONVEX_URL=https://your-convex-url.convex.cloud
# e. confirm the above values are updated automatically in .env.local by the convex script.
# f. Set the Convex Environment variable for NEXT_PUBLIC_CLERK_FRONTEND_API_URL
# Click link in terminal from running convex config, to set the NEXT_PUBLIC_CLERK_FRONTEND_API_URL there.
# Use value from above for NEXT_PUBLIC_CLERK_FRONTEND_API_URL
# g. CSRF Protection
# Generate these secrets using: node -p "require('crypto').randomBytes(32).toString('base64url')"
CSRF_SECRET=<32-byte-base64url-string>
SESSION_SECRET=<32-byte-base64url-string>
# Clerk Redirect URLs (leave these for now)
# These ensure users are redirected to dashboard after authentication
NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/dashboard- Terminate and Re-Initialize Convex (leave it running in the background):
npx convex dev- Convex Webhook Secret
- Get the site url from your Convex dashboard at https://dashboard.convex.dev
- Select your new project
-
Settings > URL & Deploy Key > Show development credentials > HTTP Actions URL (copy that URL)
- Goto Clerk dashboard > Configure > Webhooks > Add Endpoint > Paste in the above Convex HTTP Actions URL and append endpoint
- ex. {HTTP Actions URL}/clerk-users-webhook
- Search for and enable the following events:
- Enable events:
user.created,user.updated,user.deleted,paymentAttempt.updated - Copy the webhook signing secret to your Convex environment variables
- Note: CLERK_WEBHOOK_SECRET should be set in your Convex dashboard environment variables (not here)
- Set up Convex environment variables in your Convex dashboard:
# In Convex Dashboard Environment Variables
CLERK_WEBHOOK_SECRET=whsec_your_webhook_secret_here
NEXT_PUBLIC_CLERK_FRONTEND_API_URL=https://your-clerk-frontend-api-url.clerk.accounts.dev
ADMIN_EMAIL=your-admin-email@example.comNote: The ADMIN_EMAIL must match the email address you use to sign up/sign in. Only this user will see the "Administration" section in the sidebar and have access to the Security Monitoring dashboard. If ADMIN_EMAIL is not configured, the app will throw an error when accessing admin features.
- Setup Clerk Subscriptons (Required for template to work)
- Clerk dashboard > Subscriptions > Create a Plan > Add User Plan > Name the plan > Set the Monthly base fee
- Hit Save button
- Hit Enable Billing button at top of screen (very important)
Start the development server:
npm run devYour application will be available at http://localhost:3000.
Secure Vibe Coding OS includes a unique self-improving learning system that gets smarter over time as you use it.
The system maintains a dynamic lessons library that captures your real-world experiences:
.claude/skills/lessons/
βββ implementing-rate-limiting/
β βββ SKILL.md # What worked, what failed, exact parameters
βββ fixing-csrf-validation/
β βββ SKILL.md # Debugging steps, root causes, solutions
βββ optimizing-convex-queries/
β βββ SKILL.md # Performance learnings, query patterns
βββ [your-future-lessons]/
βββ SKILL.md # Grows with every session
1. Before Starting Work - Learn from the past
/advise
# Describe your goal:
> "I need to implement rate limiting on my API endpoints"
# Claude searches past lessons and finds:
# β
What worked: withRateLimit HOF pattern
# β What failed: Redis approach (too complex)
# π§ Exact params: 5 req/min, IP-based tracking
# π‘ Key insight: Use Map for simple cases2. During Work - Apply proven solutions
- Use exact parameters that worked before
- Avoid documented pitfalls from "Failed Attempts" tables
- Build on previous successes instead of starting from scratch
3. After Completing Work - Capture your learnings
/retrospective
# Claude analyzes your session and creates:
# - Detailed documentation of what you accomplished
# - What worked vs. what failed
# - Exact parameters and configurations
# - Lessons learned for next time
# - Creates a PR with your learningsTraditional Development: Every task starts from zero, same mistakes repeated
Task 1: Try A, B, C β C works
Task 2: Try A, B, C β C works again (wasted effort)
Task 3: Try A, B, C β C works again (still wasting time)
With Continual Learning: Each task makes future tasks faster
Task 1: Try A, B, C β C works β Document in lessons
Task 2: Read lesson β Start with C β Success immediately
Task 3: Read lesson β Start with C β Success immediately
Week 1: Empty lessons folder, learning as you go
- Complete first API endpoint (took 2 hours, tried 3 approaches)
- Run
/retrospectiveto capture learnings
Week 2: Start building lessons library
- Need second API endpoint
- Run
/advise- finds lesson from Week 1 - Complete in 30 minutes using proven pattern
Week 4: Lessons compound
- 5+ documented lessons from various tasks
- Most new work has relevant past experience
- Development speed increases 3-5x on similar tasks
- Team members learn from your documented experiences
Month 3: Institutional knowledge base
- 20+ real-world lessons from actual production work
- Exact parameters for common tasks
- Documented pitfalls and how to avoid them
- New team members onboard faster by reading lessons
lessons/implementing-rate-limiting/SKILL.md:
## Failed Attempts β
| Attempt | What We Tried | Why It Failed | Lesson Learned |
|---------|---------------|---------------|----------------|
| 1 | Redis-based rate limiter | Too complex for use case | Start simple, add complexity only if needed |
| 2 | Cookie-based tracking | Easily bypassed | Use IP-based for public endpoints |
## What Worked β
- withRateLimit HOF with Map-based tracking
- Parameters: limitWindow: 60000, maxRequests: 5
- IP extraction from x-forwarded-for headerlessons/debugging-clerk-webhooks/SKILL.md:
## Root Cause
Webhook secret was set in .env.local instead of Convex dashboard
## Solution
1. Remove CLERK_WEBHOOK_SECRET from .env.local
2. Add to Convex Dashboard β Settings β Environment Variables
3. Restart Convex dev server
## Time Saved Next Time
Original debugging: 3 hours
With this lesson: 5 minutes1. Run /advise before every non-trivial task
- Search lessons for similar past work
- Read relevant SKILL.md files
- Start with proven approaches
2. Run /retrospective after completing work
- Especially after solving difficult problems
- Always after trying multiple approaches
- Document exact parameters that worked
3. Be specific in lessons
- Include exact error messages encountered
- Document specific version numbers
- Provide copy-paste ready code examples
- No vague advice - only concrete specifics
4. Share lessons with your team
- Lessons are stored in git
- PRs document decision-making process
- Team members review and learn from each other
- Collective knowledge compounds faster
The continual learning system complements the built-in security skills:
Security Skills (.claude/skills/security/):
- Pre-built best practices and patterns
- General security implementation guides
- Updated by package maintainers
Your Lessons (.claude/skills/lessons/):
- YOUR specific experiences applying those patterns
- YOUR project's unique challenges and solutions
- YOUR team's specific parameters and configurations
- Updated by YOU as you work
Together, they create a complete knowledge system:
- Start with security best practices
- Learn from your specific implementations
- Build institutional knowledge over time
- Never repeat the same mistake twice
Day 1: Just use the system normally
- Complete your tasks as you normally would
- Run
/retrospectiveat end of day to capture learnings
Day 7: Start seeing benefits
- Run
/advisebefore starting similar tasks - Notice faster completion times on repeated work
Day 30: System is significantly smarter
- Substantial lessons library built up
- Most tasks have relevant past experience
- Development velocity noticeably increased
Day 90: Institutional knowledge established
- Comprehensive coverage of common tasks
- New team members leverage your experience
- Complex tasks completed in fraction of original time
Remember: Every session you complete makes the NEXT session faster. The system literally gets smarter the more you use it. π
This application includes built-in CSRF (Cross-Site Request Forgery) protection for all state-changing operations. To enable CSRF protection, you need to configure two secret keys:
- Generate CSRF secrets:
# Generate CSRF_SECRET
node -p "require('crypto').randomBytes(32).toString('base64url')"
# Generate SESSION_SECRET
node -p "require('crypto').randomBytes(32).toString('base64url')"- Add to
.env.local:
CSRF_SECRET=your_generated_csrf_secret_here
SESSION_SECRET=your_generated_session_secret_here- CSRF tokens are automatically generated and bound to user sessions using HMAC-SHA256
- Tokens are stored in HTTP-only, SameSite=Strict cookies (
XSRF-TOKEN) - Frontend applications should fetch tokens from
/api/csrfbefore making POST requests - Tokens are single-use and cleared after validation
To protect an API route with CSRF validation:
import { withCsrf } from '@/lib/withCsrf';
import { NextRequest, NextResponse } from 'next/server';
async function myProtectedHandler(request: NextRequest) {
// Your API logic here
return NextResponse.json({ success: true });
}
// Wrap your handler with withCsrf
export const POST = withCsrf(myProtectedHandler);// Fetch CSRF token
const response = await fetch('/api/csrf', { credentials: 'include' });
const { csrfToken } = await response.json();
// Use token in POST request
await fetch('/api/your-endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
credentials: 'include',
body: JSON.stringify(data)
});The application includes built-in rate limiting to protect against brute force attacks and abuse. Rate limiting is configured per IP address:
- Limit: 5 requests per minute per IP address
- Response: HTTP 429 (Too Many Requests) when limit exceeded
To protect an API route with rate limiting:
import { withRateLimit } from '@/lib/withRateLimit';
import { NextRequest, NextResponse } from 'next/server';
async function sensitiveHandler(request: NextRequest) {
// Your API logic here
return NextResponse.json({ success: true });
}
// Wrap your handler with withRateLimit
export const POST = withRateLimit(sensitiveHandler);For maximum security, you can combine both protections:
import { withRateLimit } from '@/lib/withRateLimit';
import { withCsrf } from '@/lib/withCsrf';
import { NextRequest, NextResponse } from 'next/server';
async function protectedHandler(request: NextRequest) {
// Your API logic here
return NextResponse.json({ success: true });
}
// Apply both rate limiting and CSRF protection
export const POST = withRateLimit(withCsrf(protectedHandler));Note: Rate limiting tracks by IP address using the x-forwarded-for header (for proxies/load balancers) or x-real-ip as fallback.
A test script is provided to verify rate limiting is working correctly on your protected endpoints:
# Test the default test endpoint (uses port 3000)
node scripts/test-rate-limit.js
# Specify a custom port if your dev server runs on a different port
node scripts/test-rate-limit.js --port=3003
# Test your custom protected endpoint
node scripts/test-rate-limit.js /api/your-custom-routeExpected Results:
- First 5 requests: HTTP 200 (Success)
- Requests 6-10: HTTP 429 (Rate Limited)
The script will display color-coded results showing which requests succeeded and which were rate limited. Wait 60 seconds between test runs for the rate limit to reset.
The application uses Zod schemas for type-safe input validation and XSS prevention. All user input is validated and sanitized before processing.
User input is automatically sanitized to prevent Cross-Site Scripting (XSS) attacks by removing dangerous characters:
- Removed:
<>"& - Preserved:
'(apostrophes for names like O'Neal, D'Angelo, etc.)
Note: React automatically escapes JSX output, so this is defense-in-depth protection.
Import pre-built schemas from @/lib/validation:
import {
emailSchema,
safeTextSchema,
safeLongTextSchema,
createPostSchema
} from '@/lib/validation';import { NextRequest, NextResponse } from 'next/server';
import { withRateLimit } from '@/lib/withRateLimit';
import { withCsrf } from '@/lib/withCsrf';
import { validateRequest } from '@/lib/validateRequest';
import { createPostSchema } from '@/lib/validation';
async function createPostHandler(request: NextRequest) {
const body = await request.json();
// Validate and sanitize input
const validation = validateRequest(createPostSchema, body);
if (!validation.success) {
return validation.response; // Returns 400 with error details
}
// validation.data is type-safe and XSS-sanitized
const { title, content, tags } = validation.data;
// Save to database (data is already sanitized)
// ...
return NextResponse.json({ success: true });
}
// Apply all security layers
export const POST = withRateLimit(withCsrf(createPostHandler));emailSchema- Email validation with sanitizationsafeTextSchema- Short text (max 100 chars)safeLongTextSchema- Long text (max 5000 chars)usernameSchema- Alphanumeric usernamesurlSchema- HTTPS URLs onlycontactFormSchema- Complete contact formcreatePostSchema- User-generated contentupdateProfileSchema- Profile updates
See lib/validation.ts for all available schemas and create custom ones as needed.
// convex/posts.ts
import { mutation } from "./_generated/server";
import { createPostSchema } from "../lib/validation";
export const createPost = mutation({
handler: async (ctx, args) => {
const validation = createPostSchema.safeParse(args);
if (!validation.success) {
throw new Error("Invalid input");
}
// validation.data is sanitized
await ctx.db.insert("posts", validation.data);
}
});The application automatically sets the following security headers on all responses:
X-Content-Type-Options: nosniff- Prevents MIME type sniffingX-Frame-Options: DENY- Prevents clickjacking attacksContent-Security-Policy- Controls resource loadingX-Robots-Tag: noindex, nofollow- Prevents indexing of protected routesStrict-Transport-Security- Forces HTTPS in production (HSTS)
The application includes secure error handling to prevent information leakage:
- Development: Full error details and stack traces for debugging
- Production: Generic error messages, no internal details exposed
Import error handlers from @/lib/errorHandler:
import {
handleApiError,
handleValidationError,
handleForbiddenError,
handleUnauthorizedError,
handleNotFoundError
} from '@/lib/errorHandler';
async function myApiHandler(request: NextRequest) {
try {
// Your API logic here
const result = await performOperation();
return NextResponse.json({ success: true, result });
} catch (error) {
// Secure error handling (hides stack traces in production)
return handleApiError(error, 'my-api-route');
}
}Available Error Handlers:
handleApiError(error, context)- HTTP 500 for unexpected errorshandleValidationError(message, details)- HTTP 400 for validation failureshandleForbiddenError(message)- HTTP 403 for authorization failureshandleUnauthorizedError(message)- HTTP 401 for authentication failureshandleNotFoundError(resource)- HTTP 404 for missing resources
The application includes a comprehensive real-time security monitoring dashboard accessible at /dashboard/security. This feature provides visibility into all security events across your application with advanced filtering and analysis capabilities.
Admin Only: The Security Monitoring dashboard is only accessible to the admin user (configured via ADMIN_EMAIL environment variable in Convex dashboard). Admin users see an "Administration" section in the sidebar containing the Security Monitoring link. Non-admin users will not see this section and will receive an "Access Denied" message if they navigate directly to /dashboard/security.
- Real-time Event Tracking - Monitor security events as they happen
- Severity Classification - Events categorized as Critical, High, Medium, or Low
- Event Type Filtering - Filter by specific attack types (rate limiting, CSRF, XSS, prompt injection, etc.)
- Date Range Selection - View events from last 24 hours, 7 days, 30 days, or custom ranges
- Status Management - Track open (unread) vs closed (read) events
- Detailed Event Metadata - View origin, IP address, fingerprint, endpoint, and error details
- Attack Detection - Automatic detection of 19+ attack types including:
- Origin mismatch
- Rate limit exceeded
- CSRF validation failed
- XSS attempts
- Prompt injection attempts
- JWT validation failures
- Fingerprint manipulation
- Unauthorized access
- And more...
- Ensure
ADMIN_EMAILis set in your Convex dashboard environment variables - Sign in with the email address matching
ADMIN_EMAIL - Navigate to the "Security Monitoring" link in the "Administration" section of the sidebar
Security events are automatically logged when using the built-in security middlewares (withRateLimit, withCsrf, etc.). You can also manually log events using the security logger:
import { logSecurity } from '@/convex/lib/securityLogger';
// In a Convex mutation
await logSecurity(
ctx,
projectId,
"xss_attempt",
"high",
{
endpoint: "/api/submit",
ipAddress: request.headers.get('x-forwarded-for'),
errorMessage: "XSS pattern detected in user input",
requestPayload: JSON.stringify(body)
}
);Security events are automatically logged when violations occur in the real middleware. The easiest way to test is to trigger actual rate limit violations.
Testing Rate Limiting (Recommended):
# Start the dev server first
npm run dev
# Run the rate limit test script
node scripts/test-rate-limit.js
# Specify a custom port (default is 3000)
node scripts/test-rate-limit.js --port=3001What happens:
- The script sends 10 rapid requests to
/api/test-rate-limit - First 5 requests succeed (HTTP 200)
- Remaining 5 requests are rate-limited (HTTP 429)
- Each rate limit violation is logged to the security dashboard
- Events appear in
/dashboard/securitywith typerate_limit_exceeded
Viewing Security Events:
- Sign in to the application
- Navigate to
/dashboard/security - View logged rate limit violations and other security events
- Filter by severity, event type, or date range
Note: Events are logged automatically by the middleware - no manual logging required.
The dashboard tracks the following security event types:
origin_mismatch- Request from unauthorized originrate_limit_exceeded- Too many requests from single IPinvalid_api_key- Invalid API key providedfingerprint_change- Browser fingerprint changed mid-sessionsuspicious_activity- Anomaly detection triggeredjwt_validation_failed- JWT token validation errorunauthorized_access- Access to protected resource without authinput_validation_failed- Input validation rejected malicious datareplay_detected- Replay attack detectedcsrf_validation_failed- CSRF token validation failedxss_attempt- Cross-site scripting attempt detectedprompt_injection_attempt- AI prompt injection detectedfingerprint_manipulation- Browser fingerprint tamperinghttp_origin_blocked- Blocked origin in HTTP request- And more...
Summary Cards:
- Total Events - Overview of all security events
- Critical Events - Immediate action required
- High Events - Authentication failures
- Medium Events - Rate limiting violations
- Low Events - Informational events
- Overall Status - Current security posture
Event Feed:
- Chronological list of security events
- Expandable details for each event
- Request payload viewing
- Mark as read/unread functionality
- End-user information when available
Use the provided script to audit dependencies and check for vulnerabilities:
# Run comprehensive security check
bash scripts/security-check.sh
# Or manually:
npm audit --production
npm outdated# Automatic fixes (minor/patch updates)
npm audit fix
# Force major version updates (review breaking changes!)
npm audit fix --forceRecommendation: Run npm audit regularly and before deploying to production.
Security architecture is implemented through specialized Claude Code skills managed as a separate package:
- Package: secure-claude-skills
- npm:
npx secure-claude-skills init - Location:
.claude/skills/security/(managed as git subtree)
The skills are synchronized with the main package using git subtree. To update:
```bash git subtree pull --prefix=.claude/skills/security \ https://github.com/harperaa/secure-claude-skills.git main --squash ```
## π Keeping Your OS Updated
Secure Vibe Coding OS is a **living template** that receives regular updates including:
- π‘οΈ New security features and hardening improvements
- π Additional course modules and learning materials
- π§ Enhanced Claude Code commands and agents
- π Bug fixes and dependency updates
- β‘ Performance optimizations
### Update Commands Overview
The template includes specialized pull commands to help you selectively update different parts of your installation:
| Command | Updates | Safety Level | Use When |
|---------|---------|--------------|----------|
| `/pull-repo-safe` | Entire repository | β
Safe (preview first) | General updates to core template |
| `/pull-security-skills` | Security skills only | β
Safe (merge with squash) | Security feature updates |
| `/pull-commands` | Claude commands only | β οΈ Force overwrites | Command updates only |
| `/pull-agents` | Claude agents only | β οΈ Force overwrites | Agent updates only |
### How to Update
#### Option 1: Safe Full Update (Recommended)
Use this when you've customized your app and want to safely merge template updates:
```bash
# From your project root, use the Claude Code command:
/pull-repo-safe
What happens:
- Fetches latest changes from the main repository
- Shows you exactly what changed (file-by-file diff)
- Detects if you have uncommitted local changes
- Provides step-by-step merge instructions
- Lets you decide what to merge
Example workflow:
# Step 1: Check for updates
/pull-repo-safe
# Step 2: Review the changes shown
# Read the diffs carefully
# Step 3: Commit your local changes
git add . && git commit -m 'Save my customizations'
# Step 4: Merge the updates
git merge origin/main
# Step 5: Resolve any conflicts (if they occur)
# - Edit conflicted files
# - git add <resolved-files>
# - git commit -m 'Merge template updates'π‘ Let Claude Code help you merge:
Ask Claude: "Commit my current changes, then merge the updates from origin/main,
resolving any conflicts while preserving my customizations"
Security skills are updated frequently with new security patterns and best practices:
/pull-security-skillsWhat happens:
- Checks for local changes in
.claude/skills/security/ - Shows you what you've modified (if anything)
- Pulls latest security skills using git subtree
- Merges changes with
--squashflag (keeps history clean)
If conflicts occur:
# Manual resolution:
git status # See conflicted files
# Edit files to resolve conflicts
git add . # Stage resolved files
git commit -m 'Merge security skills updates' # Complete the merge
# Or ask Claude Code:
"Review the merge conflicts and resolve them intelligently,
preserving my customizations while integrating new features"Use this to get the latest Claude Code commands without touching your app code:
/pull-commands.claude/commands/
What happens:
- Checks for uncommitted changes in
.claude/commands/ - Shows you what you've modified
- Warns that continuing will overwrite your changes
- Gives you a chance to cancel (Ctrl+C)
- Force pulls latest commands from origin/main
After successful update:
git commit -m "Update Claude Code commands from template"
# Or ask Claude Code:
"Review the updated commands and commit them with a descriptive message"Get the latest Claude Code agents without touching anything else:
/pull-agents.claude/agents/
What happens:
Same as /pull-commands but for the .claude/agents/ folder.
Always commit your work before pulling updates:
git add .
git commit -m "Save my current work before updating"If you've modified app pages, components, or core functionality:
- β
Use
/pull-repo-safeto preview changes - β Don't use force update commands
Take time to read the diffs shown by /pull-repo-safe:
- Understand what's changing
- Identify potential conflicts with your customizations
- Decide what to merge and what to skip
- Check for updates weekly or monthly
- Smaller, frequent updates are easier to merge than large infrequent ones
- Security updates should be applied promptly
After merging updates:
# Reinstall dependencies (if package.json changed)
npm install
# Run type checking
npm run tsc --noEmit
# Test your app locally
npm run dev
# Visit key pages and test functionalityInstead of always pulling from main, you can pull specific releases:
git fetch --tags
git merge v2.0.0 # Merge a specific versionWhen conflicts occur, you have options:
# 1. See conflicted files
git status
# 2. Open each file and look for conflict markers:
<<<<<<< HEAD
Your customizations
=======
Template updates
>>>>>>> origin/main
# 3. Edit to keep what you want
# 4. Remove conflict markers
# 5. Save the file
# 6. Stage resolved files
git add <resolved-file>
# 7. Complete the merge
git commit -m "Merge template updates"Ask Claude Code to help:
"Review the merge conflicts and resolve them intelligently,
preserving my customizations while integrating new features"
Or:
"Help me merge the security skills updates"
Claude Code can:
- Analyze both versions
- Understand intent
- Preserve your customizations
- Integrate new features
- Resolve conflicts intelligently
app/- Application pages and routescomponents/- UI componentslib/- Utility librariesconvex/- Backend functions and schemapackage.json- Dependencies
.claude/skills/security/- All security implementation guides- New security patterns and best practices
- Updated vulnerability awareness content
.claude/commands/- Claude Code command definitions- Automation scripts and workflows
.claude/agents/- Specialized Claude Code agents- Task automation configurations
You don't have to update everything:
Conservative approach:
# Only update what you need
/pull-security-skills # Get security updates (important!)
/pull-commands # Get new commands (optional)
# Skip core template updates if you've heavily customizedAggressive approach:
# Stay current with everything
/pull-repo-safe # Review and merge all updates
# Follow prompts to mergeSmart approach:
# Update frequently used parts, cherry-pick core updates
/pull-security-skills # Security is critical
/pull-commands # Commands are easy to update
/pull-agents # Agents enhance productivity
# For core template:
/pull-repo-safe # Preview changes
# Cherry-pick specific features you want:
git cherry-pick <commit-hash>Before deploying updated code to production:
- All updates committed locally
-
npm installcompleted successfully -
npm run tsc --noEmitpasses without errors - App runs locally without errors (
npm run dev) - Key features tested manually
- Security updates applied (if any)
- Environment variables still correct
- No secrets committed to git
/- Beautiful landing page with pricing/dashboard- Protected user dashboard/dashboard/payment-gated- Subscription-protected content/clerk-users-webhook- Clerk webhook handler
- Seamless sign-up/sign-in with Clerk
- Automatic user sync to Convex database
- Protected routes with middleware
- Social login support
- Automatic redirects to dashboard after auth
- Custom Clerk pricing table component
- Subscription-based access control
- Real-time payment status updates
- Webhook-driven payment tracking
// Users table
users: {
name: string,
externalId: string // Clerk user ID
}
// Payment attempts tracking
paymentAttempts: {
payment_id: string,
userId: Id<"users">,
payer: { user_id: string },
// ... additional payment data
}βββ app/
β βββ (landing)/ # Landing page components
β β βββ hero-section.tsx
β β βββ features-one.tsx
β β βββ pricing.tsx
β β βββ ...
β βββ dashboard/ # Protected dashboard
β β βββ layout.tsx
β β βββ page.tsx
β β βββ payment-gated/
β βββ globals.css # Global styles
β βββ layout.tsx # Root layout
β βββ not-found.tsx # Custom 404 page
βββ components/
β βββ ui/ # shadcn/ui components
β βββ custom-clerk-pricing.tsx
β βββ theme-provider.tsx
β βββ ...
βββ convex/ # Backend functions
β βββ schema.ts # Database schema
β βββ users.ts # User management
β βββ paymentAttempts.ts # Payment tracking
β βββ http.ts # Webhook handlers
βββ lib/
β βββ utils.ts # Utility functions
βββ middleware.ts # Route protection
- Hero Section - Animated hero with CTAs
- Features Section - Interactive feature showcase
- Pricing Table - Custom Clerk billing integration
- Testimonials - Social proof section
- FAQ Section - Common questions
- Footer - Links and information
- Sidebar Navigation - Collapsible sidebar with user menu
- Interactive Charts - Data visualization with Recharts
- Data Tables - Sortable and filterable tables
- Payment Gating - Subscription-based access control
- Splash Cursor - Interactive cursor effects
- Animated Lists - Smooth list animations
- Progressive Blur - Modern blur effects
- Infinite Slider - Continuous scrolling elements
The starter kit includes a fully customizable theme system. You can customize colors, typography, and components using:
- Theme Tools: tweakcn.com, themux.vercel.app, or ui.jln.dev
- Global CSS: Modify
app/globals.cssfor custom styling - Component Themes: Update individual component styles in
components/ui/
CONVEX_DEPLOYMENT- Your Convex deployment URLNEXT_PUBLIC_CONVEX_URL- Your Convex client URLNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY- Clerk publishable keyCLERK_SECRET_KEY- Clerk secret keyNEXT_PUBLIC_CLERK_FRONTEND_API_URL- Clerk frontend API URL (from JWT template)NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL- Redirect after sign inNEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL- Redirect after sign upNEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL- Fallback redirect for sign inNEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL- Fallback redirect for sign up
CLERK_WEBHOOK_SECRET- Clerk webhook secret (set in Convex dashboard)NEXT_PUBLIC_CLERK_FRONTEND_API_URL- Clerk frontend API URL (set in Convex dashboard)ADMIN_EMAIL- Email address of the admin user who can access Security Monitoring dashboard
- Connect your repository to Vercel
- Set environment variables in Vercel dashboard
- Deploy automatically on push to main branch
The project is optimized for Vercel with:
- Automatic builds with Turbopack
- Environment variable management
- Edge function support
Build for production:
npm run build
npm start- Modify
app/globals.cssfor global styles - Update TailwindCSS configuration
- Customize component themes in
components/ui/
- Update logo in
components/logo.tsx - Modify metadata in
app/layout.tsx - Customize color scheme in CSS variables
- Add new dashboard pages in
app/dashboard/ - Extend database schema in
convex/schema.ts - Create custom components in
components/
npm run dev- Start development server with Turbopacknpm run build- Build for productionnpm start- Start production servernpm run lint- Run ESLintnpm run generate:sitemap- Generate sitemap.xml and robots.txt
The starter kit includes a full-featured, SEO and LLM-optimized blog engine built for maximum discoverability by both search engines and AI crawlers like ChatGPT, Claude, and Perplexity.
Blog posts are stored as Markdown/MDX files in:
/content/blog/
βββ your-first-post.mdx
βββ another-post.mdx
βββ ...
The easiest way to create a blog post is with the /create-blog Claude Code command:
claude
# Then type: /create-blog "How to implement rate limiting in Next.js"This command:
- Gathers requirements β asks for topic, audience, category, and key points
- Generates SEO-optimized content β full MDX article with proper heading hierarchy, summary section, and LLM-friendly structure
- Creates an AI banner image β generates a 16:9 banner using Gemini Pro via
scripts/generate-image.jsand saves it topublic/blog/images/ - Saves everything β writes the MDX file to
content/blog/with complete frontmatter and the image in the correct location
The banner image is generated automatically using the Gemini Pro API. You'll need a GEMINI_API_KEY in your .env.local β get one free at Google AI Studio. This same key is also used by the /technical-diagram command to generate Nano Banana Pro architecture diagrams.
You can also generate images independently:
node scripts/generate-image.js "Your prompt here" "public/blog/images/output.png" --aspect-ratio 16:9Create a new .mdx file in /content/blog/ with the following frontmatter:
---
title: "Your Post Title"
description: "A clear, concise description for SEO (150-160 characters)"
date: "2026-01-08"
author: "Your Name"
category: "Security"
tags: ["tag1", "tag2", "tag3"]
image: "/blog/images/your-image.png"
---
## Summary
A 2-3 sentence summary of your article. LLMs extract this first, so make it count.
## Your Content Here
Write your content using standard Markdown syntax...- Static Site Generation (SSG) - All posts pre-rendered at build time for fast loading
- Full-text Search - Client-side search across all posts
- Categories & Tags - Filter posts by category or tag
- Table of Contents - Auto-generated from headings with scroll tracking
- Related Posts - Automatically suggests related articles
- Reading Time - Estimated reading time for each post
- Social Sharing - Twitter, LinkedIn, and copy link buttons
- RSS Feed - Available at
/feed.xml - Dark Mode - Full dark mode support with prose styling
The blog is optimized for AI crawlers with:
| File | Purpose |
|---|---|
/public/llms.txt |
Guides AI crawlers to your most important content |
/public/sitemap.xml |
Auto-generated sitemap with all blog URLs |
/public/robots.txt |
Explicitly allows GPTBot, ClaudeBot, PerplexityBot |
/feed.xml |
RSS feed for content syndication |
Key optimizations:
- Server-side rendered content (AI crawlers don't execute JavaScript)
- JSON-LD structured data (TechArticle schema) on every post
- Clear heading hierarchy (H1 β H2 β H3)
- Summary sections for easy LLM extraction
- Consistent terminology for strong embeddings
The sitemap is automatically generated before each build via the prebuild script. You can also generate it manually:
npm run generate:sitemapThis creates:
/public/sitemap.xml- All blog posts, categories, and tags/public/robots.txt- AI crawler permissions (if not exists)
Blog images are stored in /public/blog/images/ and referenced in frontmatter:
image: "/blog/images/my-post-image.png"The /create-blog command generates banner images automatically. To generate one manually:
node scripts/generate-image.js "Professional blog header about your topic, modern tech style, no text" "public/blog/images/your-slug.png" --aspect-ratio 16:9Requires GEMINI_API_KEY in .env.local. Get your key at Google AI Studio, then add to .env.local:
GEMINI_API_KEY=your_gemini_api_key_hereSet your site URL for proper sitemap and meta tag generation:
NEXT_PUBLIC_SITE_URL=https://yourdomain.comπ For the recommended deployment path (
/installβ/deploy-to-devβ/deploy-to-prod) and advanced deployment topics, see DEPLOYMENT.md
The sections below cover manual production setup for reference. If you have Claude Code, the /deploy-to-prod command automates most of these steps.
This application uses separate instances for development and production. Both can run simultaneously without interfering with each other.
Clerk: Has separate Development and Production instances
Your Application in Clerk Dashboard
ββ Development Instance
β ββ Keys: pk_test_..., sk_test_...
β ββ Users: Test users
β ββ Stripe: Test mode
β
ββ Production Instance
ββ Keys: pk_live_..., sk_live_...
ββ Users: Real users
ββ Stripe: Live mode
Convex: Has separate Development and Production deployments
Your Project in Convex Dashboard
ββ dev:your-deployment-name
β ββ URL: https://....convex.cloud
β ββ Database: Dev data
β
ββ prod:your-deployment-name
ββ URL: https://....convex.site
ββ Database: Prod data
Your application connects to different instances based on environment variables:
Local Development (.env.local):
# Points to DEV instances
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
CONVEX_DEPLOYMENT=dev:polite-bulldog-532
NEXT_PUBLIC_CONVEX_URL=https://....convex.cloudProduction (Vercel Environment Variables):
# Points to PROD instances
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_SECRET_KEY=sk_live_...
CONVEX_DEPLOYMENT=prod:your-deployment-name
NEXT_PUBLIC_CONVEX_URL=https://....convex.siteTip: If you have Claude Code, the
/deploy-to-prodcommand automates most of these steps. See Quick Deploy to Production above.
Follow this complete guide to deploy your application to production with Clerk, Convex, and Vercel.
Step 1: Create Production Instance
- Go to Clerk Dashboard: https://dashboard.clerk.com
- Select your application (the one you created for development)
- Top of page: Click the "Development" toggle/dropdown (top right corner)
- Click: "Create production instance"
- Choose:
- "Clone development settings" (recommended - copies your dev config), OR
- "Use default settings" (start fresh)
- Application domain: Enter your custom domain (e.g.,
myapp.comorapp.mydomain.com). Clerk requires a domain you own β*.vercel.appis not accepted. You can update this later in Clerk Dashboard β Domains. - Click: "Create Instance"
Clerk creates a separate production instance alongside your dev instance (dev instance is NOT removed).
Step 2: Get Production API Keys
- Ensure you're viewing Production (top toggle should say "Production")
- Left sidebar: Click "API Keys"
- Copy and save these keys:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY(starts withpk_live_...)CLERK_SECRET_KEY(starts withsk_live_...)- Frontend API URL (e.g.,
https://your-prod.clerk.accounts.dev)
You'll add these to Vercel later.
Step 3: Configure Production Domain
- Left sidebar: Click "Domains"
- Add your production domain:
- For Vercel:
your-app.vercel.app(DNS automatic) - For custom domain: Add domain and configure DNS records shown
- For Vercel:
- Wait for DNS propagation (if custom domain - can take up to 48 hours)
Step 4: Configure OAuth Providers (If Using Social Login)
- Left sidebar: Click "SSO Connections" or "Social Connections"
- For each provider you use (Google, GitHub, etc.):
- Click the provider
- Important: Production requires YOUR OWN OAuth credentials
- Click "Use custom credentials"
- Follow Clerk's provider-specific guide to create OAuth app
- Add Client ID and Client Secret from the provider
- Save
Note: Development uses Clerk's shared credentials, but production requires your own for security.
Step 5: Connect Stripe
- Left sidebar: Click "Billing" β "Settings"
- Click: "Connect Stripe Account"
- Choose:
- Connect existing Stripe account (if you have one), OR
- Create new Stripe account through Clerk
- Follow Stripe connection flow
- Important: Start in Stripe Test Mode (toggle at top of Billing page)
Step 6: Deploy Certificates
- Go to Clerk Dashboard home page
- Review checklist - it shows remaining steps
- Once all green checkmarks appear:
- Click "Deploy certificates" button
- This activates your production instance
β Clerk Production Instance is now active!
Step 1: Generate Production Deploy Key
- Go to Convex Dashboard: https://dashboard.convex.dev
- Select your project
- Left sidebar: Click "Settings" β "Deploy Keys"
- Click: "Generate a production deploy key"
- Copy the entire key (format:
prod:xxx|yyy...) - Save it securely - you'll add this to Vercel
Important: This key is shown only once. If you lose it, you'll need to generate a new one.
Step 2: Note Your Convex Project Info
You'll need:
- Production deployment name (will be
prod:your-deployment-name) - Production URL (will be created on first deploy, ends with
.convex.site)
These will be auto-configured by Vercel during deployment.
Step 1: Create Vercel Project
- Go to: https://vercel.com
- Click: "Add New Project"
- Import your GitHub repository:
harperaa/secure-vibe-coding-OS - Click: "Import"
Step 2: Configure Build Settings
In Vercel project configuration (before first deploy):
-
Build Command - Override to:
npx convex deploy --cmd 'npm run build' -
Install Command - Leave as default:
npm install
-
Output Directory - Leave as default:
.next -
Root Directory - Leave as default (blank)
Step 3: Add Production Environment Variables
In Vercel β Settings β Environment Variables:
For each variable below, select "Production" environment:
# Convex Production Deploy Key (CRITICAL - from Part 2, Step 1)
CONVEX_DEPLOY_KEY=prod:abc123|xyz...
# Clerk Production Keys (from Part 1, Step 2)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
CLERK_SECRET_KEY=sk_live_...
NEXT_PUBLIC_CLERK_FRONTEND_API_URL=https://your-prod.clerk.accounts.dev
# Site Branding
NEXT_PUBLIC_SITE_NAME=Secure Vibe Coding OS
# CSRF Protection (use SAME values from your .env.local)
CSRF_SECRET=<copy-from-your-dev-env>
SESSION_SECRET=<copy-from-your-dev-env>
# Clerk Redirects (same as dev)
NEXT_PUBLIC_CLERK_SIGN_IN_FORCE_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_UP_FORCE_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/dashboardImportant:
- Do NOT add
CONVEX_DEPLOYMENTorNEXT_PUBLIC_CONVEX_URLmanually - Convex automatically sets these during the deploy process
- Only add
CONVEX_DEPLOY_KEY- the rest is automatic
Step 4: Deploy to Production
-
Click: "Deploy" button
-
Watch the deployment logs:
- Convex will deploy your functions
- Create production deployment (if first time)
- Build your Next.js app
- Deploy to Vercel
-
After successful deploy:
- Note your production URL:
https://your-app.vercel.app - Visit the URL to verify it's working
- Note your production URL:
-
Go to Convex Dashboard:
- You'll now see a
prod:your-deployment-namedeployment - Click it and copy the production URL (ends with
.convex.site) - You'll need this for webhooks in the next part
- You'll now see a
Step 1: Set Up Clerk Production Webhook
- Clerk Dashboard β Ensure in Production mode (top toggle)
- Left sidebar: Click "Webhooks"
- Click: "Add Endpoint"
- Endpoint URL - Enter your Convex production URL + endpoint:
https://your-prod-deployment.convex.site/clerk-users-webhook - Subscribe to events - Check these:
- β
user.created - β
user.updated - β
user.deleted - β
paymentAttempt.updated
- β
- Click: "Create"
- Copy the signing secret (starts with
whsec_...)
Step 2: Add Webhook Secret to Convex Production
- Convex Dashboard β Select your production deployment (prod:...)
- Left sidebar: Click "Settings" β "Environment Variables"
- Add these variables:
CLERK_WEBHOOK_SECRET=whsec_your_production_secret NEXT_PUBLIC_CLERK_FRONTEND_API_URL=https://your-prod.clerk.accounts.dev ADMIN_EMAIL=your-admin-email@example.com
- Click: "Save"
Step 3: Verify Webhook Connection
- Create a test user in your production app
- Check Convex Dashboard β Production β Data β
userstable - User should appear (confirms webhook working)
Phase 1: Test Mode Testing
-
Verify Stripe is in Test Mode:
- Clerk Dashboard (Production) β Billing β Settings
- Should show "Test Mode" toggle enabled
-
Visit your production URL:
https://your-app.vercel.app -
Test user signup/login:
- Create account
- Sign in
- Verify user appears in Convex production database
-
Test subscription with Stripe test card:
- Go to payment-gated page
- Click subscribe
- Use test card:
4242 4242 4242 4242 - Any future date, any CVC
- Complete "payment"
-
Verify subscription access:
- Should redirect to payment-gated content
- Check Convex β
paymentAttemptstable - Subscription should be recorded
-
Test subscription cancellation:
- User profile β Manage subscription
- Cancel subscription
- Verify access revoked
Phase 2: Go Live with Real Payments
When everything works in test mode:
-
Clerk Dashboard (Production) β Billing β Settings
-
Toggle: Switch from Test Mode to Live Mode
-
Confirm the switch (Clerk will warn you about real payments)
-
Test with real card:
- Subscribe yourself with a real credit card
- Verify charge appears in your Stripe dashboard
- Verify subscription works
- Cancel subscription (to avoid recurring charges)
-
You're now live! π
- Real users can sign up
- Real payments are processed
- Stripe takes their fees (~2.9% + 30Β’)
Monitoring:
- Clerk Dashboard (Production) β View real users
- Convex Dashboard (Production) β Monitor database
- Stripe Dashboard β Track payments and revenue
- Vercel Dashboard β Monitor deployments and performance
Updates:
- Make changes locally (uses dev instances)
- Test thoroughly in development
- Push to GitHub
- Vercel auto-deploys to production
- Both dev and prod run simultaneously
Key Points:
- β Production is separate from development
- β Making production doesn't delete development
- β You can develop locally while users use production
- β Each environment is completely isolated
Typical developer workflow:
Morning - Start developing:
npm run dev # Terminal 1 - Next.js dev server
npx convex dev # Terminal 2 - Convex dev connectionβ Uses dev instances, test users, test payments
Meanwhile:
- Production app running on Vercel
- Real users using production instances
- Completely isolated from your dev work
Deploy changes:
git add .
git commit -m "New feature"
git push origin mainβ Vercel auto-deploys to production β Production uses prod instances β Dev instances unchanged
Key Points:
- β Dev and prod instances never interfere
- β Dev instances exist permanently (not removed when creating prod)
- β Same codebase, different environment variables select which instance
- β You can develop locally while prod serves real users
- β Each instance has separate users, databases, and payment data
Variables that CHANGE between dev and prod:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY(pk_test_ β pk_live_)CLERK_SECRET_KEY(sk_test_ β sk_live_)CONVEX_DEPLOYMENT(dev:... β prod:...)NEXT_PUBLIC_CONVEX_URL(.convex.cloud β .convex.site)
Variables that STAY THE SAME:
NEXT_PUBLIC_SITE_NAMECSRF_SECRETSESSION_SECRET- All
NEXT_PUBLIC_CLERK_SIGN_*redirect URLs
π For advanced deployment topics including:
- 3-environment setup (dev β preview β production)
- Staging/QA workflows with test branches
- Database snapshots and rollback strategies
- Data cloning for testing
- Emergency hotfix procedures
See the complete guide: DEPLOYMENT.md
THE EASIEST TO SET UP. EASIEST IN TERMS OF CODE.
- β Clerk + Convex + Clerk Billing make it incredibly simple
- β No complex payment integrations - Clerk handles everything
- β Real-time user sync - Webhooks work out of the box
- β Beautiful UI - Tailark.com inspired landing page blocks
- β Production ready - Authentication, payments, and database included
- β Type safe - Full TypeScript support throughout
- β Security - Full security support throughout
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License.
Stop rebuilding the same foundation over and over. More Secure Starter DIY eliminates weeks of integration work by providing a complete, production-ready SaaS template with authentication, payments, and real-time data working seamlessly out of the box.
Built with β€οΈ using Next.js 15, Convex, Clerk, and modern web technologies.