ADR-055: Licensed Docker Container Schema Design
Status
ACCEPTED - Implemented 2026-01-05
Context
CODITECT needs to distribute licensed containers that:
- Protect the framework source code from modification
- Validate customer license keys on startup
- Maintain session heartbeats for license compliance
- 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
| Protection | Implementation |
|---|---|
| Root-Owned Framework | /opt/coditect owned by root:root |
| Read-Only Files | All files: chmod 444 |
| Executable Dirs | All dirs: chmod 555 |
| No Sudo Access | Developer 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
│
├── opentofu/
│ ├── main.tf # Root infrastructure module
│ ├── modules/
│ │ ├── artifact-registry/ # Container registry
│ │ ├── customer-deployment/ # Customer Cloud Run module
│ │ └── license-validation/ # Secret Manager, Redis
│ └── README.md
│
└── 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 imagecoditect-docker:VERSION- Customer Docker deploymentcoditect-workstation:VERSION- GCP Cloud Workstations
Environment Variables
| Variable | Required | Description |
|---|---|---|
CODITECT_LICENSE_KEY | Yes (Docker) | Customer license key |
CODITECT_API_URL | No | License API URL (default: api.coditect.ai) |
CODITECT_HEARTBEAT_INTERVAL | No | Heartbeat interval in seconds (default: 300) |
CODITECT_CONTAINER_TYPE | Auto | docker or workstation |
Testing
93 automated tests verify the protection model and image signing:
| Test Suite | Tests | Purpose |
|---|---|---|
test_protection_model.sh | 25 | Root ownership, permissions, no sudo |
test_claude_code.sh | 28 | Framework access, workspace, symlinks |
test_license_validation.sh | 13 | Valid/invalid/expired license flow |
test_heartbeat.sh | 17 | Session heartbeat mechanism |
test_signature_verification.sh | 10 | Cosign/KMS image signing verification |
Run tests:
docker exec <container> /opt/coditect-scripts/test_protection_model.sh
CI/CD
Cloud Build (GCP)
# Build and push Docker image
cd distribution/licensed-docker
gcloud builds submit --config=cloudbuild.yaml ../..
GitHub Actions
Workflow: .github/workflows/build-licensed-images.yml
- Triggers on release publish
- Builds base, Docker, and Workstation images
- Runs protection model tests
- Scans for vulnerabilities with Trivy
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
| Threat | Without DCT | With DCT |
|---|---|---|
| Man-in-the-Middle | Attacker swaps image in transit | Signature mismatch rejects |
| Registry Compromise | Attacker replaces image in registry | Signature mismatch rejects |
| Supply Chain Attack | Malicious image pushed to repo | Only signed images accepted |
| Insider Threat | Rogue employee pushes backdoor | Requires KMS key access |
| Rollback Attack | Attacker serves old vulnerable image | Signature includes version |
Why REQUIRED
- Customer Trust - Licensed customers pay for verified, unmodified framework
- Compliance - SOC2, ISO 27001 require software integrity verification
- Enterprise Sales - Security teams audit container supply chain
- Liability - If compromised image deployed, clear chain of custody needed
- 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
}
GitHub Actions (Cosign):
# In .github/workflows/build-licensed-images.yml
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Sign Image
env:
COSIGN_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
run: |
cosign sign --key env://COSIGN_KEY \
${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/coditect-docker:${{ github.event.release.tag_name }}
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
| Component | Monthly Cost |
|---|---|
| Cloud KMS HSM Key | ~$1.00/key/month |
| Signing Operations | ~$0.03/10,000 ops |
| Total | ~$2-5/month |
ROI Justification
| Factor | Value |
|---|---|
| Enterprise deals requiring signed images | 60%+ |
| Average enterprise contract value | $50K+ |
| Cost of implementation | ~$500 (one-time) + $5/month |
| Conclusion | Enables enterprise sales at negligible cost |
Implementation Status
| Step | Status | Effort |
|---|---|---|
| Create KMS keyring | Complete | 15 min |
| Add Cosign to workflow | Complete | 30 min |
| Update customer docs | Complete | 30 min |
| Add verification tests | Complete | 30 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
- 83 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
Related
Supersedes: None Superseded By: None Related ADRs: ADR-053 (Cloud Context Sync), ADR-054 (Track Nomenclature)