Licensing and subscription management API for the Karate Debug VS Code Extension. This serverless API handles user authentication, subscription management, and license validation.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ VS Code │────▶│ Karate Debug │────▶│ Supabase │
│ Extension │ │ API (Vercel) │ │ (PostgreSQL) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ Stripe │
│ (Payments) │
└──────────────────┘
| Component | Technology | Purpose |
|---|---|---|
| Runtime | Vercel Serverless Functions | API hosting |
| Language | TypeScript | Type-safe development |
| Database | Supabase (PostgreSQL) | User data, subscriptions, machine activations |
| Payments | Stripe | Subscription billing |
| Auth | GitHub OAuth | User authentication |
| Tokens | JWT | License validation tokens |
- GitHub OAuth Authentication - Users sign in with GitHub
- Anonymous Machine Trials - 30-day trial without requiring signup
- Stripe Subscriptions - Monthly/annual billing
- Machine Licensing - Limit of 5 machines per user
- JWT License Tokens - 24-hour tokens for offline validation (configurable)
karate-debug-api/
├── api/ # Vercel serverless functions
│ ├── admin/ # Admin endpoints
│ │ └── user-reset.ts # Delete user for testing
│ ├── auth/ # Authentication endpoints
│ │ ├── callback.ts # GitHub OAuth callback
│ │ └── github.ts # GitHub token exchange
│ ├── license/ # License management
│ │ ├── activate.ts # Activate machine
│ │ ├── deactivate.ts # Deactivate machine
│ │ ├── machines.ts # List user's machines
│ │ ├── refresh.ts # Refresh expired/expiring token
│ │ └── validate.ts # Validate license & get token
│ ├── subscription/ # Subscription management
│ │ ├── checkout.ts # Create Stripe checkout
│ │ ├── portal.ts # Stripe billing portal
│ │ └── result.ts # Post-checkout result page
│ ├── trial/ # Anonymous trial management
│ │ ├── start.ts # Start anonymous machine trial
│ │ └── status.ts # Check trial status
│ ├── webhooks/ # External webhooks
│ │ └── stripe.ts # Stripe webhook handler
│ └── health.ts # Health check endpoint
├── lib/ # Shared utilities
│ ├── config.ts # Environment config & validation
│ ├── supabase.ts # Supabase client & types
│ ├── stripe.ts # Stripe client
│ ├── jwt.ts # License token signing/verification
│ ├── sanitize.ts # XSS protection utilities
│ ├── validation.ts # Zod request validation schemas
│ ├── sentry.ts # Error tracking
│ └── api-handler.ts # API wrapper with error handling
├── tests/ # Test files
│ ├── helpers/ # Test utilities
│ │ ├── mock-request.ts # Mock Vercel request/response
│ │ └── mock-supabase.ts # Mock data factories
│ ├── integration/ # Integration tests
│ │ └── license-validate.test.ts
│ ├── jwt.test.ts # JWT token tests
│ ├── sanitize.test.ts # XSS sanitization tests
│ └── validation.test.ts # Schema validation tests
├── supabase/ # Database
│ └── migrations/ # SQL migration files
├── scripts/ # Development scripts
│ ├── reset-dev-user.sh # Reset user for testing (DB + local state + VS Code)
│ └── reset-user.js # Node script for DB-only reset
└── .env.example # Environment template
- Node.js 18+
- npm or yarn
- Vercel CLI (
npm i -g vercel) - Stripe CLI (for webhook testing)
git clone https://github.com/j8d-org/karate-debug-api.git
cd karate-debug-api
npm installCopy the development environment file for local use:
cp .env.development .env.localEnvironment Files:
| File | Purpose | Committed |
|---|---|---|
.env.example |
Template with placeholders | ✅ Yes |
.env.development |
Dev credentials (dev Supabase, dev GitHub OAuth, Stripe test) | ❌ No |
.env.production |
Prod credentials (prod Supabase, prod GitHub OAuth, Stripe live) | ❌ No |
.env.local |
Local overrides (copy from .env.development) |
❌ No |
See Environment Variables below for details on each variable.
npm run devThis starts the Vercel development server at http://localhost:3000.
npm run test # Run tests once
npm run test:watch # Watch mode
npm run test:coverage # With coverage reportIn a separate terminal, forward Stripe webhooks to your local server:
stripe listen --forward-to localhost:3000/api/webhooks/stripeThe webhook secret in .env.development is pre-configured for stripe listen. If you get a different secret, update STRIPE_WEBHOOK_SECRET in .env.local.
To completely reset a user for fresh trial testing:
./scripts/reset-dev-user.sh [github_username] [vscode_profile]
# Defaults: j8d j8dThis script:
- Deletes the user from the dev Supabase database (users, subscriptions, machine_activations)
- Deletes the local license backup file (
~/.karate-debug-license) - Clears VS Code extension globalState for all profiles
- Sends a reload command to VS Code
For shorter trial testing, set TRIAL_MINUTES=5 in .env.local (overrides TRIAL_DAYS).
The project uses separate environments for development and production:
| Environment | Supabase Project | GitHub OAuth App | Stripe Mode | Vercel Scope |
|---|---|---|---|---|
| Local Dev | karate-debug-dev | Karate Debug (Dev) | Test | Development |
| Preview | karate-debug-dev | Karate Debug (Dev) | Test | Preview |
| Production | karate-debug | Karate Debug | Test* | Production |
*Switch to Stripe Live mode before public launch.
| Scope | When Used | Where Runs |
|---|---|---|
| Production | Deploys from main branch |
Vercel servers |
| Preview | Deploys from PRs/feature branches | Vercel servers |
| Development | vercel dev command |
Local machine |
| Project | Reference ID | Purpose |
|---|---|---|
| karate-debug | vcpilqkjckjpoeqjiyxl |
Production |
| karate-debug-dev | baxumzctorjohrrqshbp |
Development & Preview |
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
✅ | Your Supabase project URL |
SUPABASE_SERVICE_ROLE_KEY |
✅ | Supabase service role key (has admin access) |
GITHUB_CLIENT_ID |
✅ | GitHub OAuth App client ID |
GITHUB_CLIENT_SECRET |
✅ | GitHub OAuth App client secret |
STRIPE_SECRET_KEY |
✅ | Stripe secret key (use sk_test_ for dev) |
STRIPE_PRICE_ID |
✅ | Stripe Price ID for subscription |
STRIPE_WEBHOOK_SECRET |
✅ | Stripe webhook signing secret |
JWT_SECRET |
✅ | Secret for signing license tokens (min 32 chars) |
APP_URL |
❌ | Base URL (defaults to Vercel URL) |
SENTRY_DSN |
❌ | Sentry DSN for error tracking |
SENTRY_DEBUG |
❌ | Set to true to send errors to Sentry in dev |
TRIAL_DAYS |
❌ | Trial period length in days (default: 30) |
TRIAL_MINUTES |
❌ | Trial period in minutes - overrides TRIAL_DAYS if set (for testing) |
MAX_MACHINES |
❌ | Machine limit per user (default: 5) |
TOKEN_EXPIRY_HOURS |
❌ | JWT token expiry in hours (default: 24) |
WEBSITE_URL |
❌ | Website URL for purchase redirects (default: https://www.karatedebug.com) |
ADMIN_API_KEY |
❌ | Admin API key for protected operations (required in prod) |
openssl rand -hex 32| Endpoint | Method | Description |
|---|---|---|
/api/auth/github |
POST | Exchange GitHub code for access token |
/api/auth/callback |
GET | OAuth callback (redirects to extension) |
| Endpoint | Method | Description |
|---|---|---|
/api/license/validate |
POST | Validate license, return JWT token |
/api/license/refresh |
POST | Refresh an expired/expiring token |
/api/license/activate |
POST | Activate a machine |
/api/license/deactivate |
POST | Deactivate a machine |
/api/license/machines |
GET | List user's activated machines |
| Endpoint | Method | Description |
|---|---|---|
/api/trial/start |
POST | Start anonymous machine trial (30 days) |
/api/trial/status |
GET | Check trial status for a machine |
| Endpoint | Method | Description |
|---|---|---|
/api/subscription/checkout |
POST | Create Stripe checkout session |
/api/subscription/portal |
POST | Create Stripe billing portal session |
/api/subscription/result |
GET | Post-checkout result page (success/cancel) |
| Endpoint | Method | Description |
|---|---|---|
/api/purchase/start |
GET | Initiate purchase from website (redirects to GitHub OAuth) |
/api/purchase/callback |
GET | Handle OAuth callback, create user, redirect to Stripe |
| Endpoint | Method | Description |
|---|---|---|
/api/webhooks/stripe |
POST | Stripe webhook handler |
/api/health |
GET | Health check with DB connectivity |
| Endpoint | Method | Description |
|---|---|---|
/api/admin/user-reset |
POST | Delete user, subscription, and machines (requires X-Admin-Key header) |
The API uses Supabase (PostgreSQL) with the following tables:
- users - GitHub user info, Stripe customer ID
- subscriptions - Subscription status, trial dates
- machine_activations - Activated machines per user (max 5)
- anonymous_trials - Machine-based anonymous trials (pre-signup)
- webhook_events - Processed Stripe events (idempotency)
See supabase/migrations/ for full schema.
- Install Vercel CLI:
npm i -g vercel - Link project:
vercel link - Set environment variables in Vercel dashboard
- Deploy:
vercel --prod
Option 1: Using Supabase CLI (Recommended)
# Link to your project (dev or prod)
supabase link --project-ref <project-ref>
# Push migrations
supabase db pushOption 2: Manual via Dashboard
- Go to your Supabase Dashboard
- Navigate to SQL Editor
- Run each file in
supabase/migrations/in order
| File | Description |
|---|---|
000_initial_schema.sql |
Users, subscriptions, machines tables + triggers |
001_webhook_events.sql |
Webhook idempotency table |
002_rename_machines_table.sql |
Rename machines to machine_activations |
003_enable_rls.sql |
Enable Row Level Security on tables |
004_fix_function_search_path.sql |
Fix function search path security |
005_drop_old_machines_table.sql |
Drop deprecated machines table |
006_anonymous_trials.sql |
Anonymous machine-based trials table |
007_analytics_platform_tracking.sql |
Platform tracking + analytics views |
008_backfill_platform_from_user_agent.sql |
Backfill platform data |
009_fix_analytics_views_security.sql |
Fix SECURITY INVOKER on analytics views |
010_client_version_tracking.sql |
Client version tracking for extensions/plugins |
The project uses Vitest for fast, TypeScript-native testing.
npm run test # Run all tests once
npm run test:watch # Watch mode (re-runs on file changes)
npm run test:coverage # Run with coverage report| File | Coverage |
|---|---|
tests/jwt.test.ts |
Token signing, verification, expiration |
tests/sanitize.test.ts |
XSS prevention utilities |
tests/validation.test.ts |
Zod schema validation |
tests/integration/license-validate.test.ts |
License validation API flow |
Tests are located in the tests/ directory. Example:
import { describe, it, expect } from 'vitest';
import { escapeHtml } from '../lib/sanitize';
describe('escapeHtml', () => {
it('should escape HTML special characters', () => {
expect(escapeHtml('<script>')).toBe('<script>');
});
});- XSS Protection: All user inputs sanitized before HTML output
- Webhook Idempotency: Stripe events tracked to prevent duplicate processing
- Environment Validation: Required variables validated at startup
- Error Tracking: Sentry integration for production monitoring
The API uses Sentry for error tracking and monitoring.
- Create a Sentry project at sentry.io
- Copy your DSN from Settings → Projects → [Your Project] → Client Keys (DSN)
- Add to your environment:
SENTRY_DSN=https://your-key@your-org.ingest.sentry.io/your-project-id
All API endpoints use the withApiHandler wrapper which automatically:
- Captures unhandled exceptions to Sentry
- Adds request context (method, URL, timing)
- Includes a unique
requestIdfor tracing - Flushes events before responding
| Environment | Behavior |
|---|---|
development |
Errors logged locally only (unless SENTRY_DEBUG=true) |
production |
All errors sent to Sentry |
To test Sentry locally, add SENTRY_DEBUG=true to your .env.
import { withApiHandler } from '../lib/api-handler';
// Wrap your handler - errors are automatically captured
export default withApiHandler(async (req, res) => {
// Your code here
// Any thrown errors go to Sentry automatically
});For manual error capture:
import { captureException, captureMessage } from '../lib/sentry';
// Capture an error
captureException(error, { userId, action: 'checkout' });
// Capture a message
captureMessage('User exceeded rate limit', 'warning');You may see vulnerabilities reported when running npm audit. These are in the
vercel CLI package (esbuild, path-to-regexp, undici) which is a devDependency
only and does not affect production deployments.
| Package | Severity | Production Risk |
|---|---|---|
| esbuild | Moderate | ❌ None - dev only |
| path-to-regexp | High | ❌ None - dev only |
| undici | Moderate | ❌ None - dev only |
The suggested fix (npm audit fix --force) would downgrade to an older Vercel CLI
version, which is not recommended. These will be resolved when Vercel updates their CLI.
Private - All rights reserved.