Skip to main content

ADR-193: Licensed Docker Container Schema Design

Status

ACCEPTED - Implemented 2026-01-05

Context

CODITECT needs to distribute licensed containers that:

  1. Protect the framework source code from modification
  2. Validate customer license keys on startup
  3. Maintain session heartbeats for license compliance
  4. Support both Docker and GCP Cloud Workstations deployment

Decision

Implement a layered container architecture with root-owned framework protection:

Architecture

┌─────────────────────────────────────────────────────────────────┐
│ Container Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ base/Dockerfile.base │
│ ├── Python 3.11 + Node.js 20 │
│ ├── Claude Code pre-installed │
│ ├── Developer user (uid 1000, no sudo) │
│ └── /opt/coditect -> root:root, 444/555 permissions │
│ │ │
│ ┌──────────┴──────────┐ ┌───────────────────────┐ │
│ │ │ │ │ │
│ ▼ ▼ ▼ │ │
│ licensed-docker/ licensed-workstation/ │ │
│ └── License key auth └── GCP OAuth auth │ │
│ └── API heartbeat └── Metadata service │ │
│ └── Session tracking └── Multi-user support │ │
│ │
└─────────────────────────────────────────────────────────────────┘

Protection Model

ProtectionImplementation
Root-Owned Framework/opt/coditect owned by root:root
Read-Only FilesAll files: chmod 444
Executable DirsAll dirs: chmod 555
No Sudo AccessDeveloper user not in sudo/wheel/admin groups
Symlink Discovery/home/developer/.coditect/opt/coditect

License Validation Flow

Container Start


┌─────────────────┐
│ Read License Key│ ← CODITECT_LICENSE_KEY env var
└────────┬────────┘


┌─────────────────┐
│ Validate with │ → POST /api/v1/sessions/validate
│ License API │
└────────┬────────┘

┌────┴────┐
│ │
Valid Invalid
│ │
▼ ▼
┌───────┐ ┌───────────┐
│ Start │ │ Exit with │
│Heartbt│ │ Error Msg │
└───┬───┘ └───────────┘


┌─────────────────┐
│ Background │ → Every 5 minutes
│ Heartbeat │ → POST /api/v1/sessions/heartbeat
└─────────────────┘

Implementation

File Structure

distribution/
├── base/
│ ├── Dockerfile.base # Shared base image
│ └── scripts/
│ ├── entrypoint-base.sh # Base entrypoint with heartbeat
│ ├── healthcheck.sh # Container health check
│ └── welcome.sh # Welcome message

├── licensed-docker/
│ ├── Dockerfile # Docker deployment image
│ ├── cloudbuild.yaml # Cloud Build for Docker
│ └── scripts/
│ └── entrypoint.sh # License key validation

├── licensed-workstation/
│ ├── Dockerfile # Workstation deployment image
│ ├── cloudbuild.yaml # Cloud Build for Workstation
│ ├── config.yaml.template # Workstation config template
│ └── scripts/
│ └── entrypoint.sh # GCP OAuth validation

├── local-dev/ # INTERNAL development only
│ ├── Dockerfile # No license required
│ ├── docker-compose.yml # Full dev stack
│ └── README.md # Internal use warning

└── tests/
├── test_protection_model.sh # ADR-055 protection tests
├── test_claude_code.sh # Framework integration tests
├── test_license_validation.sh # License flow tests
└── test_heartbeat.sh # Heartbeat mechanism tests

Artifact Registry

  • Registry: us-central1-docker.pkg.dev/coditect-citus-prod/coditect-licensed
  • Images:
    • coditect-base:VERSION - Shared base image
    • coditect-docker:VERSION - Customer Docker deployment
    • coditect-workstation:VERSION - GCP Cloud Workstations

Environment Variables

VariableRequiredDescription
CODITECT_LICENSE_KEYYes (Docker)Customer license key
CODITECT_API_URLNoLicense API URL (default: api.coditect.ai)
CODITECT_HEARTBEAT_INTERVALNoHeartbeat interval in seconds (default: 300)
CODITECT_CONTAINER_TYPEAutodocker or workstation

Testing

93 automated tests verify the protection model and image signing:

Test SuiteTestsPurpose
test_protection_model.sh25Root ownership, permissions, no sudo
test_claude_code.sh28Framework access, workspace, symlinks
test_license_validation.sh13Valid/invalid/expired license flow
test_heartbeat.sh17Session heartbeat mechanism
test_signature_verification.sh10Cosign/KMS image signing verification

Run tests:

docker exec <container> /opt/coditect-scripts/test_protection_model.sh

Docker Content Trust (Image Signing)

Status: REQUIRED (C.6.6.3)

Purpose

Docker Content Trust (DCT) uses cryptographic signatures to verify image integrity and publisher identity. When enabled, Docker clients refuse to pull unsigned images.

Signing Flow

┌─────────────────────────────────────────────────────────────────┐
│ IMAGE SIGNING FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ BUILD TIME PULL TIME │
│ ────────── ───────── │
│ ┌──────────┐ ┌──────────┐ │
│ │ Build │ │ Docker │ │
│ │ Image │ │ Pull │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Sign │◄── Cloud KMS Key │ Verify │ │
│ │ Digest │ │Signature │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ ▼ │ │
│ ┌──────────┐ ┌───┴───┐ │
│ │ Push to │ │ │ │
│ │ Registry │ Valid Invalid │
│ └──────────┘ │ │ │
│ ▼ ▼ │
│ ┌──────┐ ┌──────┐ │
│ │ Pull │ │REJECT│ │
│ │ OK │ │ │ │
│ └──────┘ └──────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Security Value

ThreatWithout DCTWith DCT
Man-in-the-MiddleAttacker swaps image in transitSignature mismatch rejects
Registry CompromiseAttacker replaces image in registrySignature mismatch rejects
Supply Chain AttackMalicious image pushed to repoOnly signed images accepted
Insider ThreatRogue employee pushes backdoorRequires KMS key access
Rollback AttackAttacker serves old vulnerable imageSignature includes version

Why REQUIRED

  1. Customer Trust - Licensed customers pay for verified, unmodified framework
  2. Compliance - SOC2, ISO 27001 require software integrity verification
  3. Enterprise Sales - Security teams audit container supply chain
  4. Liability - If compromised image deployed, clear chain of custody needed
  5. Differentiation - Competitors (Cursor, Windsurf) don't offer signed containers

Implementation

Cloud KMS Setup (OpenTofu):

# In distribution/opentofu/modules/artifact-registry/main.tf

resource "google_kms_key_ring" "container_signing" {
name = "coditect-container-signing"
location = "us-central1"
}

resource "google_kms_crypto_key" "dct_signing_key" {
name = "dct-signing-key"
key_ring = google_kms_key_ring.container_signing.id
purpose = "ASYMMETRIC_SIGN"

version_template {
algorithm = "EC_SIGN_P384_SHA384"
protection_level = "HSM" # Hardware Security Module
}

rotation_period = "7776000s" # 90 days
}

Customer Verification:

# Verify image before pulling
cosign verify --key https://coditect.ai/.well-known/cosign.pub \
us-central1-docker.pkg.dev/coditect-citus-prod/coditect-licensed/coditect-docker:v1.0.0

Cost Analysis

ComponentMonthly Cost
Cloud KMS HSM Key~$1.00/key/month
Signing Operations~$0.03/10,000 ops
Total~$2-5/month

Implementation Status

StepStatusEffort
Create KMS keyringComplete15 min
Add Cosign to workflowComplete30 min
Update customer docsComplete30 min
Add verification testsComplete30 min

Implemented: 2026-01-05


Consequences

Positive

  • Framework source code protected from modification
  • License compliance enforced at container level
  • Consistent experience across Docker and Workstation
  • 93 automated tests ensure protection model integrity
  • Cryptographic image signing enables enterprise compliance (SOC2, ISO 27001)
  • Supply chain security prevents tampering in transit or at registry

Negative

  • Customers cannot modify framework (by design)
  • Requires license server availability for validation
  • Additional complexity in container build process

Neutral

  • Two deployment paths (Docker vs Workstation) to maintain
  • OpenTofu infrastructure required for customer deployment

Supersedes: None Superseded By: None Related ADRs: ADR-053 (Cloud Context Sync), ADR-054 (Track Nomenclature)