Skip to main content

ADR-005: Builder vs. Runtime Licensing Model

Status: Accepted Date: 2025-11-30 Deciders: Architecture Team, Product Team, Finance Team Tags: licensing, pricing, usage-model, embedded-licensing


Context

CODITECT-CORE has two distinct usage patterns that require different licensing approaches:

Usage Pattern 1: Builder (Development Tool)

Scenario: Developer uses CODITECT framework to build applications

Developer installs CODITECT-CORE:
└─ Uses agents, commands, skills to develop applications
└─ Example: Building a CICD pipeline, creating API documentation
└─ Pricing: Per-developer seat (monthly/annual subscription)

Key Characteristics:

  • User: Human developer
  • Duration: Long-lived sessions (8+ hours of active development)
  • Frequency: Daily usage during working hours
  • Value: Productivity tool (builds applications faster)

Usage Pattern 2: Runtime (Embedded in Applications)

Scenario: Developer embeds CODITECT-CORE in their end-user applications

Application built with CODITECT:
└─ Embeds .coditect/ directory and license SDK
└─ End-users run the application (CODITECT runs automatically)
└─ Example: AI-powered SaaS dashboard, automated report generator
└─ Pricing: Royalty-based or runtime license fees

Key Characteristics:

  • User: End-user (not developer)
  • Duration: Short-lived sessions (minutes to hours)
  • Frequency: Varies by application usage
  • Value: Application functionality (part of delivered product)

Business Challenge

Current State (Phase 0):

  • Only "builder" usage implemented (developer floating licenses)
  • No runtime licensing model defined
  • Risk: Developers could embed CODITECT in products without runtime licenses

Requirements:

  1. Separate Pricing Models - Builders pay per-seat, runtime pays per-usage or royalty
  2. License Enforcement - Prevent builder licenses from being used for runtime (and vice versa)
  3. Fair Pricing - Don't charge developers twice (builder + runtime for same project)
  4. Scalability - Runtime model must scale with end-user adoption
  5. Compliance Tracking - Audit trail for builder vs. runtime usage

Competitive Landscape

Commercial Licensing Models:

VendorBuilder ModelRuntime Model
QtPer-developer ($5,950/year)Royalty: 3-5% of revenue OR runtime license ($500-2000/device)
FlexeraPer-developer ($1,200/year)Runtime: Per-activation ($5-50/activation)
UnityFree for <$100K revenueRuntime: 2.5% revenue OR $0.20/install
ElectronOpen-source (builder)Free runtime (monetize via support/services)

CODITECT Positioning:

  • Builder: $58/month/developer (competitive with developer tools)
  • Runtime: TBD (Phase 3 implementation)

Technical Constraints

  • CODITECT installed as git submodule (.coditect/ directory)
  • License SDK embedded in applications
  • No central server in end-user applications (local-first architecture)
  • Must distinguish builder vs. runtime usage programmatically
  • Offline grace periods required (24-168 hours)

Decision

We will implement a usage_type field in session metadata to distinguish builder vs. runtime usage, with separate license pools and pricing models.

Session Metadata Structure

Every license session includes:

session_metadata = {
'session_id': 'sha256(...)',
'user_email': 'dev@example.com',
'hardware_id': 'hw-fingerprint',
'project_root': '/Users/dev/my-project',
'coditect_path': '/Users/dev/my-project/.coditect',
'coditect_version': '1.0.0',
'usage_type': 'builder', # 'builder' OR 'runtime'
'tier': 'pro', # 'free', 'pro', 'team', 'enterprise'
'created_at': '2025-11-30T10:00:00Z'
}

Usage Type Detection

Automatic Detection:

def detect_usage_type():
"""
Detect if CODITECT is being used for development (builder)
or embedded in an application (runtime).

Returns:
str: 'builder' or 'runtime'
"""

# Detection heuristic #1: Check for interactive shell
if is_interactive_session():
# Developer in terminal/IDE = builder
return 'builder'

# Detection heuristic #2: Check for CI/CD environment
if is_ci_cd_environment():
# Automated build pipeline = builder
return 'builder'

# Detection heuristic #3: Check for end-user application markers
if has_runtime_marker():
# Application with runtime marker = runtime
return 'runtime'

# Default: Builder (conservative default for Phase 1)
return 'builder'


def is_interactive_session():
"""Check if running in interactive shell."""
import sys
return sys.stdin.isatty() or os.getenv('TERM') is not None


def is_ci_cd_environment():
"""Check for CI/CD environment variables."""
ci_indicators = [
'CI', # Generic CI indicator
'JENKINS_HOME', # Jenkins
'TRAVIS', # Travis CI
'CIRCLECI', # Circle CI
'GITHUB_ACTIONS', # GitHub Actions
'GITLAB_CI', # GitLab CI
]
return any(os.getenv(var) for var in ci_indicators)


def has_runtime_marker():
"""
Check for explicit runtime marker file.

Applications embedding CODITECT can create:
.coditect/.RUNTIME_LICENSE

Contents:
{
"license_type": "runtime",
"application_name": "My SaaS App",
"application_version": "2.1.0",
"developer_license_key": "LICENSE-KEY",
"runtime_license_key": "RUNTIME-KEY"
}
"""
marker_file = Path('.coditect/.RUNTIME_LICENSE')
return marker_file.exists()

License Pool Separation

Builder License Pool:

# backend/licenses/models.py

class License(models.Model):
license_key = models.CharField(max_length=64, unique=True)
license_type = models.CharField(
max_length=20,
choices=[
('builder', 'Builder (Development)'),
('runtime', 'Runtime (Embedded)'),
('hybrid', 'Hybrid (Builder + Runtime)')
],
default='builder'
)

# Builder-specific fields
seats_total = models.IntegerField(default=1) # Concurrent seats
seats_used = models.IntegerField(default=0) # Current usage

# Runtime-specific fields (Phase 3)
runtime_activations_total = models.IntegerField(null=True, blank=True)
runtime_activations_used = models.IntegerField(default=0)
revenue_share_percent = models.DecimalField(
max_digits=5,
decimal_places=2,
null=True,
blank=True
) # e.g., 3.00 = 3%

# Common fields
tier = models.CharField(max_length=20) # 'free', 'pro', 'team', 'enterprise'
status = models.CharField(max_length=20, default='active')
expires_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)

API Endpoint Differentiation:

# backend/licenses/views.py

class AcquireBuilderLicenseView(APIView):
"""Acquire builder license seat (floating)."""

def post(self, request):
# Verify usage_type == 'builder'
if request.data.get('usage_type') != 'builder':
return Response(
{'error': 'Invalid usage type for builder endpoint'},
status=status.HTTP_400_BAD_REQUEST
)

# Check builder license pool
license_obj = License.objects.get(
license_key=request.data['license_key'],
license_type__in=['builder', 'hybrid']
)

# Atomic seat acquisition (Redis Lua)
# ... (same as existing floating license logic)


class AcquireRuntimeLicenseView(APIView):
"""Acquire runtime license (Phase 3 - not yet implemented)."""

def post(self, request):
# Verify usage_type == 'runtime'
if request.data.get('usage_type') != 'runtime':
return Response(
{'error': 'Invalid usage type for runtime endpoint'},
status=status.HTTP_400_BAD_REQUEST
)

# Check runtime license terms
license_obj = License.objects.get(
license_key=request.data['runtime_license_key'],
license_type__in=['runtime', 'hybrid']
)

# Runtime licensing logic:
# - Option 1: Per-activation metering
# - Option 2: Revenue share tracking
# - Option 3: Time-based runtime license

# TODO: Phase 3 implementation
pass

Pricing Models

Builder Pricing (Phase 1 - Implemented):

TierPrice/MonthSeatsOffline GraceFeatures
Free$0124hBasic agents, 5 commands
Pro$58172hAll agents, unlimited commands
Team$290 (5 seats)548hShared seats, team collaboration
EnterpriseCustomCustom168hSLA, dedicated support

Runtime Pricing (Phase 3 - Proposed):

ModelPricingUse Case
Per-Activation$5-10/activationLow-volume SaaS apps
Monthly Active Users$0.50/MAUHigh-volume consumer apps
Revenue Share3-5% of app revenueIndie developers, startups
Flat Runtime License$500-2000/yearEnterprise embedded apps

Hybrid Licensing (Bundle Builder + Runtime):

  • Developer + 100 runtime activations: $100/month
  • Developer + 1000 runtime activations: $300/month
  • Developer + unlimited runtime: $1000/month

Enforcement Logic

Phase 1 (Current): Builder Only

# .coditect/sdk/license_client.py

def acquire_license():
"""Acquire license (builder usage only for Phase 1)."""

# Detect usage type
usage_type = detect_usage_type()

if usage_type == 'runtime':
# Phase 3: Runtime licensing not yet implemented
print("⚠️ Runtime licensing coming in Q2 2026")
print(" For now, use builder license for development")
print(" Contact sales@coditect.ai for runtime licensing")

# Fallback: Allow builder license for now (grace period)
usage_type = 'builder'

# Acquire builder license
response = requests.post(
f"{api_url}/v1/licenses/builder/acquire",
json={
'license_key': get_license_key(),
'session_id': generate_session_id(),
'usage_type': usage_type, # Always 'builder' for Phase 1
'user_email': get_user_email(),
'hardware_id': get_hardware_id(),
'project_root': get_project_root()
}
)

# ... handle response

Phase 3 (Future): Builder + Runtime

def acquire_license():
"""Acquire license (builder or runtime)."""

usage_type = detect_usage_type()

if usage_type == 'builder':
# Developer usage
endpoint = '/v1/licenses/builder/acquire'
license_key = get_builder_license_key()

elif usage_type == 'runtime':
# Runtime usage (embedded app)
endpoint = '/v1/licenses/runtime/acquire'
license_key = get_runtime_license_key()

# Runtime-specific metadata
runtime_metadata = {
'application_name': get_app_name(),
'application_version': get_app_version(),
'end_user_id': get_end_user_id(), # Optional
'deployment_id': get_deployment_id() # For tracking
}
else:
raise ValueError(f"Unknown usage type: {usage_type}")

response = requests.post(
f"{api_url}{endpoint}",
json={
'license_key': license_key,
'session_id': generate_session_id(),
'usage_type': usage_type,
**runtime_metadata if usage_type == 'runtime' else {}
}
)

# ... handle response

Migration Path (Builder → Runtime)

Scenario: Developer builds app with CODITECT, wants to deploy to production

Step 1: Development (Builder License)

# Developer uses builder license
CODITECT_LICENSE_KEY=BUILDER-ABC-123 docker-compose up
# Pays: $58/month (Pro tier)

Step 2: Pre-Production Testing

# Still using builder license (development environment)
CODITECT_LICENSE_KEY=BUILDER-ABC-123 pytest
# No additional fees (included in builder license)

Step 3: Production Deployment (Runtime License Required)

# docker-compose.prod.yml
services:
app:
image: my-app:latest
environment:
# Runtime license for production
CODITECT_RUNTIME_LICENSE_KEY: RUNTIME-XYZ-789

# Mark as runtime usage
CODITECT_USAGE_TYPE: runtime
# Deploy to production
docker-compose -f docker-compose.prod.yml up -d

# Runtime license activated:
# - Metering: 500 activations/month → $2,500/month (at $5/activation)
# - OR revenue share: App revenue $50K/month → $1,500/month (at 3%)
# - OR flat license: $500/month (unlimited activations)

Step 4: Upgrade to Hybrid License (Bundle Builder + Runtime)

# Developer purchases hybrid license
# Pays: $300/month (includes builder seat + 1000 runtime activations)

# Development:
CODITECT_LICENSE_KEY=HYBRID-DEF-456 docker-compose up

# Production:
CODITECT_RUNTIME_LICENSE_KEY=HYBRID-DEF-456 docker-compose -f prod.yml up

Consequences

Positive

Flexible Pricing Models

  • Developers pay for development tools (builder)
  • End-users pay for application usage (runtime)
  • Hybrid option reduces complexity for bundled use cases

Scalable Revenue

  • Runtime licensing scales with application adoption
  • Revenue share aligns CODITECT success with customer success
  • Per-activation model works for low-volume apps

Fair Usage Enforcement

  • Builder licenses can't be abused for runtime (API enforcement)
  • Runtime detection prevents accidental misuse
  • Grace period during Phase 1 (runtime not yet implemented)

Clear Developer Experience

  • Explicit usage_type removes ambiguity
  • Automatic detection reduces manual configuration
  • Runtime marker file allows explicit override

Competitive Positioning

  • Builder pricing competitive with developer tools ($58/month)
  • Runtime pricing flexible (per-activation, revenue share, or flat)
  • Hybrid bundles provide cost savings for power users

Audit Trail

  • Every session logged with usage_type
  • Analytics track builder vs. runtime adoption
  • Compliance reports for license audits

Negative

⚠️ Phase 3 Dependency

  • Runtime licensing not implemented in Phase 1
  • Developers can embed CODITECT without runtime enforcement (temporary)
  • Mitigation: Grace period + sales team outreach for runtime licenses

⚠️ Detection Complexity

  • Automatic usage_type detection has edge cases
  • False positives: Developer in headless environment detected as runtime
  • Mitigation: Explicit runtime marker file (.RUNTIME_LICENSE)

⚠️ Pricing Uncertainty for Developers

  • Runtime pricing model not finalized (Phase 3)
  • Developers hesitant to embed without knowing costs
  • Mitigation: Publish pricing calculator on website

⚠️ License Key Management Burden

  • Hybrid licenses require managing 2 keys (builder + runtime)
  • OR single hybrid key (simpler but less secure)
  • Mitigation: Dashboard UI for key management

⚠️ Metering Infrastructure Required

  • Runtime licensing needs activation tracking
  • Revenue share needs integration with customer billing
  • Phase 3 implementation complexity

Neutral

🔄 Dual License Model

  • Follows industry standard (Qt, Unity, Flexera)
  • Familiar to developers accustomed to dual licensing
  • Not a new concept (low adoption friction)

🔄 API Complexity

  • Separate endpoints for builder vs. runtime
  • Increases API surface area (more code to maintain)
  • Acceptable: Clear separation of concerns

Alternatives Considered

Alternative 1: Single License for Both Builder and Runtime

Implementation:

# No distinction between builder and runtime
# Same license works for both

license_key = "SINGLE-LICENSE-KEY"
# Use for development AND production

Pros:

  • ✅ Simplest for developers (one license key)
  • ✅ No usage detection complexity

Cons:

  • ❌ Can't price differently for builder vs. runtime
  • ❌ Revenue doesn't scale with application adoption
  • ❌ Unfair to builders who don't embed (pay same price)

Rejected Because: Limits revenue scalability and pricing flexibility.

Alternative 2: Open-Source Builder, Paid Runtime

Implementation:

# Builder: Free and open-source (MIT license)
# Runtime: Proprietary license with fees

Pros:

  • ✅ Drives adoption (free for developers)
  • ✅ Revenue from successful applications only
  • ✅ Aligns with freemium SaaS model

Cons:

  • ❌ No revenue from developer tools (delays profitability)
  • ❌ Risk: Developers fork and remove runtime licensing
  • ❌ Support burden without revenue

Rejected Because: Developer tool revenue critical for Phase 1 sustainability.

Alternative 3: Revenue Share Only (No Per-Seat Builder Pricing)

Implementation:

# No upfront fees
# Only pay when application generates revenue
# E.g., 5% of application revenue

Pros:

  • ✅ Zero upfront cost (attracts developers)
  • ✅ Revenue scales with customer success
  • ✅ Simple pricing model

Cons:

  • ❌ Revenue delayed until applications launch
  • ❌ Difficult to track (requires customer revenue integration)
  • ❌ Abuse risk (developers underreport revenue)

Rejected Because: Cash flow too uncertain for early-stage startup.

Alternative 4: Embedded License Keys in Builds

Implementation:

# Embed runtime license key in application build
# License validation happens at compile time (not runtime)

Pros:

  • ✅ No runtime license server calls (faster startup)
  • ✅ Works offline always

Cons:

  • ❌ License keys visible in compiled code (security risk)
  • ❌ Can't revoke licenses remotely
  • ❌ Hard to meter actual usage

Rejected Because: Security risk and no usage metering.


Implementation Notes

Phase Rollout Plan

Phase 1 (Current - MVP):

  • ✅ Builder licensing fully implemented
  • ✅ Floating seats with Redis atomic operations
  • ⏸️ Runtime licensing: Grace period (allow builder licenses temporarily)

Phase 2 (Q1 2026):

  • Finalize runtime pricing model (per-activation vs. revenue share)
  • Implement runtime license API endpoints
  • Add metering infrastructure (track activations)

Phase 3 (Q2 2026):

  • Launch runtime licensing publicly
  • Integrate with Stripe for automated billing
  • Create hybrid license bundles

Phase 4 (Q3 2026):

  • Advanced metering (usage-based, feature gates)
  • Revenue share tracking and invoicing
  • Enterprise runtime contracts

Runtime License Key Distribution

Problem: How do developers get runtime license keys?

Solution: Automated provisioning via dashboard.

Developer Workflow:
1. Developer purchases runtime license (or hybrid)
2. Dashboard generates runtime license key
3. Developer downloads .RUNTIME_LICENSE file
4. Developer includes .RUNTIME_LICENSE in application build

File contents:
{
"license_type": "runtime",
"runtime_license_key": "RUNTIME-XYZ-789",
"activations_limit": 1000,
"billing_model": "per-activation"
}

Metering and Analytics

Track Usage Metrics:

# backend/licenses/analytics.py

class LicenseUsageMetrics:
"""Track builder vs. runtime usage."""

@staticmethod
def record_session(session_metadata):
"""Record license session for analytics."""

usage_type = session_metadata['usage_type']

if usage_type == 'builder':
# Track developer sessions
metric = 'builder_sessions_total'
labels = {
'tier': session_metadata['tier'],
'user_email': session_metadata['user_email']
}

elif usage_type == 'runtime':
# Track runtime activations
metric = 'runtime_activations_total'
labels = {
'application_name': session_metadata.get('application_name'),
'deployment_id': session_metadata.get('deployment_id')
}

# Send to Prometheus
prometheus_client.Counter(metric, labels).inc()

# Send to BigQuery for analytics
bigquery_client.insert_rows('license_sessions', [session_metadata])

Dashboard Queries:

-- Builder usage by tier
SELECT tier, COUNT(*) as sessions
FROM license_sessions
WHERE usage_type = 'builder'
AND created_at > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY tier;

-- Runtime activations by application
SELECT application_name, COUNT(*) as activations
FROM license_sessions
WHERE usage_type = 'runtime'
AND created_at > DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY application_name
ORDER BY activations DESC;

  • ADR-001: Floating Licenses vs. Node-Locked Licenses (builder seat management)
  • ADR-002: Redis Lua Scripts for Atomic Operations (seat acquisition for builders)
  • ADR-003: Check-on-Init Enforcement Pattern (when usage_type is detected)
  • ADR-004: Symlink Resolution Strategy (session_id generation includes usage_type)
  • ADR-015: Usage-Based Metering (Phase 3 - runtime metering implementation)

References


Last Updated: 2025-11-30 Owner: Architecture Team, Product Team, Finance Team Review Cycle: Quarterly or on pricing model changes