# Nickel vs KCL: Comprehensive Comparison\n\n**Status**: Reference Guide\n**Last Updated**: 2025-12-15\n**Related**: ADR-011: Migration from KCL to Nickel\n\n---\n\n## Quick Decision Tree\n\n```\nNeed to define infrastructure/schemas?\n├─ New platform schemas → Use Nickel ✅\n├─ New provider extensions → Use Nickel ✅\n├─ Legacy workspace configs → Can use KCL (migrate gradually)\n├─ Need type-safe UIs? → Nickel + TypeDialog ✅\n├─ Application settings? → Use TOML (not KCL/Nickel)\n└─ K8s/CI-CD config? → Use YAML (not KCL/Nickel)\n```\n\n---\n\n## 1. Side-by-Side Code Examples\n\n### Simple Schema: Server Configuration\n\n#### KCL Approach\n\n```\nschema ServerDefaults:\n    name: str\n    cpu_cores: int = 2\n    memory_gb: int = 4\n    os: str = "ubuntu"\n\n    check:\n        cpu_cores > 0, "CPU cores must be positive"\n        memory_gb > 0, "Memory must be positive"\n\nserver_defaults: ServerDefaults = {\n    name = "web-server",\n    cpu_cores = 4,\n    memory_gb = 8,\n    os = "ubuntu",\n}\n```\n\n**Note**: KCL is deprecated. Use Nickel for new projects.\n\n#### Nickel Approach (Three-File Pattern)\n\n**server_contracts.ncl**:\n\n```\n{\n  ServerDefaults = {\n    name | String,\n    cpu_cores | Number,\n    memory_gb | Number,\n    os | String,\n  },\n}\n```\n\n**server_defaults.ncl**:\n\n```\n{\n  server = {\n    name = "web-server",\n    cpu_cores = 4,\n    memory_gb = 8,\n    os = "ubuntu",\n  },\n}\n```\n\n**server.ncl**:\n\n```\nlet contracts = import "./server_contracts.ncl" in\nlet defaults = import "./server_defaults.ncl" in\n\n{\n  defaults = defaults,\n\n  make_server | not_exported = fun overrides =>\n    defaults.server & overrides,\n\n  DefaultServer = defaults.server,\n}\n```\n\n**Usage**:\n\n```\nlet server = import "./server.ncl" in\n\n# Simple override\nmy_server = server.make_server { cpu_cores = 8 }\n\n# With custom field (Nickel allows this!)\nmy_custom = server.defaults.server & {\n  cpu_cores = 16,\n  custom_monitoring_level = "verbose"  # ✅ Works!\n}\n```\n\n**Key Differences**:\n\n- **KCL**: Validation inline, single file, rigid schema\n- **Nickel**: Separated concerns (contracts, defaults, instances), flexible composition\n\n---\n\n### Complex Schema: Provider with Multiple Types\n\n#### KCL (from `provisioning/extensions/providers/upcloud/nickel/` - legacy approach)\n\n```\nschema StorageBackup:\n    backup_id: str\n    frequency: str\n    retention_days: int = 7\n\nschema ServerUpcloud:\n    name: str\n    plan: str\n    zone: str\n    storage_backups: [StorageBackup] = []\n\nschema ProvisionUpcloud:\n    api_key: str\n    api_password: str\n    servers: [ServerUpcloud] = []\n\nprovision_upcloud: ProvisionUpcloud = {\n    api_key = ""\n    api_password = ""\n    servers = []\n}\n```\n\n#### Nickel (from `provisioning/extensions/providers/upcloud/nickel/`)\n\n**upcloud_contracts.ncl**:\n\n```\n{\n  StorageBackup = {\n    backup_id | String,\n    frequency | String,\n    retention_days | Number,\n  },\n\n  ServerUpcloud = {\n    name | String,\n    plan | String,\n    zone | String,\n    storage_backups | Array,\n  },\n\n  ProvisionUpcloud = {\n    api_key | String,\n    api_password | String,\n    servers | Array,\n  },\n}\n```\n\n**upcloud_defaults.ncl**:\n\n```\n{\n  storage_backup = {\n    backup_id = "",\n    frequency = "daily",\n    retention_days = 7,\n  },\n\n  server_upcloud = {\n    name = "",\n    plan = "1xCPU-1 GB",\n    zone = "us-nyc1",\n    storage_backups = [],\n  },\n\n  provision_upcloud = {\n    api_key = "",\n    api_password = "",\n    servers = [],\n  },\n}\n```\n\n**upcloud_main.ncl** (from actual codebase):\n\n```\nlet contracts = import "./upcloud_contracts.ncl" in\nlet defaults = import "./upcloud_defaults.ncl" in\n\n{\n  defaults = defaults,\n\n  make_storage_backup | not_exported = fun overrides =>\n    defaults.storage_backup & overrides,\n\n  make_server_upcloud | not_exported = fun overrides =>\n    defaults.server_upcloud & overrides,\n\n  make_provision_upcloud | not_exported = fun overrides =>\n    defaults.provision_upcloud & overrides,\n\n  DefaultStorageBackup = defaults.storage_backup,\n  DefaultServerUpcloud = defaults.server_upcloud,\n  DefaultProvisionUpcloud = defaults.provision_upcloud,\n}\n```\n\n**Usage Comparison**:\n\n```\n# KCL way (KCL no lo permite bien)\n# Cannot easily extend without schema modification\n\n# Nickel way (flexible!)\nlet upcloud = import "./upcloud.ncl" in\n\n# Simple override\nstaging_server = upcloud.make_server_upcloud {\n  name = "staging-01",\n  zone = "eu-fra1",\n}\n\n# Complex config with custom fields\nproduction_stack = upcloud.make_provision_upcloud {\n  api_key = "secret",\n  api_password = "secret",\n  servers = [\n    upcloud.make_server_upcloud { name = "prod-web-01" },\n    upcloud.make_server_upcloud { name = "prod-web-02" },\n  ],\n  custom_vpc_id = "vpc-prod",           # ✅ Custom field allowed!\n  monitoring_enabled = true,             # ✅ Custom field allowed!\n  backup_schedule = "24h",              # ✅ Custom field allowed!\n}\n```\n\n---\n\n## 2. Performance Benchmarks\n\n### Evaluation Speed\n\n| File Type | KCL | Nickel | Improvement |\n| ----------- | ----- | -------- | ------------ |\n| Simple schema (100 lines) | 45 ms | 18 ms | 60% faster |\n| Complex config (500 lines) | 180 ms | 72 ms | 60% faster |\n| Large nested (2000 lines) | 420 ms | 160 ms | 62% faster |\n| Infrastructure full stack | 850 ms | 340 ms | 60% faster |\n\n**Test Conditions**:\n\n- MacOS 13.x, M1 Pro\n- Single evaluation run\n- JSON output export\n- Average of 5 runs\n\n### Memory Usage\n\n| Configuration | KCL | Nickel | Improvement |\n| --------------- | ----- | -------- | ------------ |\n| Platform schemas (422 files) | ~180 MB | ~85 MB | 53% less |\n| Full workspace (47 files) | ~45 MB | ~22 MB | 51% less |\n| Single provider ext | ~8 MB | ~4 MB | 50% less |\n\n**Lazy Evaluation Benefit**:\n\n- KCL: Evaluates all schemas upfront\n- Nickel: Only evaluates what's used (lazy)\n- Nickel advantage: 40-50% memory savings on large configs\n\n---\n\n## 3. Use Case Examples\n\n### Use Case 1: Simple Server Definition\n\n**KCL (Legacy)**:\n\n```\nschema ServerConfig:\n    name: str\n    zone: str = "us-nyc1"\n\nweb_server: ServerConfig = {\n    name = "web-01",\n}\n```\n\n**Nickel (Recommended)**:\n\n```\nlet defaults = import "./server_defaults.ncl" in\nweb_server = defaults.make_server { name = "web-01" }\n```\n\n**Winner**: Nickel (simpler, cleaner)\n\n---\n\n### Use Case 2: Multiple Taskservs with Dependencies\n\n**KCL** (from wuji infrastructure):\n\n```\nschema TaskServDependency:\n    name: str\n    wait_for_health: bool = false\n\nschema TaskServ:\n    name: str\n    version: str\n    dependencies: [TaskServDependency] = []\n\ntaskserv_kubernetes: TaskServ = {\n    name = "kubernetes",\n    version = "1.28.0",\n    dependencies = [\n        {name = "containerd"},\n        {name = "etcd"},\n    ]\n}\n\ntaskserv_cilium: TaskServ = {\n    name = "cilium",\n    version = "1.14.0",\n    dependencies = [\n        {name = "kubernetes", wait_for_health = true}\n    ]\n}\n```\n\n**Nickel** (from wuji/main.ncl):\n\n```\nlet ts_kubernetes = import "./taskservs/kubernetes.ncl" in\nlet ts_cilium = import "./taskservs/cilium.ncl" in\nlet ts_containerd = import "./taskservs/containerd.ncl" in\n\n{\n  taskservs = {\n    kubernetes = ts_kubernetes.kubernetes,\n    cilium = ts_cilium.cilium,\n    containerd = ts_containerd.containerd,\n  },\n}\n```\n\n**Winner**: Nickel (modular, scalable to 20 taskservs)\n\n---\n\n### Use Case 3: Configuration Extension with Custom Fields\n\n**Scenario**: Need to add monitoring configuration to server definition\n\n**KCL**:\n\n```\nschema ServerConfig:\n    name: str\n    # Would need to modify schema!\n    monitoring_enabled: bool = false\n    monitoring_level: str = "basic"\n\n# All existing configs need updating...\n```\n\n**Nickel**:\n\n```\nlet server = import "./server.ncl" in\n\n# Add custom fields without modifying schema!\nmy_server = server.defaults.server & {\n  name = "web-01",\n  monitoring_enabled = true,\n  monitoring_level = "detailed",\n  custom_tags = ["production", "critical"],\n  grafana_dashboard = "web-servers",\n}\n```\n\n**Winner**: Nickel (no schema modifications needed)\n\n---\n\n## 4. Architecture Patterns Comparison\n\n### Schema Inheritance\n\n**KCL Approach (Legacy)**:\n\n```\nschema ServerDefaults:\n    cpu: int = 2\n    memory: int = 4\n\nschema Server(ServerDefaults):\n    name: str\n\nserver: Server = {\n    name = "web-01",\n    cpu = 4,\n    memory = 8,\n}\n```\n\n**Problem**: Inheritance creates rigid hierarchies, breaking changes propagate\n\n---\n\n**Nickel Approach**:\n\n```\n# defaults.ncl\nserver_defaults = {\n  cpu = 2,\n  memory = 4,\n}\n\n# main.ncl\nlet make_server = fun overrides =>\n  defaults.server_defaults & overrides\n\nserver = make_server {\n  name = "web-01",\n  cpu = 4,\n  memory = 8,\n}\n```\n\n**Advantage**: Flexible composition via record merging, no inheritance rigidity\n\n---\n\n### Validation\n\n**KCL Validation (Legacy)** (compile-time, inline):\n\n```\nschema Config:\n    timeout: int = 5\n\n    check:\n        timeout > 0, "Timeout must be positive"\n        timeout < 300, "Timeout must be < 5 min"\n```\n\n**Pros**: Validation at schema definition\n**Cons**: Overhead during compilation, rigid\n\n---\n\n**Nickel Validation** (runtime, contract-based):\n\n```\n# contracts.ncl - Pure type definitions\nConfig = {\n  timeout | Number,\n}\n\n# Usage - Optional validation\nlet validate_config = fun config =>\n  if config.timeout <= 0 then\n    std.record.fail "Timeout must be positive"\n  else if config.timeout >= 300 then\n    std.record.fail "Timeout must be < 5 min"\n  else\n    config\n\n# Apply only when needed\nmy_config = validate_config { timeout = 10 }\n```\n\n**Pros**: Lazy evaluation, optional, fine-grained control\n**Cons**: Must invoke validation explicitly\n\n---\n\n## 5. Migration Patterns (Before/After)\n\n### Pattern 1: Simple Schema Migration\n\n**Before (KCL - Legacy)**:\n\n```\nschema Scheduler:\n    strategy: str = "fifo"\n    workers: int = 4\n\n    check:\n        workers > 0, "Workers must be positive"\n\nscheduler_config: Scheduler = {\n    strategy = "priority",\n    workers = 8,\n}\n```\n\n**After (Nickel - Current)**:\n\n`scheduler_contracts.ncl`:\n\n```\n{\n  Scheduler = {\n    strategy | String,\n    workers | Number,\n  },\n}\n```\n\n`scheduler_defaults.ncl`:\n\n```\n{\n  scheduler = {\n    strategy = "fifo",\n    workers = 4,\n  },\n}\n```\n\n`scheduler.ncl`:\n\n```\nlet contracts = import "./scheduler_contracts.ncl" in\nlet defaults = import "./scheduler_defaults.ncl" in\n\n{\n  defaults = defaults,\n  make_scheduler | not_exported = fun o =>\n    defaults.scheduler & o,\n  DefaultScheduler = defaults.scheduler,\n  SchedulerConfig = defaults.scheduler & {\n    strategy = "priority",\n    workers = 8,\n  },\n}\n```\n\n---\n\n### Pattern 2: Union Types → Enums\n\n**Before (KCL - Legacy)**:\n\n```\nschema Mode:\n    deployment_type: str = "solo"  # "solo" | "multiuser" | "cicd" | "enterprise"\n\n    check:\n        deployment_type in ["solo", "multiuser", "cicd", "enterprise"],\n            "Invalid deployment type"\n```\n\n**After (Nickel - Current)**:\n\n```\n# contracts.ncl\n{\n  Mode = {\n    deployment_type | [| 'solo, 'multiuser, 'cicd, 'enterprise |],\n  },\n}\n\n# defaults.ncl\n{\n  mode = {\n    deployment_type = 'solo,\n  },\n}\n```\n\n**Benefits**: Type-safe, no string validation needed\n\n---\n\n### Pattern 3: Schema Inheritance → Record Merging\n\n**Before (KCL - Legacy)**:\n\n```\nschema ServerDefaults:\n    cpu: int = 2\n    memory: int = 4\n\nschema Server(ServerDefaults):\n    name: str\n\nweb_server: Server = {\n    name = "web-01",\n    cpu = 8,\n    memory = 16,\n}\n```\n\n**After (Nickel - Current)**:\n\n```\n# defaults.ncl\n{\n  server_defaults = {\n    cpu = 2,\n    memory = 4,\n  },\n\n  web_server = {\n    name = "web-01",\n    cpu = 8,\n    memory = 16,\n  },\n}\n\n# main.ncl - Composition\nlet make_server = fun config =>\n  defaults.server_defaults & config & {\n    name = config.name,\n  }\n```\n\n**Advantage**: Explicit, flexible, composable\n\n---\n\n## 6. Deployment Workflows\n\n### Development Mode (Single Source of Truth)\n\n**When to Use**: Local development, testing, iterations\n\n**Workflow**:\n\n```\n# Edit workspace config\ncd workspace_librecloud/nickel\nvim wuji/main.ncl\n\n# Test immediately (relative imports)\nnickel export wuji/main.ncl --format json\n\n# Changes to central provisioning reflected immediately\nvim ../../provisioning/schemas/lib/main.ncl\nnickel export wuji/main.ncl  # Uses updated schemas\n```\n\n**Imports** (relative, central):\n\n```\nimport "../../provisioning/schemas/main.ncl"\nimport "../../provisioning/extensions/taskservs/kubernetes/nickel/main.ncl"\n```\n\n---\n\n### Production Mode (Frozen Snapshots)\n\n**When to Use**: Deployments, releases, reproducibility\n\n**Workflow**:\n\n```\n# 1. Create immutable snapshot\nprovisioning workspace freeze \\n  --version "2025-12-15-prod-v1" \\n  --env production\n\n# 2. Frozen structure created\n.frozen/2025-12-15-prod-v1/\n├── provisioning/schemas/     # Snapshot\n├── extensions/               # Snapshot\n└── workspace/                # Snapshot\n\n# 3. Deploy from frozen\nprovisioning deploy \\n  --frozen "2025-12-15-prod-v1" \\n  --infra wuji\n\n# 4. Rollback if needed\nprovisioning deploy \\n  --frozen "2025-12-10-prod-v0" \\n  --infra wuji\n```\n\n**Frozen Imports** (rewritten to local):\n\n```\n# Original in workspace\nimport "../../provisioning/schemas/main.ncl"\n\n# Rewritten in frozen snapshot\nimport "./provisioning/schemas/main.ncl"\n```\n\n**Benefits**:\n\n- ✅ Immutable deployments\n- ✅ No external dependencies\n- ✅ Reproducible across environments\n- ✅ Works offline/air-gapped\n- ✅ Easy rollback\n\n---\n\n## 7. Troubleshooting Guide\n\n### Error: "unexpected token" with Multiple Let Bindings\n\n**Problem**:\n\n```\n# ❌ WRONG\nlet A = { x = 1 }\nlet B = { y = 2 }\n{ A = A, B = B }\n```\n\nError: `unexpected token`\n\n**Solution**: Use `let...in` chaining:\n\n```\n# ✅ CORRECT\nlet A = { x = 1 } in\nlet B = { y = 2 } in\n{ A = A, B = B }\n```\n\n---\n\n### Error: "this can't be used as a contract"\n\n**Problem**:\n\n```\n# ❌ WRONG\nlet StorageVol = {\n  mount_path : String | null = null,\n}\n```\n\nError: `this can't be used as a contract`\n\n**Explanation**: Union types with `null` don't work in field annotations\n\n**Solution**: Use untyped assignment:\n\n```\n# ✅ CORRECT\nlet StorageVol = {\n  mount_path = null,\n}\n```\n\n---\n\n### Error: "infinite recursion" when Exporting\n\n**Problem**:\n\n```\n# ❌ WRONG\n{\n  get_value = fun x => x + 1,\n  result = get_value 5,\n}\n```\n\nError: Functions can't be serialized\n\n**Solution**: Mark helper functions `not_exported`:\n\n```\n# ✅ CORRECT\n{\n  get_value | not_exported = fun x => x + 1,\n  result = get_value 5,\n}\n```\n\n---\n\n### Error: "field not found" After Renaming\n\n**Problem**:\n\n```\nlet defaults = import "./defaults.ncl" in\ndefaults.scheduler_config  # But file has "scheduler"\n```\n\nError: `field not found`\n\n**Solution**: Use exact field names:\n\n```\nlet defaults = import "./defaults.ncl" in\ndefaults.scheduler  # Correct name from defaults.ncl\n```\n\n---\n\n### Performance Issue: Slow Exports\n\n**Problem**: Large nested configs slow to export\n\n**Solution**: Check for circular references or missing `not_exported`:\n\n```\n# ❌ Slow - functions being serialized\n{\n  validate_config = fun x => x,\n  data = { foo = "bar" },\n}\n\n# ✅ Fast - functions excluded\n{\n  validate_config | not_exported = fun x => x,\n  data = { foo = "bar" },\n}\n```\n\n---\n\n## 8. Best Practices\n\n### For Nickel Schemas\n\n1. **Follow Three-File Pattern**\n\n   ```nickel\n\n   module_contracts.ncl    # Types only\n   module_defaults.ncl     # Values only\n   module.ncl              # Instances + interface\n\n   ```\n\n2. **Use Hybrid Interface** (4 levels)\n   - Level 1: Direct defaults (inspection)\n   - Level 2: Maker functions (customization)\n   - Level 3: Default instances (pre-built)\n   - Level 4: Contracts (optional, advanced)\n\n3. **Record Merging for Composition**\n\n   ```nickel\n   let defaults = import "./defaults.ncl" in\n   my_config = defaults.server & { custom_field = "value" }\n   ```\n\n1. **Mark Helper Functions `not_exported`**\n\n   ```nickel\n   validate | not_exported = fun x => x,\n   ```\n\n2. **No Null Values in Defaults**\n\n   ```nickel\n   # ✅ Good\n   { field = "" }  # empty string for optional\n\n   # ❌ Avoid\n   { field = null }  # causes export issues\n   ```\n\n---\n\n### For Legacy KCL (Workspace-Level - Deprecated)\n\n**Note**: KCL is deprecated. Gradually migrate to Nickel for new projects.\n\n1. **Schema-First Development**\n   - Define schemas before configs\n   - Explicit validation\n\n2. **Immutability by Default**\n   - KCL enforces immutability\n   - Use `_` prefix only when necessary\n\n3. **Direct Submodule Imports**\n\n   ```kcl\n   import provisioning.lib as lib\n   ```\n\n4. **Complex Validation**\n\n   ```kcl\n   check:\n       timeout > 0, "Must be positive"\n       timeout < 300, "Must be < 5 min"\n   ```\n\n---\n\n## 9. TypeDialog Integration\n\n### What is TypeDialog\n\nType-safe prompts, forms, and schemas that **bidirectionally integrate with Nickel**.\n\n**Location**: `/Users/Akasha/Development/typedialog`\n\n### Workflow: Nickel Schemas → Interactive UIs → Nickel Output\n\n```\n# 1. Define schema in Nickel\ncat > server.ncl << 'EOF'\nlet contracts = import "./contracts.ncl" in\n{\n  DefaultServer = {\n    name = "web-01",\n    cpu = 4,\n    memory = 8,\n    zone = "us-nyc1",\n  },\n}\nEOF\n\n# 2. Generate interactive form from schema\ntypedialog form --schema server.ncl --output json\n\n# 3. User fills form interactively (CLI, TUI, or Web)\n# Prompts generated from field names\n# Defaults populated from Nickel config\n\n# 4. Output back to Nickel\ntypedialog form --input form.toml --output nickel\n```\n\n### Benefits\n\n- **Type-Safe UIs**: Forms validated against Nickel contracts\n- **Auto-Generated**: No UI code to maintain\n- **Multiple Backends**: CLI (inquire), TUI (ratatui), Web (axum)\n- **Multiple Formats**: JSON, YAML, TOML, Nickel output\n- **Bidirectional**: Nickel → UIs → Nickel\n\n### Example: Infrastructure Wizard\n\n```\n# User runs\nprovisioning init --wizard\n\n# Backend generates TypeDialog form from:\nprovisioning/schemas/config/workspace_config/main.ncl\n\n# Interactive form with:\n- workspace_name (text prompt)\n- deployment_mode (select: solo/multiuser/cicd/enterprise)\n- preferred_provider (select: upcloud/aws/hetzner)\n- taskservs (multi-select: kubernetes, cilium, etcd, etc)\n- custom_settings (advanced, optional)\n\n# Output: workspace_config.ncl (valid Nickel!)\n```\n\n---\n\n## 10. Migration Checklist\n\n### Before Starting Migration\n\n- [ ] Read ADR-011\n- [ ] Review [Nickel Migration Guide](../development/nickel-executable-examples.md)\n- [ ] Identify which module to migrate\n- [ ] Check for dependencies on other modules\n\n### During Migration\n\n- [ ] Extract contracts from KCL schema\n- [ ] Extract defaults from KCL config\n- [ ] Create main.ncl with hybrid interface\n- [ ] Validate JSON export: `nickel export main.ncl --format json`\n- [ ] Compare JSON output with original KCL\n\n### Validation\n\n- [ ] All required fields present\n- [ ] No null values (use empty strings/arrays)\n- [ ] Contracts are pure definitions\n- [ ] Defaults are complete values\n- [ ] Main file has 4-level interface\n- [ ] Syntax validation passes\n- [ ] No `...` as code omission indicators\n\n### Post-Migration\n\n- [ ] Update imports in dependent files\n- [ ] Test in development mode\n- [ ] Create frozen snapshot\n- [ ] Test production deployment\n- [ ] Update documentation\n\n---\n\n## 11. Real-World Examples from Codebase\n\n### Example 1: Platform Schemas Entry Point\n\n**File**: `provisioning/schemas/main.ncl` (174 lines)\n\n```\n# Domain-organized architecture\n{\n  lib | doc "Core library types"\n    = import "./lib/main.ncl",\n\n  config | doc "Settings, defaults, workspace_config"\n    = {\n      settings = import "./config/settings/main.ncl",\n      defaults = import "./config/defaults/main.ncl",\n      workspace_config = import "./config/workspace_config/main.ncl",\n    },\n\n  infrastructure | doc "Compute, storage, provisioning"\n    = {\n      compute = {\n        server = import "./infrastructure/compute/server/main.ncl",\n        cluster = import "./infrastructure/compute/cluster/main.ncl",\n      },\n      storage = {\n        vm = import "./infrastructure/storage/vm/main.ncl",\n      },\n    },\n\n  operations | doc "Workflows, batch, dependencies, tasks"\n    = {\n      workflows = import "./operations/workflows/main.ncl",\n      batch = import "./operations/batch/main.ncl",\n    },\n\n  deployment | doc "Kubernetes, modes"\n    = {\n      kubernetes = import "./deployment/kubernetes/main.ncl",\n      modes = import "./deployment/modes/main.ncl",\n    },\n}\n```\n\n**Usage**:\n\n```\nlet provisioning = import "./main.ncl" in\n\nprovisioning.lib.Storage\nprovisioning.config.settings\nprovisioning.infrastructure.compute.server\nprovisioning.operations.workflows\n```\n\n---\n\n### Example 2: Provider Extension (UpCloud)\n\n**File**: `provisioning/extensions/providers/upcloud/nickel/main.ncl` (38 lines)\n\n```\nlet contracts_lib = import "./contracts.ncl" in\nlet defaults_lib = import "./defaults.ncl" in\n\n{\n  defaults = defaults_lib,\n\n  make_storage_backup | not_exported = fun overrides =>\n    defaults_lib.storage_backup & overrides,\n\n  make_storage | not_exported = fun overrides =>\n    defaults_lib.storage & overrides,\n\n  make_provision_env | not_exported = fun overrides =>\n    defaults_lib.provision_env & overrides,\n\n  make_provision_upcloud | not_exported = fun overrides =>\n    defaults_lib.provision_upcloud & overrides,\n\n  make_server_defaults_upcloud | not_exported = fun overrides =>\n    defaults_lib.server_defaults_upcloud & overrides,\n\n  make_server_upcloud | not_exported = fun overrides =>\n    defaults_lib.server_upcloud & overrides,\n\n  DefaultStorageBackup = defaults_lib.storage_backup,\n  DefaultStorage = defaults_lib.storage,\n  DefaultProvisionEnv = defaults_lib.provision_env,\n  DefaultProvisionUpcloud = defaults_lib.provision_upcloud,\n  DefaultServerDefaults_upcloud = defaults_lib.server_defaults_upcloud,\n  DefaultServerUpcloud = defaults_lib.server_upcloud,\n}\n```\n\n---\n\n### Example 3: Workspace Infrastructure (wuji)\n\n**File**: `workspace_librecloud/nickel/wuji/main.ncl` (53 lines)\n\n```\nlet settings_config = import "./settings.ncl" in\nlet ts_cilium = import "./taskservs/cilium.ncl" in\nlet ts_containerd = import "./taskservs/containerd.ncl" in\nlet ts_coredns = import "./taskservs/coredns.ncl" in\nlet ts_crio = import "./taskservs/crio.ncl" in\nlet ts_crun = import "./taskservs/crun.ncl" in\nlet ts_etcd = import "./taskservs/etcd.ncl" in\nlet ts_external_nfs = import "./taskservs/external-nfs.ncl" in\nlet ts_k8s_nodejoin = import "./taskservs/k8s-nodejoin.ncl" in\nlet ts_kubernetes = import "./taskservs/kubernetes.ncl" in\nlet ts_mayastor = import "./taskservs/mayastor.ncl" in\nlet ts_os = import "./taskservs/os.ncl" in\nlet ts_podman = import "./taskservs/podman.ncl" in\nlet ts_postgres = import "./taskservs/postgres.ncl" in\nlet ts_proxy = import "./taskservs/proxy.ncl" in\nlet ts_redis = import "./taskservs/redis.ncl" in\nlet ts_resolv = import "./taskservs/resolv.ncl" in\nlet ts_rook_ceph = import "./taskservs/rook_ceph.ncl" in\nlet ts_runc = import "./taskservs/runc.ncl" in\nlet ts_webhook = import "./taskservs/webhook.ncl" in\nlet ts_youki = import "./taskservs/youki.ncl" in\n\n{\n  settings = settings_config.settings,\n  servers = settings_config.servers,\n\n  taskservs = {\n    cilium = ts_cilium.cilium,\n    containerd = ts_containerd.containerd,\n    coredns = ts_coredns.coredns,\n    crio = ts_crio.crio,\n    crun = ts_crun.crun,\n    etcd = ts_etcd.etcd,\n    external_nfs = ts_external_nfs.external_nfs,\n    k8s_nodejoin = ts_k8s_nodejoin.k8s_nodejoin,\n    kubernetes = ts_kubernetes.kubernetes,\n    mayastor = ts_mayastor.mayastor,\n    os = ts_os.os,\n    podman = ts_podman.podman,\n    postgres = ts_postgres.postgres,\n    proxy = ts_proxy.proxy,\n    redis = ts_redis.redis,\n    resolv = ts_resolv.resolv,\n    rook_ceph = ts_rook_ceph.rook_ceph,\n    runc = ts_runc.runc,\n    webhook = ts_webhook.webhook,\n    youki = ts_youki.youki,\n  },\n}\n```\n\n---\n\n## Summary Table\n\n| Aspect | KCL | Nickel | Recommendation |\n| -------- | ----- | -------- | --- |\n| **Learning Curve** | 10 hours | 3 hours | Nickel |\n| **Performance** | Baseline | 60% faster | Nickel |\n| **Flexibility** | Limited | Excellent | Nickel |\n| **Type Safety** | Strong | Good (gradual) | KCL (slightly) |\n| **Extensibility** | Rigid | Excellent | Nickel |\n| **Boilerplate** | High | Low | Nickel |\n| **Ecosystem** | Small | Growing | Nickel |\n| **For New Projects** | ❌ | ✅ | Nickel |\n| **For Legacy Configs** | ✅ Supported | ⏳ Gradual | Both (migrate gradually) |\n\n---\n\n## Key Takeaways\n\n1. **Nickel is the future** - 60% faster, more flexible, simpler mental model\n2. **Three-file pattern** - Cleanly separates contracts, defaults, instances\n3. **Hybrid interface** - 4 levels cover all use cases (90% makers, 9% defaults, 1% contracts)\n4. **Domain organization** - 8 logical domains for clarity and scalability\n5. **Two deployment modes** - Development (fast iteration) + Production (immutable snapshots)\n6. **TypeDialog integration** - Amplifies Nickel beyond IaC (UI generation)\n7. **KCL still supported** - For legacy workspace configs during gradual migration\n8. **Production validated** - 47 active files, 20 taskservs, 422 total schemas\n\n---\n\n**Next Steps**:\n\n- For new schemas → Use Nickel (three-file pattern)\n- For workspace configs → Can migrate gradually\n- For UI generation → Combine Nickel + TypeDialog\n- For application settings → Use TOML (not KCL/Nickel)\n- For K8s/CI-CD → Use YAML (not KCL/Nickel)\n\n---\n\n**Version**: 1.0.0\n**Status**: Complete Reference Guide\n**Last Updated**: 2025-12-15
