Skip to main content

ADR-067: Time-Controlled Licensing System

Status

Accepted | January 12, 2026

Context

CODITECT is transitioning from an internal development framework to a commercial product requiring formal licensing:

  1. Pilot Users - 90-day trial access for early adopters and evaluation
  2. Subscription Users - Monthly/annual paid licenses
  3. Enterprise Customers - Multi-seat licenses with centralized management

Current State

CODITECT already has foundational components for licensing:

ComponentLocationPurpose
machine-id.json~/.coditect/machine-id.jsonHardware-based UUID for device binding
/orient commandSession startIdeal validation checkpoint
api.coditect.aiCloud APILicense server endpoint
ADR-053Cloud syncTenant/user association
ADR-003License validationHybrid online/offline strategy
ADR-055Container licensingDocker/Workstation patterns

Requirements

  1. Security - Prevent unauthorized use while avoiding false positives
  2. Usability - Support offline work (airplane, poor connectivity)
  3. Flexibility - Different tiers with varying restrictions
  4. Manageability - Enterprise admin visibility and control
  5. Graceful Degradation - Clear messaging when license expires

Decision

Implement a hybrid local+server licensing system with cryptographically signed license files, device binding, and configurable grace periods.

Architecture Overview

┌─────────────────────────────────────────────────────────────────────────────┐
│ CODITECT TIME-CONTROLLED LICENSING │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ LOCAL INSTALLATION │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ~/.coditect/ │ │
│ │ ├── machine-id.json # Hardware UUID (existing) │ │
│ │ ├── license/ # NEW: License directory │ │
│ │ │ ├── license.json # Signed license file │ │
│ │ │ ├── validation-cache.json # Server validation cache │ │
│ │ │ └── offline-log.json # Offline period tracking │ │
│ │ └── config/ │ │
│ │ └── licensing.json # License configuration │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ VALIDATION FLOW │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ /orient (Session Start) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Load Local │ │ │
│ │ │ License File │ │ │
│ │ └────────┬────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Verify Signature│ ← Ed25519 public key (embedded) │ │
│ │ └────────┬────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Check Machine │ ← Compare with machine-id.json │ │
│ │ │ Binding │ │ │
│ │ └────────┬────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Check │ │ │
│ │ │ Expiration │ │ │
│ │ └────────┬────────┘ │ │
│ │ │ │ │
│ │ ┌────┴────┐ │ │
│ │ │ │ │ │
│ │ Valid Expired │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌───────┐ ┌───────────┐ │ │
│ │ │Server │ │ Grace │ │ │
│ │ │Check │ │ Period? │ │ │
│ │ └───┬───┘ └─────┬─────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ Continue Degrade/Block │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ CLOUD API (api.coditect.ai) │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ POST /api/v1/licenses/validate │ │
│ │ → Validate license key against tenant/user │ │
│ │ → Check seat availability │ │
│ │ → Return signed license payload │ │
│ │ │ │
│ │ POST /api/v1/licenses/refresh │ │
│ │ → Refresh existing license │ │
│ │ → Extend expiration (subscription renewals) │ │
│ │ → Return updated signed license │ │
│ │ │ │
│ │ POST /api/v1/licenses/revoke │ │
│ │ → Revoke license (admin action) │ │
│ │ → Add to revocation list │ │
│ │ │ │
│ │ GET /api/v1/licenses/revocations │ │
│ │ → Return revocation list (CRL-like) │ │
│ │ → Used for offline revocation checking │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

License File Format

File: ~/.coditect/license/license.json

{
"version": "1.0",
"license_id": "lic_01HXYZ789ABC123DEF456GHI",
"license_key": "CODITECT-PILOT-XXXX-XXXX-XXXX",

"licensee": {
"tenant_id": "tenant_01ABC123",
"user_id": "user_01DEF456",
"email": "user@company.com",
"organization": "Acme Corp"
},

"type": "pilot",
"tier": "pro",

"validity": {
"issued_at": "2026-01-12T00:00:00Z",
"expires_at": "2026-04-12T23:59:59Z",
"grace_period_days": 7
},

"binding": {
"machine_uuid": "d3d3a316-09c6-8f41-4a3f-d93e422d199c",
"hardware_hash": "sha256:d3d3a316...",
"max_devices": 2,
"device_name": "Hals-MacBook-Air-2.local"
},

"features": {
"agents": true,
"commands": true,
"skills": true,
"cloud_sync": true,
"context_watcher": true,
"max_tokens_per_day": null,
"priority_support": false
},

"offline": {
"max_offline_days": 14,
"last_server_check": "2026-01-12T10:30:00Z",
"offline_start": null
},

"signature": {
"algorithm": "Ed25519",
"key_id": "coditect-license-key-2026-01",
"value": "base64-encoded-signature..."
}
}

License Types and Tiers

┌─────────────────────────────────────────────────────────────────────────────┐
│ LICENSE TYPE MATRIX │
├─────────────────┬───────────┬────────────┬────────────┬────────────────────┤
│ Aspect │ Pilot │ Pro │ Team │ Enterprise │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Duration │ 90 days │ Monthly/ │ Annual │ Custom │
│ │ │ Annual │ │ │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Grace Period │ 7 days │ 7 days │ 3 days │ Configurable │
│ (after expiry) │ │ │ │ (0-14 days) │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Offline │ 7 days │ 14 days │ 7 days │ Configurable │
│ Tolerance │ │ │ │ (0-30 days) │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Server Check │ Daily │ Daily │ Daily │ Configurable │
│ Interval │ │ │ │ (hourly-weekly) │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Max Devices │ 1 │ 2 │ Per-seat │ Unlimited │
│ │ │ │ │ (per license) │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Expiration │ Warning │ Warning │ Degraded │ Configurable │
│ Behavior │ → Block │ → Degraded │ → Block │ │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Seat Management │ N/A │ N/A │ Per-team │ Centralized │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Cloud Sync │ Yes │ Yes │ Yes │ Yes + Audit │
├─────────────────┼───────────┼────────────┼────────────┼────────────────────┤
│ Price │ Free │ $29/mo │ $99/mo │ Custom │
│ │ │ $290/yr │ per seat │ │
└─────────────────┴───────────┴────────────┴────────────┴────────────────────┘

Expiration Behavior Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ EXPIRATION BEHAVIOR STATE MACHINE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ LICENSE STATES │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌────────┐ │ │
│ │ │ ACTIVE │────▶│ WARNING │────▶│ GRACE │────▶│ EXPIRED│ │ │
│ │ │ │ │ (7 days) │ │ PERIOD │ │ │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ └────────┘ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ ▼ ▼ ▼ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ BEHAVIOR │ │ │
│ │ ├──────────┬────────────┬───────────────┬────────────────────┤ │ │
│ │ │ ACTIVE │ WARNING │ GRACE │ EXPIRED │ │ │
│ │ ├──────────┼────────────┼───────────────┼────────────────────┤ │ │
│ │ │ Full │ Full │ Degraded │ Blocked or │ │ │
│ │ │ features │ features + │ features │ Free tier │ │ │
│ │ │ │ banner │ (no sync, │ (tier-dependent) │ │ │
│ │ │ │ │ basic agents)│ │ │ │
│ │ └──────────┴────────────┴───────────────┴────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ TRANSITION TRIGGERS │
│ │
│ ACTIVE → WARNING: 7 days before expiration │
│ WARNING → GRACE: License expiration date reached │
│ GRACE → EXPIRED: Grace period exhausted (0-14 days based on tier) │
│ │
│ ANY → ACTIVE: License renewed/extended │
│ ANY → REVOKED: Admin revocation (immediate) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Offline Tolerance System

┌─────────────────────────────────────────────────────────────────────────────┐
│ OFFLINE TOLERANCE FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SESSION START (/orient) │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Try Server │ │
│ │ Validation │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ Success Network │
│ │ Error │
│ │ │ │
│ ▼ ▼ │
│ ┌───────┐ ┌───────────────────────────────────────┐ │
│ │Update │ │ OFFLINE MODE │ │
│ │Cache │ │ │ │
│ │ │ │ 1. Load cached license │ │
│ │ │ │ 2. Check offline duration │ │
│ │ │ │ 3. Verify not revoked (cached CRL) │ │
│ │ │ │ 4. Log offline period start │ │
│ └───┬───┘ │ │ │
│ │ │ ┌────────────────────────────┐ │ │
│ │ │ │ Offline Duration Check │ │ │
│ │ │ └────────────┬───────────────┘ │ │
│ │ │ │ │ │
│ │ │ ┌─────────┴─────────┐ │ │
│ │ │ │ │ │ │
│ │ │ Within Limit Exceeded │ │
│ │ │ │ │ │ │
│ │ │ ▼ ▼ │ │
│ │ │ ┌─────────┐ ┌─────────┐ │ │
│ │ │ │ ALLOW │ │ DEGRADE │ │ │
│ │ │ │ (banner)│ │ or BLOCK│ │ │
│ │ │ └─────────┘ └─────────┘ │ │
│ │ │ │ │
│ │ └───────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Continue Session │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ OFFLINE TRACKING FILE: ~/.coditect/license/offline-log.json │
│ │
│ { │
│ "offline_periods": [ │
│ { │
│ "started_at": "2026-01-10T08:00:00Z", │
│ "ended_at": "2026-01-11T14:30:00Z", │
│ "duration_hours": 30.5, │
│ "reason": "network_unavailable" │
│ } │
│ ], │
│ "current_offline_start": null, │
│ "total_offline_hours_30d": 42.5, │
│ "offline_abuse_flag": false │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Device Binding Using machine-id.json

The existing machine-id.json provides hardware-based device identification:

┌─────────────────────────────────────────────────────────────────────────────┐
│ DEVICE BINDING FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ FIRST ACTIVATION (New Device) │
│ │
│ 1. User enters license key │
│ │ │
│ ▼ │
│ 2. Read machine-id.json │
│ { │
│ "machine_uuid": "d3d3a316-09c6-8f41-4a3f-d93e422d199c", │
│ "hardware_hash": "sha256:d3d3a316...", │
│ "hostname": "Hals-MacBook-Air-2.local" │
│ } │
│ │ │
│ ▼ │
│ 3. POST /api/v1/licenses/activate │
│ { │
│ "license_key": "CODITECT-PILOT-XXXX-XXXX-XXXX", │
│ "machine_uuid": "d3d3a316-09c6-8f41-4a3f-d93e422d199c", │
│ "hardware_hash": "sha256:d3d3a316...", │
│ "device_name": "Hals-MacBook-Air-2.local" │
│ } │
│ │ │
│ ▼ │
│ 4. Server checks: │
│ - License key valid? │
│ - Device count within max_devices? │
│ - Not already activated on different device? (if max_devices=1) │
│ │ │
│ ▼ │
│ 5. Server returns signed license bound to machine_uuid │
│ │ │
│ ▼ │
│ 6. Save to ~/.coditect/license/license.json │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ SUBSEQUENT VALIDATION │
│ │
│ 1. Load ~/.coditect/license/license.json │
│ │ │
│ ▼ │
│ 2. Compare license.binding.machine_uuid with machine-id.json │
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ Match Mismatch │
│ │ │ │
│ ▼ ▼ │
│ Continue Block + Message: │
│ "License activated on different device. │
│ Deactivate old device or contact support." │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ DEVICE TRANSFER (User Request) │
│ │
│ 1. User runs: /license deactivate │
│ │ │
│ ▼ │
│ 2. POST /api/v1/licenses/deactivate │
│ - Removes machine_uuid binding │
│ - Frees up device slot │
│ │ │
│ ▼ │
│ 3. User can activate on new device │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Cryptographic Security

┌─────────────────────────────────────────────────────────────────────────────┐
│ LICENSE SIGNING AND VERIFICATION │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SIGNING (Server-Side) │
│ │
│ ┌─────────────────┐ │
│ │ License Payload │ │
│ │ (JSON without │ │
│ │ signature) │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Canonical JSON │ ← Sort keys, no whitespace, UTF-8 │
│ │ Serialize │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Ed25519 Sign │ ← Private key in Cloud KMS (HSM-protected) │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Base64 Encode │ │
│ │ Signature │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ Append to license.signature.value │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ VERIFICATION (Client-Side) │
│ │
│ ┌─────────────────┐ │
│ │ Load License │ │
│ │ File │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Extract │ │
│ │ Signature │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Rebuild │ ← Same canonical serialization │
│ │ Payload │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Ed25519 Verify │ ← Public key embedded in framework │
│ └────────┬────────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ │ │
│ Valid Invalid │
│ │ │ │
│ ▼ ▼ │
│ Continue Block + Message: │
│ "License file has been tampered with. │
│ Please re-activate your license." │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ KEY MANAGEMENT │
│ │
│ Private Key: │
│ - Stored in Google Cloud KMS (HSM-protected) │
│ - Key: projects/coditect-citus-prod/locations/us-central1/ │
│ keyRings/coditect-licensing/cryptoKeys/license-signing-key │
│ - Rotation: Annual (key_id in signature tracks version) │
│ │
│ Public Key: │
│ - Embedded in framework: ~/.coditect/config/license-public-keys.json │
│ - Published: https://coditect.ai/.well-known/license-keys.json │
│ - Multiple keys supported for rotation │
│ │
│ Key File Format: │
│ { │
│ "keys": [ │
│ { │
│ "key_id": "coditect-license-key-2026-01", │
│ "algorithm": "Ed25519", │
│ "public_key": "base64-encoded-public-key...", │
│ "valid_from": "2026-01-01T00:00:00Z", │
│ "valid_until": "2027-12-31T23:59:59Z" │
│ } │
│ ] │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

API Endpoint Specifications

POST /api/v1/licenses/activate

Activate a license key on a new device.

Request:

{
"license_key": "CODITECT-PILOT-XXXX-XXXX-XXXX",
"machine_uuid": "d3d3a316-09c6-8f41-4a3f-d93e422d199c",
"hardware_hash": "sha256:d3d3a316...",
"device_name": "Hals-MacBook-Air-2.local"
}

Response (Success):

{
"success": true,
"license": { /* Full signed license.json content */ },
"message": "License activated successfully"
}

Response (Error - Max Devices):

{
"success": false,
"error": "max_devices_exceeded",
"message": "License already activated on maximum devices (2). Deactivate a device first.",
"activated_devices": [
{"device_name": "Old-MacBook-Pro", "activated_at": "2026-01-01T10:00:00Z"}
]
}

POST /api/v1/licenses/validate

Validate and refresh a license (called periodically).

Request:

{
"license_id": "lic_01HXYZ789ABC123DEF456GHI",
"machine_uuid": "d3d3a316-09c6-8f41-4a3f-d93e422d199c"
}

Response:

{
"valid": true,
"status": "active",
"days_remaining": 45,
"license": { /* Updated signed license if changed */ },
"revocation_list_hash": "sha256:abc123...",
"next_check_recommended": "2026-01-13T10:30:00Z"
}

POST /api/v1/licenses/deactivate

Deactivate license on current device (free up slot).

Request:

{
"license_id": "lic_01HXYZ789ABC123DEF456GHI",
"machine_uuid": "d3d3a316-09c6-8f41-4a3f-d93e422d199c"
}

Response:

{
"success": true,
"message": "Device deactivated. 1 device slot now available.",
"remaining_devices": 1
}

GET /api/v1/licenses/revocations

Get list of revoked licenses (for offline checking).

Response:

{
"updated_at": "2026-01-12T12:00:00Z",
"revocations": [
{
"license_id": "lic_REVOKED123",
"revoked_at": "2026-01-10T08:00:00Z",
"reason": "payment_failed"
}
],
"hash": "sha256:abc123..."
}

Integration with /orient Command

# In commands/orient.py (simplified)

async def validate_license_on_orient():
"""Called at start of /orient command."""

license_manager = LicenseManager()

# Load and verify license
try:
license = license_manager.load_license()
except LicenseNotFoundError:
print("No license found. Run '/license activate XXXX-XXXX-XXXX' to activate.")
return False
except SignatureVerificationError:
print("License file corrupted. Run '/license refresh' to re-download.")
return False

# Check machine binding
machine_id = load_machine_id()
if license.binding.machine_uuid != machine_id.machine_uuid:
print("License not valid for this device.")
return False

# Check expiration
status = license_manager.get_status()

if status == LicenseStatus.EXPIRED:
if license_manager.in_grace_period():
days_remaining = license_manager.grace_days_remaining()
print(f"License expired. Grace period: {days_remaining} days remaining.")
print("Renew at https://coditect.ai/renew")
# Continue with degraded features
else:
print("License expired and grace period exhausted.")
print("Renew at https://coditect.ai/renew")
return False

elif status == LicenseStatus.WARNING:
days_remaining = license_manager.days_until_expiry()
print(f"License expires in {days_remaining} days.")
print("Renew at https://coditect.ai/renew")

# Try server validation (non-blocking if offline)
try:
await license_manager.server_validate()
except NetworkError:
if license_manager.offline_days() > license.offline.max_offline_days:
print(f"Offline limit exceeded ({license.offline.max_offline_days} days).")
print("Please connect to internet to continue.")
return False
else:
offline_days = license_manager.offline_days()
print(f"Offline mode ({offline_days}/{license.offline.max_offline_days} days).")

return True

Renewal Mechanisms

┌─────────────────────────────────────────────────────────────────────────────┐
│ LICENSE RENEWAL FLOWS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ AUTOMATIC RENEWAL (Subscription) │
│ │
│ 1. Stripe webhook: invoice.paid │
│ │ │
│ ▼ │
│ 2. Backend extends license expiration │
│ │ │
│ ▼ │
│ 3. Next /orient → server validation → updated license downloaded │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ MANUAL RENEWAL (Pilot → Paid) │
│ │
│ 1. User visits https://coditect.ai/upgrade │
│ │ │
│ ▼ │
│ 2. Enters payment info → Stripe processes │
│ │ │
│ ▼ │
│ 3. Backend upgrades license tier + extends expiration │
│ │ │
│ ▼ │
│ 4. User runs: /license refresh │
│ │ │
│ ▼ │
│ 5. New signed license downloaded │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ CLI COMMANDS │
│ │
│ /license status Show current license status │
│ /license activate KEY Activate new license key │
│ /license refresh Re-download license from server │
│ /license deactivate Deactivate on this device (free up slot) │
│ /license info Show detailed license information │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Tampering Prevention

Attack VectorMitigation
Edit license.jsonEd25519 signature verification fails
Copy license to other machinemachine_uuid binding mismatch
Clock manipulationServer validation compares server time
Replay old licenseLicense ID + device binding tracked server-side
Man-in-the-middleHTTPS + signature verification
Reverse engineer public keyEd25519 is asymmetric - private key in HSM
Patch framework codeRoot-owned protected installation (ADR-055)

File Locations

FilePurpose
~/.coditect/license/license.jsonSigned license file
~/.coditect/license/validation-cache.jsonCached server response
~/.coditect/license/offline-log.jsonOffline period tracking
~/.coditect/config/license-public-keys.jsonEmbedded public keys
~/.coditect/config/licensing.jsonLicense configuration
~/.coditect/machine-id.jsonHardware UUID (existing)

Consequences

Positive

  1. Revenue Protection - Prevents unauthorized use and license sharing
  2. Offline Support - Users can work without internet for configured period
  3. Graceful Degradation - Clear warnings before blocking, grace periods
  4. Enterprise-Ready - Centralized management, audit trails, configurable policies
  5. Cryptographically Secure - HSM-protected signing keys, Ed25519 signatures
  6. Device Binding - Uses existing machine-id.json infrastructure
  7. User-Friendly - Automatic validation in /orient, clear CLI commands

Negative

  1. Network Dependency - Requires periodic server check (mitigated by offline tolerance)
  2. Implementation Complexity - Multiple states, flows, and edge cases
  3. User Friction - Activation step required for new users
  4. Infrastructure Cost - Cloud KMS, API endpoints, revocation checking

Risks and Mitigations

RiskMitigation
Server outage blocks users14-day offline tolerance + cached validation
Key compromiseKey rotation mechanism + revocation list
False positive blocksGrace periods + clear error messages + support escalation
Clock drift issuesUse server time, allow +/- 1 hour tolerance
Database sync issuesLocal cache is authoritative within offline period

Implementation Recommendations

Phase 1: Foundation (Week 1-2)

  • Create ~/.coditect/license/ directory structure
  • Implement license file format and signature verification
  • Create /license CLI commands (status, activate, refresh)
  • Integrate validation check into /orient

Phase 2: Backend API (Week 3-4)

  • Implement /api/v1/licenses/* endpoints
  • Set up Cloud KMS signing key
  • Create admin dashboard for license management
  • Implement Stripe webhook for subscription renewal

Phase 3: Offline Support (Week 5)

  • Implement offline tolerance tracking
  • Create revocation list endpoint and caching
  • Add offline abuse detection
  • Test network failure scenarios

Phase 4: Enterprise Features (Week 6-8)

  • Seat management API
  • Centralized admin portal
  • Audit logging
  • Custom policy configuration

References


Author: senior-architect agent Reviewed By: Pending Approved: January 12, 2026