Skip to main content

CODITECT Cloud Backend - Complete Commercial Flow Requirements

Date: December 1, 2025, 5:00 AM EST Purpose: Define all components needed for complete user → payment → license → usage flow Target: Production-ready commercial license management system


🎯 Complete User Journey

User Journey: Sign Up → Pay → Get License → Run CODITECT → Continue Paying Monthly

┌─────────────────┐
│ 1. Sign Up │ User creates account
└────────┬────────┘


┌─────────────────┐
│ 2. Choose Plan │ Select subscription tier
└────────┬────────┘


┌─────────────────┐
│ 3. Pay │ Stripe checkout flow
└────────┬────────┘


┌─────────────────┐
│ 4. Get License │ License key emailed
└────────┬────────┘


┌─────────────────┐
│ 5. Install │ Download & install CODITECT
└────────┬────────┘


┌─────────────────┐
│ 6. Activate │ Enter license key
└────────┬────────┘


┌─────────────────┐
│ 7. Validate │ Check license with cloud
└────────┬────────┘


┌─────────────────┐
│ 8. Run │ Use CODITECT with heartbeats
└────────┬────────┘


┌─────────────────┐
│ 9. Renew │ Monthly subscription charge
└─────────────────┘

│ (loop back to step 8)


[Continue using]

📊 Current State vs Required Components

Legend

  • Complete - Fully implemented and tested
  • Partial - Infrastructure ready, implementation incomplete
  • Missing - Not started
  • 🔴 Critical - Blocking commercial launch

Step 1: User Registration

Current State: ❌ Missing (0%)

What We Have:

  • Nothing related to user registration

What We Need:

1.1 Frontend Registration Form 🔴

Status: ❌ Missing Estimated Time: 2 days Priority: P0 (Critical)

Requirements:

// Registration form fields
interface RegistrationForm {
email: string; // Primary identifier
password: string; // Min 8 chars, complexity requirements
fullName: string; // User's full name
companyName?: string; // Optional for business users
agreeToTerms: boolean; // Legal requirement
}

Components Needed:

  • Sign-up page (React/Next.js)
  • Form validation
  • Password strength indicator
  • Terms of Service acceptance
  • Privacy policy link
  • Email verification flow

Dependencies:

  • Frontend web application (not yet built)
  • Hosting (can use GKE with Ingress)

1.2 Backend User Registration API 🔴

Status: ❌ Missing Estimated Time: 2 days Priority: P0 (Critical)

Endpoints Needed:

# POST /api/v1/auth/register
# Request:
{
"email": "user@example.com",
"password": "SecurePass123!",
"full_name": "John Doe",
"company_name": "Acme Corp"
}

# Response:
{
"user_id": "usr_abc123",
"email": "user@example.com",
"status": "pending_verification",
"verification_email_sent": true
}

Implementation Requirements:

  • Hash passwords (bcrypt, Argon2)
  • Generate email verification token
  • Store user in PostgreSQL
  • Send verification email
  • Handle duplicate email prevention
  • Rate limiting (prevent spam registrations)

Database Schema:

CREATE TABLE users (
user_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
full_name VARCHAR(255) NOT NULL,
company_name VARCHAR(255),
email_verified BOOLEAN DEFAULT FALSE,
verification_token VARCHAR(255),
verification_token_expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login_at TIMESTAMP,
status VARCHAR(50) DEFAULT 'active' -- active, suspended, deleted
);

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_verification_token ON users(verification_token);

1.3 Email Verification 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Flow:

  1. User clicks verification link in email
  2. Backend validates token
  3. Mark email as verified
  4. Redirect to login or onboarding

Endpoints:

# GET /api/v1/auth/verify-email?token={verification_token}
# Response: Redirect to /login with success message

# POST /api/v1/auth/resend-verification
# Request: {"email": "user@example.com"}
# Response: {"message": "Verification email sent"}

Email Service Integration:

  • Option 1: SendGrid (recommended)

    • Cost: Free tier (100 emails/day), $15/month (40K emails)
    • Easy API integration
    • Template support
    • Deliverability tracking
  • Option 2: AWS SES

    • Cost: $0.10 per 1,000 emails
    • More complex setup
    • Requires domain verification

Email Template:

Subject: Verify your CODITECT account

Hi {full_name},

Welcome to CODITECT! Please verify your email address by clicking the link below:

{verification_url}

This link expires in 24 hours.

If you didn't create this account, please ignore this email.

Thanks,
The CODITECT Team

1.4 Firebase Authentication Integration ⏳

Status: ⏳ Partial (Firebase project created, not configured) Estimated Time: 1 day Priority: P0 (Critical)

Current State:

  • Firebase service account exists
  • Key stored in Secret Manager
  • Django middleware stub exists

Remaining Work:

  1. Configure Firebase Authentication in console
  2. Enable Google OAuth provider
  3. Enable GitHub OAuth provider
  4. Update Django middleware to validate tokens
  5. Test OAuth flow end-to-end

Why Firebase?

  • Social login (Google, GitHub) reduces friction
  • Secure token validation
  • No password management (for OAuth users)
  • Industry-standard authentication

Step 2: Choose Subscription Plan

Current State: ❌ Missing (0%)

What We Have:

  • Nothing related to subscription plans

What We Need:

2.1 Subscription Plans Definition 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Recommended Plans:

Free Trial (14 days):

  • 1 concurrent seat
  • All features
  • No credit card required
  • Auto-expires after 14 days

Starter ($29/month):

  • 3 concurrent seats
  • All core features
  • Email support
  • Monthly billing only

Professional ($99/month):

  • 10 concurrent seats
  • Priority support
  • Advanced features (custom agents)
  • Annual billing option (save 20%)

Team ($299/month):

  • 50 concurrent seats
  • Dedicated support
  • SSO integration
  • Usage analytics
  • Annual billing option (save 20%)

Enterprise (Custom pricing):

  • Unlimited seats
  • On-premise deployment option
  • Custom contract
  • SLA guarantees

Database Schema:

CREATE TABLE subscription_plans (
plan_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
plan_name VARCHAR(100) UNIQUE NOT NULL, -- 'starter', 'professional', 'team', 'enterprise'
display_name VARCHAR(255) NOT NULL, -- 'Starter Plan'
description TEXT,
max_concurrent_seats INTEGER NOT NULL,
monthly_price_cents INTEGER, -- NULL for free trial
annual_price_cents INTEGER, -- NULL if annual not available
stripe_monthly_price_id VARCHAR(255), -- Stripe Price ID
stripe_annual_price_id VARCHAR(255),
features JSONB, -- {"priority_support": true, "custom_agents": false}
is_active BOOLEAN DEFAULT TRUE,
trial_duration_days INTEGER, -- NULL if no trial
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Seed data
INSERT INTO subscription_plans VALUES
('starter', 'Starter Plan', 3, 2900, NULL, 'price_starter_monthly', NULL),
('professional', 'Professional Plan', 10, 9900, 95040, 'price_pro_monthly', 'price_pro_annual'),
('team', 'Team Plan', 50, 29900, 287040, 'price_team_monthly', 'price_team_annual');

2.2 Plan Selection UI 🔴

Status: ❌ Missing Estimated Time: 2 days Priority: P0 (Critical)

Components:

  • Pricing page showing all plans
  • Feature comparison table
  • "Start Free Trial" vs "Subscribe" CTAs
  • Plan upgrade/downgrade flow
  • Billing cycle toggle (monthly/annual)

Example UI Flow:

┌─────────────────────────────────────────┐
│ Choose Your Plan │
├─────────────────────────────────────────┤
│ │
│ [Free Trial] [Starter] [Pro] [Team]│
│ $0 $29/mo $99/mo $299/mo│
│ │
│ 1 seat 3 seats 10 seats 50 │
│ 14 days All core Priority SSO │
│ All features features support │
│ │
│ [Try Free] [Subscribe] [Subscribe] │
└─────────────────────────────────────────┘

2.3 Plan Selection API 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Endpoints:

# GET /api/v1/plans
# Response: List of available subscription plans
{
"plans": [
{
"plan_id": "plan_abc123",
"plan_name": "starter",
"display_name": "Starter Plan",
"monthly_price_cents": 2900,
"max_concurrent_seats": 3,
"features": {...}
},
...
]
}

# POST /api/v1/subscriptions/select-plan
# Request: {"plan_id": "plan_abc123", "billing_cycle": "monthly"}
# Response: {"checkout_url": "https://checkout.stripe.com/..."}

Step 3: Payment Processing

Current State: ❌ Missing (0%)

What We Have:

  • Nothing related to payment processing

What We Need:

3.1 Stripe Integration 🔴

Status: ❌ Missing Estimated Time: 3 days Priority: P0 (Critical - Blocks revenue!)

Stripe Setup Required:

  1. Create Stripe account
  2. Add bank account for payouts
  3. Configure company information
  4. Set up tax calculation (Stripe Tax)
  5. Create products and prices in Stripe Dashboard

Stripe API Integration:

import stripe

# Create checkout session
def create_checkout_session(user_id: str, plan_id: str, billing_cycle: str):
stripe.api_key = settings.STRIPE_SECRET_KEY

# Get plan details
plan = SubscriptionPlan.objects.get(plan_id=plan_id)

# Determine price ID based on billing cycle
price_id = (plan.stripe_annual_price_id if billing_cycle == 'annual'
else plan.stripe_monthly_price_id)

# Create Stripe checkout session
session = stripe.checkout.Session.create(
customer_email=user.email,
mode='subscription',
payment_method_types=['card'],
line_items=[{
'price': price_id,
'quantity': 1
}],
success_url=f'{settings.FRONTEND_URL}/checkout/success?session_id={{CHECKOUT_SESSION_ID}}',
cancel_url=f'{settings.FRONTEND_URL}/pricing',
metadata={
'user_id': user_id,
'plan_id': plan_id
}
)

return session.url

Endpoints:

# POST /api/v1/payments/create-checkout-session
# Request:
{
"plan_id": "plan_abc123",
"billing_cycle": "monthly" // or "annual"
}

# Response:
{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_...",
"session_id": "cs_test_abc123"
}

3.2 Stripe Webhooks 🔴

Status: ❌ Missing Estimated Time: 2 days Priority: P0 (Critical)

Why Webhooks?

  • Asynchronous payment processing
  • Handle subscription lifecycle events
  • Secure server-to-server communication

Webhook Events to Handle:

1. checkout.session.completed (Payment successful)

def handle_checkout_completed(event):
session = event['data']['object']
user_id = session['metadata']['user_id']
stripe_subscription_id = session['subscription']

# Create subscription record
Subscription.objects.create(
user_id=user_id,
stripe_subscription_id=stripe_subscription_id,
status='active',
current_period_start=...,
current_period_end=...
)

# Generate license key
license_key = generate_license_key()

# Create license
License.objects.create(
user_id=user_id,
license_key=license_key,
max_concurrent_seats=plan.max_concurrent_seats,
status='active'
)

# Send license email
send_license_email(user.email, license_key)

2. invoice.payment_succeeded (Monthly renewal successful)

def handle_invoice_paid(event):
invoice = event['data']['object']
subscription_id = invoice['subscription']

# Update subscription period
subscription = Subscription.objects.get(stripe_subscription_id=subscription_id)
subscription.current_period_end = datetime.fromtimestamp(invoice['period_end'])
subscription.save()

# Ensure license remains active
license = License.objects.get(user_id=subscription.user_id)
license.status = 'active'
license.save()

3. invoice.payment_failed (Payment failed)

def handle_invoice_failed(event):
invoice = event['data']['object']
subscription_id = invoice['subscription']

# Mark subscription as past_due
subscription = Subscription.objects.get(stripe_subscription_id=subscription_id)
subscription.status = 'past_due'
subscription.save()

# Suspend license (with grace period)
license = License.objects.get(user_id=subscription.user_id)
license.status = 'suspended'
license.suspended_at = datetime.now()
license.grace_period_ends_at = datetime.now() + timedelta(days=7)
license.save()

# Send payment failed email
send_payment_failed_email(subscription.user.email)

4. customer.subscription.deleted (Subscription cancelled)

def handle_subscription_deleted(event):
subscription_data = event['data']['object']
subscription_id = subscription_data['id']

# Mark subscription as cancelled
subscription = Subscription.objects.get(stripe_subscription_id=subscription_id)
subscription.status = 'cancelled'
subscription.cancelled_at = datetime.now()
subscription.save()

# Deactivate license
license = License.objects.get(user_id=subscription.user_id)
license.status = 'inactive'
license.deactivated_at = datetime.now()
license.save()

Webhook Endpoint:

# POST /api/v1/webhooks/stripe
# Headers: Stripe-Signature (for verification)

@csrf_exempt
def stripe_webhook(request):
payload = request.body
sig_header = request.META['HTTP_STRIPE_SIGNATURE']

try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
)
except ValueError:
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError:
return HttpResponse(status=400)

# Handle event
if event['type'] == 'checkout.session.completed':
handle_checkout_completed(event)
elif event['type'] == 'invoice.payment_succeeded':
handle_invoice_paid(event)
elif event['type'] == 'invoice.payment_failed':
handle_invoice_failed(event)
elif event['type'] == 'customer.subscription.deleted':
handle_subscription_deleted(event)

return HttpResponse(status=200)

Database Schema:

CREATE TABLE subscriptions (
subscription_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(user_id) NOT NULL,
plan_id UUID REFERENCES subscription_plans(plan_id) NOT NULL,
stripe_subscription_id VARCHAR(255) UNIQUE NOT NULL,
stripe_customer_id VARCHAR(255) NOT NULL,
status VARCHAR(50) NOT NULL, -- active, past_due, cancelled, trialing
billing_cycle VARCHAR(20) NOT NULL, -- monthly, annual
current_period_start TIMESTAMP NOT NULL,
current_period_end TIMESTAMP NOT NULL,
trial_end TIMESTAMP,
cancelled_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_subscriptions_user_id ON subscriptions(user_id);
CREATE INDEX idx_subscriptions_stripe_subscription_id ON subscriptions(stripe_subscription_id);
CREATE INDEX idx_subscriptions_status ON subscriptions(status);

3.3 Payment Success/Failure Handling 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Success Flow:

  1. Stripe redirects to /checkout/success?session_id=cs_...
  2. Frontend fetches session details from backend
  3. Display success message with license key
  4. Email license key to user
  5. Redirect to dashboard or download page

Failure Flow:

  1. Stripe redirects to /pricing (cancel URL)
  2. Display "Payment cancelled" message
  3. Offer to retry payment

Step 4: License Generation & Delivery

Current State: ❌ Missing (0%)

What We Have:

  • Nothing related to license generation

What We Need:

4.1 License Key Generation 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

License Key Format:

CODITECT-XXXX-XXXX-XXXX-XXXX-XXXX

Example: CODITECT-A7B2-9C4D-E6F8-1G3H-5J7K

Generation Algorithm:

import secrets
import hashlib

def generate_license_key() -> str:
"""
Generate cryptographically secure license key.
Format: CODITECT-XXXX-XXXX-XXXX-XXXX-XXXX
"""
# Generate 20 random bytes
random_bytes = secrets.token_bytes(20)

# Create base key (alphanumeric, uppercase)
base_key = hashlib.sha256(random_bytes).hexdigest()[:20].upper()

# Format into groups of 4
groups = [base_key[i:i+4] for i in range(0, 20, 4)]

# Return formatted key
return f"CODITECT-{'-'.join(groups)}"

# Validation
def validate_license_key_format(key: str) -> bool:
"""Validate license key format"""
pattern = r'^CODITECT-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$'
return re.match(pattern, key) is not None

Database Schema:

CREATE TABLE licenses (
license_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(user_id) NOT NULL,
license_key VARCHAR(50) UNIQUE NOT NULL,
subscription_id UUID REFERENCES subscriptions(subscription_id),
max_concurrent_seats INTEGER NOT NULL,
status VARCHAR(50) DEFAULT 'active', -- active, suspended, inactive
issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
activated_at TIMESTAMP,
suspended_at TIMESTAMP,
deactivated_at TIMESTAMP,
grace_period_ends_at TIMESTAMP,
last_validated_at TIMESTAMP,
validation_count INTEGER DEFAULT 0,
metadata JSONB -- Additional license metadata
);

CREATE INDEX idx_licenses_user_id ON licenses(user_id);
CREATE INDEX idx_licenses_license_key ON licenses(license_key);
CREATE INDEX idx_licenses_status ON licenses(status);

-- Active sessions for seat tracking
CREATE TABLE license_sessions (
session_id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
license_id UUID REFERENCES licenses(license_id) NOT NULL,
hardware_id VARCHAR(255) NOT NULL, -- Machine fingerprint
user_ip VARCHAR(45), -- IPv4 or IPv6
user_agent TEXT,
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_heartbeat_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL, -- TTL for automatic cleanup
UNIQUE(license_id, hardware_id)
);

CREATE INDEX idx_license_sessions_license_id ON license_sessions(license_id);
CREATE INDEX idx_license_sessions_expires_at ON license_sessions(expires_at);

4.2 License Email Delivery 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Email Template:

Subject: Your CODITECT License Key

Hi {full_name},

Thank you for subscribing to CODITECT {plan_name}!

Your License Key:
┌─────────────────────────────────────────┐
│ CODITECT-A7B2-9C4D-E6F8-1G3H-5J7K │
└─────────────────────────────────────────┘

Plan Details:
- Plan: {plan_display_name}
- Concurrent Seats: {max_concurrent_seats}
- Billing: ${monthly_price}/month
- Next billing date: {next_billing_date}

Getting Started:
1. Download CODITECT: https://coditect.ai/download
2. Install on your machine
3. Run: coditect auth login --license {license_key}
4. Start using CODITECT!

Need help? Visit our documentation: https://docs.coditect.ai

Thanks,
The CODITECT Team

---
Manage subscription: https://app.coditect.ai/billing

Implementation:

def send_license_email(user: User, license: License, subscription: Subscription):
"""Send license key email via SendGrid"""

# Get plan details
plan = subscription.plan

# Create email
message = Mail(
from_email='licenses@coditect.ai',
to_emails=user.email,
subject='Your CODITECT License Key',
html_content=render_template('license_email.html',
full_name=user.full_name,
license_key=license.license_key,
plan_display_name=plan.display_name,
max_concurrent_seats=plan.max_concurrent_seats,
monthly_price=plan.monthly_price_cents / 100,
next_billing_date=subscription.current_period_end
)
)

# Send via SendGrid
sg = SendGridAPIClient(settings.SENDGRID_API_KEY)
response = sg.send(message)

# Log email sent
logger.info(f"License email sent to {user.email}, status: {response.status_code}")

Step 5: Download & Install CODITECT

Current State: ⏳ Partial (40%)

What We Have:

  • CODITECT core framework exists
  • Can be installed via pip
  • Local-first architecture ready

What We Need:

5.1 Distribution Method 🟡

Status: ⏳ Partial Estimated Time: 2 days Priority: P1 (Important)

Options:

Option 1: PyPI Package (Recommended for MVP)

pip install coditect-core
  • Pros: Easy distribution, automatic updates, familiar to developers
  • Cons: Requires Python installed, not ideal for non-technical users

Option 2: Standalone Binary (Long-term)

# Windows
coditect-setup.exe

# macOS
coditect.dmg

# Linux
coditect.AppImage
  • Pros: No dependencies, works for non-technical users
  • Cons: Larger file size, OS-specific builds, code signing required
  • Tool: PyInstaller or Nuitka for binary creation

Recommendation: Start with PyPI, add standalone binaries in Phase 6

5.2 Installation Wizard 🟡

Status: ❌ Missing Estimated Time: 2 days Priority: P2 (Nice to have)

Flow:

┌─────────────────────────────────────┐
│ Welcome to CODITECT Setup │
│ │
│ 1. Install CODITECT │
│ 2. Enter License Key │
│ 3. Validate & Activate │
│ 4. Configure Settings (optional) │
│ 5. Launch CODITECT │
└─────────────────────────────────────┘

For MVP: Simple CLI is acceptable

# Install
pip install coditect-core

# Enter license
coditect auth login --license CODITECT-XXXX-XXXX-XXXX-XXXX-XXXX

# Done!

Step 6: License Activation

Current State: ⏳ Partial (20%)

What We Have:

  • Infrastructure for license validation (GKE, Cloud SQL, Redis)
  • Database schema ready (needs migration)

What We Need:

6.1 License Activation Command 🔴

Status: ❌ Missing Estimated Time: 2 days Priority: P0 (Critical)

CLI Command:

coditect auth login --license CODITECT-A7B2-9C4D-E6F8-1G3H-5J7K

Implementation in coditect-core:

# coditect/cli/auth.py
import click
import requests
from coditect.license import LicenseClient

@click.command()
@click.option('--license', required=True, help='Your license key')
def login(license: str):
"""Activate CODITECT with your license key"""

click.echo("Activating CODITECT...")

# Initialize license client
client = LicenseClient(
api_url=os.getenv('CODITECT_LICENSE_API', 'https://api.coditect.ai'),
license_key=license
)

try:
# Validate license format
if not client.validate_key_format(license):
click.echo("❌ Invalid license key format", err=True)
return

# Acquire license (calls cloud API)
result = client.acquire_license()

if result.success:
# Save license locally
client.save_license_config(result.license_data)

click.echo("✅ License activated successfully!")
click.echo(f" Plan: {result.plan_name}")
click.echo(f" Seats: {result.active_seats}/{result.max_seats}")
click.echo(f" Expires: {result.expires_at}")
else:
click.echo(f"❌ Activation failed: {result.error_message}", err=True)

except requests.exceptions.ConnectionError:
click.echo("❌ Cannot reach license server. Check your internet connection.", err=True)
except Exception as e:
click.echo(f"❌ Unexpected error: {str(e)}", err=True)

6.2 Hardware Fingerprinting 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Purpose: Uniquely identify machines to prevent license sharing

Implementation:

import platform
import hashlib
import uuid

def generate_hardware_id() -> str:
"""
Generate stable hardware fingerprint.

Uses:
- Machine UUID (best, but not always available)
- MAC address (fallback)
- Hostname + OS + Architecture (last resort)
"""
try:
# Try to get machine UUID (most stable)
machine_uuid = uuid.UUID(int=uuid.getnode()).hex
return hashlib.sha256(machine_uuid.encode()).hexdigest()[:32]
except:
# Fallback: combine multiple system attributes
components = [
platform.node(), # Hostname
platform.system(), # OS
platform.machine(), # Architecture
str(uuid.getnode()) # MAC address
]

combined = '-'.join(components)
return hashlib.sha256(combined.encode()).hexdigest()[:32]

# Example output: "a7b2c4d6e8f0a1b3c5d7e9f1a3b5c7d9"

Considerations:

  • Must be stable across reboots
  • Should survive minor OS updates
  • Must be different per machine
  • Should work across Windows/macOS/Linux

Step 7: License Validation with Cloud

Current State: ⏳ Partial (50%)

What We Have:

  • Cloud infrastructure ready (GKE, Cloud SQL, Redis)
  • Network connectivity configured
  • External IP accessible (136.114.0.156)

What We Need:

7.1 License Acquisition Endpoint 🔴

Status: ⏳ Partial (stub exists, not implemented) Estimated Time: 2 days Priority: P0 (Critical - Blocking usage!)

Endpoint:

# POST /api/v1/licenses/acquire
# Headers: Authorization: Bearer {firebase_token}
# Request:
{
"license_key": "CODITECT-A7B2-9C4D-E6F8-1G3H-5J7K",
"hardware_id": "a7b2c4d6e8f0a1b3c5d7e9f1a3b5c7d9",
"machine_info": {
"hostname": "johns-macbook",
"os": "macOS 14.1",
"coditect_version": "1.0.0"
}
}

# Response (success):
{
"session_id": "session_abc123",
"signed_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2025-12-01T05:06:00Z",
"max_concurrent_seats": 10,
"current_seats_used": 3,
"plan_name": "Professional",
"heartbeat_interval_seconds": 300
}

# Response (failure - no seats available):
{
"error": "no_seats_available",
"message": "All 10 concurrent seats are in use",
"active_sessions": [
{"hardware_id": "...", "hostname": "...", "started_at": "..."},
...
]
}

# Response (failure - subscription inactive):
{
"error": "subscription_inactive",
"message": "Your subscription is not active. Please update your payment method.",
"grace_period_ends": "2025-12-08T00:00:00Z"
}

Implementation (Django):

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django.db import transaction
import redis

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def acquire_license(request):
"""Acquire a license seat for a specific machine"""

license_key = request.data.get('license_key')
hardware_id = request.data.get('hardware_id')
machine_info = request.data.get('machine_info', {})

# Validate license key format
if not validate_license_key_format(license_key):
return Response({'error': 'invalid_license_key'}, status=400)

# Get license from database
try:
license = License.objects.select_related('subscription', 'subscription__plan').get(
license_key=license_key,
user_id=request.user.id
)
except License.DoesNotExist:
return Response({'error': 'license_not_found'}, status=404)

# Check license status
if license.status != 'active':
if license.status == 'suspended':
return Response({
'error': 'subscription_inactive',
'message': 'Your subscription is not active',
'grace_period_ends': license.grace_period_ends_at
}, status=402) # 402 Payment Required
else:
return Response({'error': 'license_inactive'}, status=403)

# Check subscription status
subscription = license.subscription
if subscription.status not in ['active', 'trialing']:
return Response({
'error': 'subscription_inactive',
'message': f'Subscription status: {subscription.status}'
}, status=402)

# Atomic seat acquisition using Redis Lua script
redis_client = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT)

# Lua script for atomic seat check and increment
lua_script = """
local license_key = KEYS[1]
local max_seats = tonumber(ARGV[1])
local ttl_seconds = tonumber(ARGV[2])

local current_seats = redis.call('GET', license_key)
current_seats = current_seats and tonumber(current_seats) or 0

if current_seats < max_seats then
redis.call('INCR', license_key)
redis.call('EXPIRE', license_key, ttl_seconds)
return {1, current_seats + 1}
else
return {0, current_seats}
end
"""

# Execute Lua script
result = redis_client.eval(
lua_script,
1, # Number of keys
f'seats:{license.license_id}', # Key
license.max_concurrent_seats, # Max seats
360 # TTL: 6 minutes (heartbeat every 5 min)
)

success, seat_count = result

if not success:
# No seats available
active_sessions = LicenseSession.objects.filter(
license=license,
expires_at__gt=timezone.now()
).values('hardware_id', 'started_at', 'last_heartbeat_at')

return Response({
'error': 'no_seats_available',
'message': f'All {license.max_concurrent_seats} seats in use',
'current_seats_used': seat_count,
'active_sessions': list(active_sessions)
}, status=429) # 429 Too Many Requests

# Create session record in database
with transaction.atomic():
session = LicenseSession.objects.create(
license=license,
hardware_id=hardware_id,
user_ip=request.META.get('REMOTE_ADDR'),
user_agent=request.META.get('HTTP_USER_AGENT'),
expires_at=timezone.now() + timedelta(minutes=6),
metadata={
'machine_info': machine_info
}
)

# Update license last_validated_at
license.last_validated_at = timezone.now()
license.validation_count += 1
license.save()

# Generate signed license token (with Cloud KMS in Phase 2)
# For now, use Django signing
signed_token = signing.dumps({
'session_id': str(session.session_id),
'license_key': license_key,
'hardware_id': hardware_id,
'expires_at': session.expires_at.isoformat()
})

return Response({
'session_id': str(session.session_id),
'signed_token': signed_token,
'expires_at': session.expires_at,
'max_concurrent_seats': license.max_concurrent_seats,
'current_seats_used': seat_count,
'plan_name': subscription.plan.display_name,
'heartbeat_interval_seconds': 300
}, status=200)

7.2 License Client SDK 🔴

Status: ❌ Missing Estimated Time: 2 days Priority: P0 (Critical)

Implementation in coditect-core:

# coditect/license/client.py
import requests
import json
from pathlib import Path
from typing import Optional
from datetime import datetime, timedelta

class LicenseClient:
"""Client for CODITECT license validation"""

def __init__(self, api_url: str, license_key: str):
self.api_url = api_url
self.license_key = license_key
self.session_id: Optional[str] = None
self.signed_token: Optional[str] = None
self.expires_at: Optional[datetime] = None

def acquire_license(self) -> LicenseResult:
"""Acquire a license seat from the cloud"""

# Get hardware ID
hardware_id = generate_hardware_id()

# Call API
response = requests.post(
f'{self.api_url}/api/v1/licenses/acquire',
json={
'license_key': self.license_key,
'hardware_id': hardware_id,
'machine_info': self._get_machine_info()
},
headers={'Authorization': f'Bearer {self._get_auth_token()}'}
)

if response.status_code == 200:
data = response.json()

# Store session info
self.session_id = data['session_id']
self.signed_token = data['signed_token']
self.expires_at = datetime.fromisoformat(data['expires_at'])

return LicenseResult(
success=True,
license_data=data
)
else:
error_data = response.json()
return LicenseResult(
success=False,
error_code=error_data.get('error'),
error_message=error_data.get('message')
)

def save_license_config(self, license_data: dict):
"""Save license configuration locally"""
config_dir = Path.home() / '.coditect'
config_dir.mkdir(exist_ok=True)

config_file = config_dir / 'license.json'
config_file.write_text(json.dumps({
'license_key': self.license_key,
'session_id': license_data['session_id'],
'signed_token': license_data['signed_token'],
'expires_at': license_data['expires_at'],
'plan_name': license_data['plan_name']
}, indent=2))

Step 8: Running CODITECT with Heartbeats

Current State: ⏳ Partial (30%)

What We Have:

  • CODITECT core framework runs locally
  • Can execute AI-powered development tasks

What We Need:

8.1 Heartbeat Mechanism 🔴

Status: ❌ Missing Estimated Time: 2 days Priority: P0 (Critical)

Purpose:

  • Keep license seat active while CODITECT is running
  • Automatically release seat if CODITECT crashes or is force-killed
  • Detect zombie sessions (no heartbeat for 6+ minutes)

Heartbeat Endpoint:

# POST /api/v1/licenses/heartbeat
# Headers: Authorization: Bearer {firebase_token}
# Request:
{
"session_id": "session_abc123",
"license_key": "CODITECT-A7B2-9C4D-E6F8-1G3H-5J7K"
}

# Response:
{
"status": "ok",
"expires_at": "2025-12-01T05:11:00Z",
"subscription_status": "active"
}

Implementation (Django):

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def heartbeat(request):
"""Extend license session TTL"""

session_id = request.data.get('session_id')
license_key = request.data.get('license_key')

# Get session
try:
session = LicenseSession.objects.select_related('license').get(
session_id=session_id,
license__license_key=license_key,
license__user_id=request.user.id
)
except LicenseSession.DoesNotExist:
return Response({'error': 'session_not_found'}, status=404)

# Check if session expired
if session.expires_at < timezone.now():
return Response({'error': 'session_expired'}, status=410)

# Extend session TTL
session.last_heartbeat_at = timezone.now()
session.expires_at = timezone.now() + timedelta(minutes=6)
session.save()

# Extend Redis TTL
redis_client.expire(f'seats:{session.license.license_id}', 360)

# Check subscription status
subscription = session.license.subscription

return Response({
'status': 'ok',
'expires_at': session.expires_at,
'subscription_status': subscription.status
}, status=200)

Client Implementation:

# coditect/license/heartbeat.py
import threading
import time

class HeartbeatThread(threading.Thread):
"""Background thread to send heartbeats every 5 minutes"""

def __init__(self, license_client: LicenseClient):
super().__init__(daemon=True)
self.license_client = license_client
self.running = True

def run(self):
"""Send heartbeat every 5 minutes"""
while self.running:
try:
# Sleep first (initial seat acquired via acquire_license)
time.sleep(300) # 5 minutes

# Send heartbeat
result = self.license_client.send_heartbeat()

if not result.success:
logger.error(f"Heartbeat failed: {result.error_message}")
# TODO: Show warning to user

except Exception as e:
logger.error(f"Heartbeat error: {str(e)}")

def stop(self):
"""Stop heartbeat thread"""
self.running = False

8.2 Graceful Shutdown / Seat Release 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Purpose: Release seat when user exits CODITECT normally

Release Endpoint:

# POST /api/v1/licenses/release
# Request:
{
"session_id": "session_abc123",
"license_key": "CODITECT-A7B2-9C4D-E6F8-1G3H-5J7K"
}

# Response:
{
"status": "released",
"message": "License seat released successfully"
}

Implementation (Django):

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def release_license(request):
"""Release a license seat"""

session_id = request.data.get('session_id')
license_key = request.data.get('license_key')

# Get session
try:
session = LicenseSession.objects.select_related('license').get(
session_id=session_id,
license__license_key=license_key,
license__user_id=request.user.id
)
except LicenseSession.DoesNotExist:
return Response({'error': 'session_not_found'}, status=404)

# Decrement seat count in Redis atomically
redis_client = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT)

lua_script = """
local key = KEYS[1]
local current = redis.call('GET', key)
if current and tonumber(current) > 0 then
return redis.call('DECR', key)
end
return 0
"""

redis_client.eval(lua_script, 1, f'seats:{session.license.license_id}')

# Delete session from database
session.delete()

return Response({
'status': 'released',
'message': 'License seat released successfully'
}, status=200)

Client Implementation:

# coditect/license/client.py (continued)

import atexit
import signal

class LicenseClient:
def __init__(self, ...):
...
# Register cleanup handlers
atexit.register(self.cleanup)
signal.signal(signal.SIGINT, self._signal_handler)
signal.signal(signal.SIGTERM, self._signal_handler)

def cleanup(self):
"""Release license seat on exit"""
if self.session_id:
try:
self.release_license()
print("License seat released.")
except Exception as e:
logger.error(f"Error releasing license: {str(e)}")

def _signal_handler(self, signum, frame):
"""Handle termination signals"""
self.cleanup()
exit(0)

def release_license(self):
"""Explicitly release license seat"""
if not self.session_id:
return

response = requests.post(
f'{self.api_url}/api/v1/licenses/release',
json={
'session_id': self.session_id,
'license_key': self.license_key
},
headers={'Authorization': f'Bearer {self._get_auth_token()}'}
)

if response.status_code == 200:
self.session_id = None
self.signed_token = None

Step 9: Monthly Subscription Renewal

Current State: ❌ Missing (0%)

What We Have:

  • Nothing related to subscription renewal

What We Need:

9.1 Automatic Renewal via Stripe 🔴

Status: ❌ Missing Estimated Time: Already handled by Stripe + Webhooks Priority: P0 (Critical)

How It Works:

  1. Stripe automatically charges customer on renewal date
  2. Stripe sends invoice.payment_succeeded webhook
  3. Backend updates subscription period
  4. License remains active (no action needed)

Already Covered: See Step 3.2 - Stripe Webhooks

9.2 Payment Failure Handling 🔴

Status: ❌ Missing Estimated Time: 1 day Priority: P0 (Critical)

Grace Period Flow:

Day 0: Payment fails

Stripe sends invoice.payment_failed webhook

Backend: Mark subscription as past_due
Backend: Set license status to 'suspended'
Backend: Set grace_period_ends_at = now + 7 days

Email user: "Payment failed, please update payment method"

Day 1-6: Grace period

License still works (with warning)
CODITECT shows: "⚠️ Payment failed. Update payment by Dec 8 to continue."

Day 7: Grace period ends

Backend: Set license status to 'inactive'

CODITECT blocks usage: "License inactive. Update payment to continue."

License Validation with Grace Period:

def acquire_license(request):
...

# Check license status with grace period
if license.status == 'suspended':
grace_period_remaining = (license.grace_period_ends_at - timezone.now()).days

if grace_period_remaining > 0:
# Allow usage with warning
return Response({
'session_id': ...,
'signed_token': ...,
'warning': 'payment_failed',
'warning_message': f'Payment failed. Update payment method within {grace_period_remaining} days.',
'grace_period_ends': license.grace_period_ends_at,
'update_payment_url': 'https://app.coditect.ai/billing'
}, status=200)
else:
# Grace period expired
license.status = 'inactive'
license.save()

return Response({
'error': 'license_inactive',
'message': 'Your subscription is inactive. Please update your payment method.',
'update_payment_url': 'https://app.coditect.ai/billing'
}, status=402)

9.3 Customer Billing Portal 🟡

Status: ❌ Missing Estimated Time: 1 day (Stripe provides this!) Priority: P1 (Important)

Stripe Customer Portal:

  • Stripe provides pre-built billing portal
  • Customers can:
    • Update payment method
    • View invoices
    • Cancel subscription
    • Change plan

Implementation:

# POST /api/v1/billing/portal
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_billing_portal_session(request):
"""Create Stripe billing portal session"""

subscription = Subscription.objects.get(user_id=request.user.id)

session = stripe.billing_portal.Session.create(
customer=subscription.stripe_customer_id,
return_url=f'{settings.FRONTEND_URL}/dashboard'
)

return Response({
'portal_url': session.url
})

Usage:

User clicks "Manage Billing" in dashboard

Frontend calls /api/v1/billing/portal

Redirect to Stripe portal

User updates payment method

Stripe automatically retries failed payment

Webhook: invoice.payment_succeeded

License reactivated automatically

📊 Complete Feature Matrix

What We Have vs What We Need

StepFeatureStatusEst. TimePriority
1. User Registration
Frontend form❌ Missing2 daysP0
Backend API❌ Missing2 daysP0
Email verification❌ Missing1 dayP0
Firebase OAuth⏳ Partial1 dayP0
2. Plan Selection
Plan definitions❌ Missing1 dayP0
Selection UI❌ Missing2 daysP0
Selection API❌ Missing1 dayP0
3. Payment
Stripe integration❌ Missing3 daysP0
Webhook handling❌ Missing2 daysP0
Success/failure flows❌ Missing1 dayP0
4. License Generation
Key generation❌ Missing1 dayP0
Email delivery❌ Missing1 dayP0
5. Download/Install
PyPI package⏳ Partial2 daysP1
Installation wizard❌ Missing2 daysP2
6. Activation
CLI command❌ Missing2 daysP0
Hardware fingerprinting❌ Missing1 dayP0
7. Validation
Acquire endpoint⏳ Partial2 daysP0
Client SDK❌ Missing2 daysP0
8. Running
Heartbeat endpoint❌ Missing2 daysP0
Heartbeat thread❌ Missing1 dayP0
Graceful release❌ Missing1 dayP0
9. Renewal
Auto-renewal⏳ Via Stripe0 daysP0
Payment failure❌ Missing1 dayP0
Billing portal❌ Missing1 dayP1

Total Estimated Time: 35-40 days of focused development


🎯 Critical Path to Commercial Launch

Phase 1: Core Commerce (10 days) 🔴

Week 1:

  • Day 1-2: User registration (frontend + backend)
  • Day 3: Email verification
  • Day 4: Firebase OAuth integration
  • Day 5: Subscription plan definitions

Week 2:

  • Day 6-7: Stripe integration
  • Day 8-9: Webhook handling (all events)
  • Day 10: License generation + email delivery

Deliverable: Users can sign up and purchase subscriptions

Phase 2: License Validation (8 days) 🔴

Week 3:

  • Day 11-12: License acquisition endpoint
  • Day 13-14: License client SDK
  • Day 15: Hardware fingerprinting
  • Day 16-17: Heartbeat mechanism
  • Day 18: Graceful release

Deliverable: CODITECT validates licenses with cloud

Phase 3: Subscription Management (4 days) 🟡

Week 4:

  • Day 19: Payment failure handling
  • Day 20: Grace period logic
  • Day 21: Billing portal integration
  • Day 22: Subscription cancellation flow

Deliverable: Complete subscription lifecycle

Phase 4: Production Hardening (8 days) 🟡

Week 5:

  • Day 23-24: Cloud KMS license signing
  • Day 25-26: SSL/TLS configuration
  • Day 27-28: Monitoring & alerting
  • Day 29-30: Production deployment

Deliverable: Production-ready system

Total: 30 days (6 weeks)


💰 Additional Costs

New Services Required

ServicePurposeMonthly Cost
SendGridEmail delivery (10K emails/mo)$15
StripePayment processing2.9% + $0.30/transaction
Domain + SSLCustom domain with HTTPS$20
Frontend HostingGKE Ingress + CDN$30
Total New Costs~$65 + transaction fees

Combined with Infrastructure: $60 (staging) + $65 = $125/month for complete staging environment

Production: $500 (infra) + $100 (services) = $600/month

Revenue Projections

Break-even Analysis:

Assuming $29/month Starter plan (most popular):

  • Monthly costs: $125 (staging) + $600 (production) = $725
  • Break-even: 25 customers (25 × $29 = $725)
  • Stripe fees: ~$20 (25 × 2.9% × $29)
  • Net break-even: ~27 customers

Conservative Growth:

  • Month 1: 10 customers = $290 revenue (−$435 loss)
  • Month 3: 30 customers = $870 revenue (+$145 profit)
  • Month 6: 100 customers = $2,900 revenue (+$2,175 profit)
  • Month 12: 300 customers = $8,700 revenue (+$8,000 profit)

📋 Launch Checklist

MVP Requirements (Must Have)

  • User registration working
  • Email verification working
  • Stripe payment integration complete
  • License generation functional
  • License validation API operational
  • Client SDK in coditect-core
  • Heartbeat mechanism working
  • Subscription renewal automated
  • Payment failure handling implemented
  • Basic documentation (getting started guide)

Nice to Have (Can Launch Without)

  • Social login (Google/GitHub OAuth)
  • Standalone binary installers
  • Admin dashboard
  • Usage analytics
  • Billing portal (Stripe provides basic one)
  • Advanced monitoring/alerting

Production Readiness

  • SSL/TLS certificates configured
  • Cloud KMS license signing
  • Security audit completed
  • Load testing (100+ concurrent users)
  • Disaster recovery plan documented
  • Terms of Service published
  • Privacy Policy published
  • Refund policy defined

🚀 Recommendation

Immediate Next Steps (This Week)

  1. Fix Firebase Authentication (1 day) - Unblocking P0
  2. Stripe Account Setup (1 day) - Parallel track
  3. User Registration Backend (2 days) - Core commerce
  4. License Generation Logic (1 day) - Quick win

Goal: Have payment processing working by end of week

Short-Term (Next 2 Weeks)

  1. Complete Phase 1: Core Commerce
  2. Start Phase 2: License Validation

Goal: First paying customer can use CODITECT by December 15

Medium-Term (Next 4 Weeks)

  1. Complete Phase 2: License Validation
  2. Complete Phase 3: Subscription Management
  3. Start Phase 4: Production Hardening

Goal: Production launch December 27, 2025


✅ Conclusion

Current Reality

We have 75% of the infrastructure but 0% of the commercial flow. The infrastructure is solid, but we cannot accept payments or issue licenses yet.

What's Missing for Revenue

Critical (P0) - 22 days:

  • User registration (6 days)
  • Payment processing (6 days)
  • License validation (10 days)

Total to First Dollar: ~22 days of focused development

Path Forward

Option 1: MVP Launch (30 days)

  • Build only P0 features
  • Accept first paying customers
  • Iterate based on feedback
  • Revenue Start: January 1, 2026

Option 2: Polished Launch (45 days)

  • Include P1 features (nice to have)
  • More refined user experience
  • Better monitoring/observability
  • Revenue Start: January 15, 2026

Recommendation: Option 1 (MVP Launch) - Get to revenue faster, iterate based on real customer feedback.


Document Created: December 1, 2025, 5:00 AM EST Next Review: December 8, 2025 (after Phase 1 progress) Target MVP Launch: January 1, 2026

Created by: Claude Code (Anthropic AI) For: Hal Casteel, Founder/CEO/CTO, AZ1.AI INC Repository: coditect-cloud-backend