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:
- Separate Pricing Models - Builders pay per-seat, runtime pays per-usage or royalty
- License Enforcement - Prevent builder licenses from being used for runtime (and vice versa)
- Fair Pricing - Don't charge developers twice (builder + runtime for same project)
- Scalability - Runtime model must scale with end-user adoption
- Compliance Tracking - Audit trail for builder vs. runtime usage
Competitive Landscape
Commercial Licensing Models:
| Vendor | Builder Model | Runtime Model |
|---|---|---|
| Qt | Per-developer ($5,950/year) | Royalty: 3-5% of revenue OR runtime license ($500-2000/device) |
| Flexera | Per-developer ($1,200/year) | Runtime: Per-activation ($5-50/activation) |
| Unity | Free for <$100K revenue | Runtime: 2.5% revenue OR $0.20/install |
| Electron | Open-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):
| Tier | Price/Month | Seats | Offline Grace | Features |
|---|---|---|---|---|
| Free | $0 | 1 | 24h | Basic agents, 5 commands |
| Pro | $58 | 1 | 72h | All agents, unlimited commands |
| Team | $290 (5 seats) | 5 | 48h | Shared seats, team collaboration |
| Enterprise | Custom | Custom | 168h | SLA, dedicated support |
Runtime Pricing (Phase 3 - Proposed):
| Model | Pricing | Use Case |
|---|---|---|
| Per-Activation | $5-10/activation | Low-volume SaaS apps |
| Monthly Active Users | $0.50/MAU | High-volume consumer apps |
| Revenue Share | 3-5% of app revenue | Indie developers, startups |
| Flat Runtime License | $500-2000/year | Enterprise 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;
Related ADRs
- 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
- Qt Licensing Model - Dual model inspiration
- Unity Licensing - Revenue share model
- Flexera Licensing - Runtime licensing patterns
- Stripe Usage-Based Billing - Metering infrastructure
- license-server-sdd.md - Part 1: Section 1.3 (Builder vs. Runtime Models)
Last Updated: 2025-11-30 Owner: Architecture Team, Product Team, Finance Team Review Cycle: Quarterly or on pricing model changes