# Configuration Rendering Guide\n\nThis guide covers the unified configuration rendering system in the CLI daemon that supports Nickel and Tera template engines.\n\n## Overview\n\nThe CLI daemon (`cli-daemon`) provides a high-performance REST API for rendering configurations in multiple formats:\n\n- **Nickel**: Functional configuration language with lazy evaluation and type safety (primary choice)\n- **Tera**: Jinja2-compatible template engine (simple templating)\n\nAll renderers are accessible through a single unified API endpoint with intelligent caching to minimize latency.\n\n## Quick Start\n\n### Starting the Daemon\n\nThe daemon runs on port 9091 by default:\n\n```\n# Start in background\n./target/release/cli-daemon &\n\n# Check it's running\ncurl http://localhost:9091/health\n```\n\n### Simple Nickel Rendering\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "nickel",\n    "content": "{ name = \"my-server\", cpu = 4, memory = 8192 }",\n    "name": "server-config"\n  }'\n```\n\n**Response**:\n\n```\n{\n  "rendered": "{ name = \"my-server\", cpu = 4, memory = 8192 }",\n  "error": null,\n  "language": "nickel",\n  "execution_time_ms": 23\n}\n```\n\n## REST API Reference\n\n### POST /config/render\n\nRender a configuration in any supported language.\n\n**Request Headers**:\n\n```\nContent-Type: application/json\n```\n\n**Request Body**:\n\n```\n{\n  "language": "nickel|tera",\n  "content": "...configuration content...",\n  "context": {\n    "key1": "value1",\n    "key2": 123\n  },\n  "name": "optional-config-name"\n}\n```\n\n**Parameters**:\n\n| Parameter | Type | Required | Description |\n| ----------- | ------ | ---------- | ------------- |\n| `language` | string | Yes | One of: `nickel`, `tera` |\n| `content` | string | Yes | The configuration or template content to render |\n| `context` | object | No | Variables to pass to the configuration (JSON object) |\n| `name` | string | No | Optional name for logging purposes |\n\n**Response** (Success):\n\n```\n{\n  "rendered": "...rendered output...",\n  "error": null,\n  "language": "nickel",\n  "execution_time_ms": 23\n}\n```\n\n**Response** (Error):\n\n```\n{\n  "rendered": null,\n  "error": "Nickel evaluation failed: undefined variable 'name'",\n  "language": "nickel",\n  "execution_time_ms": 18\n}\n```\n\n**Status Codes**:\n\n- `200 OK` - Rendering completed (check `error` field in body for evaluation errors)\n- `400 Bad Request` - Invalid request format\n- `500 Internal Server Error` - Daemon error\n\n### GET /config/stats\n\nGet rendering statistics across all languages.\n\n**Response**:\n\n```\n{\n  "total_renders": 156,\n  "successful_renders": 154,\n  "failed_renders": 2,\n  "average_time_ms": 28,\n  "nickel_renders": 104,\n  "tera_renders": 52,\n  "nickel_cache_hits": 87,\n  "tera_cache_hits": 38\n}\n```\n\n### POST /config/stats/reset\n\nReset all rendering statistics.\n\n**Response**:\n\n```\n{\n  "status": "success",\n  "message": "Configuration rendering statistics reset"\n}\n```\n\n## Nickel Rendering\n\n### Basic Nickel Configuration\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "nickel",\n    "content": "{\n  name = \"production-server\",\n  type = \"web\",\n  cpu = 4,\n  memory = 8192,\n  disk = 50,\n  tags = {\n    environment = \"production\",\n    team = \"platform\"\n  }\n}",\n    "name": "nickel-server-config"\n  }'\n```\n\n### Nickel with Lazy Evaluation\n\nNickel excels at evaluating only what's needed:\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "nickel",\n    "content": "{\n  server = {\n    name = \"db-01\",\n    # Expensive computation - only computed if accessed\n    health_check = std.array.fold\n      (fun acc x => acc + x)\n      0\n      [1, 2, 3, 4, 5]\n  },\n  networking = {\n    dns_servers = [\"8.8.8.8\", \"8.8.4.4\"],\n    firewall_rules = [\"allow_ssh\", \"allow_https\"]\n  }\n}",\n    "context": {\n      "only_server": true\n    }\n  }'\n```\n\n### Expected Nickel Rendering Time\n\n- **First render (cache miss)**: 30-60 ms\n- **Cached render (same content)**: 1-5 ms\n- **Large configs with lazy evaluation**: 40-80 ms\n\n**Advantage**: Nickel only computes fields that are actually used in the output\n\n## Tera Template Rendering\n\n### Basic Tera Template\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "tera",\n    "content": "\nServer Configuration\n====================\n\nName: {{ server_name }}\nEnvironment: {{ environment | default(value=\"development\") }}\nType: {{ server_type }}\n\nAssigned Tasks:\n{% for task in tasks %}\n  - {{ task }}\n{% endfor %}\n\n{% if enable_monitoring %}\nMonitoring: ENABLED\n  - Prometheus: true\n  - Grafana: true\n{% else %}\nMonitoring: DISABLED\n{% endif %}\n",\n    "context": {\n      "server_name": "prod-web-01",\n      "environment": "production",\n      "server_type": "web",\n      "tasks": ["kubernetes", "prometheus", "cilium"],\n      "enable_monitoring": true\n    },\n    "name": "server-template"\n  }'\n```\n\n### Tera Filters and Functions\n\nTera supports Jinja2-compatible filters and functions:\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "tera",\n    "content": "\nConfiguration for {{ environment | upper }}\nServers: {{ server_count | default(value=1) }}\nCost estimate: \${{ monthly_cost | round(precision=2) }}\n\n{% for server in servers | reverse %}\n- {{ server.name }}: {{ server.cpu }} CPUs\n{% endfor %}\n",\n    "context": {\n      "environment": "production",\n      "server_count": 5,\n      "monthly_cost": 1234.567,\n      "servers": [\n        {"name": "web-01", "cpu": 4},\n        {"name": "db-01", "cpu": 8},\n        {"name": "cache-01", "cpu": 2}\n      ]\n    }\n  }'\n```\n\n### Expected Tera Rendering Time\n\n- **Simple templates**: 4-10 ms\n- **Complex templates with loops**: 10-20 ms\n- **Always fast** (template is pre-compiled)\n\n## Performance Characteristics\n\n### Caching Strategy\n\nAll three renderers use LRU (Least Recently Used) caching:\n\n- **Cache Size**: 100 entries per renderer\n- **Cache Key**: SHA256 hash of (content + context)\n- **Cache Hit**: Typically < 5 ms\n- **Cache Miss**: Language-dependent (20-60 ms)\n\n**To maximize cache hits**:\n\n1. Render the same config multiple times → hits after first render\n2. Use static content when possible → better cache reuse\n3. Monitor cache hit ratio via `/config/stats`\n\n### Benchmarks\n\nComparison of rendering times (on commodity hardware):\n\n| Scenario | Nickel | Tera |\n| ---------- | -------- | ------ |\n| Simple config (10 vars) | 30 ms | 5 ms |\n| Medium config (50 vars) | 45 ms | 8 ms |\n| Large config (100+ vars) | 50-80 ms | 10 ms |\n| Cached render | 1-5 ms | 1-5 ms |\n\n### Memory Usage\n\n- Each renderer keeps 100 cached entries in memory\n- Average config size in cache: ~5 KB\n- Maximum memory per renderer: ~500 KB + overhead\n\n## Error Handling\n\n### Common Errors\n\n#### Nickel Binary Not Found\n\n**Error Response**:\n\n```\n{\n  "rendered": null,\n  "error": "Nickel binary not found in PATH. Install Nickel or set NICKEL_PATH environment variable",\n  "language": "nickel",\n  "execution_time_ms": 0\n}\n```\n\n**Solution**:\n\n```\n# Install Nickel\nnickel version\n\n# Or set explicit path\nexport NICKEL_PATH=/usr/local/bin/nickel\n```\n\n#### Invalid Nickel Syntax\n\n**Error Response**:\n\n```\n{\n  "rendered": null,\n  "error": "Nickel evaluation failed: Type mismatch at line 3: expected String, got Number",\n  "language": "nickel",\n  "execution_time_ms": 12\n}\n```\n\n**Solution**: Verify Nickel syntax. Run `nickel typecheck file.ncl` directly for better error messages.\n\n#### Missing Context Variable\n\n**Error Response**:\n\n```\n{\n  "rendered": null,\n  "error": "Nickel evaluation failed: undefined variable 'required_var'",\n  "language": "nickel",\n  "execution_time_ms": 8\n}\n```\n\n**Solution**: Provide required context variables or define fields with default values.\n\n#### Invalid JSON in Context\n\n**HTTP Status**: `400 Bad Request`\n**Body**: Error message about invalid JSON\n\n**Solution**: Ensure context is valid JSON.\n\n## Integration Examples\n\n### Using with Nushell\n\n```\n# Render a Nickel config from Nushell\nlet config = open workspace/config/provisioning.ncl | into string\nlet response = curl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d $"{{ language: \"nickel\", content: $config }}" | from json\n\nprint $response.rendered\n```\n\n### Using with Python\n\n```\nimport requests\nimport json\n\ndef render_config(language, content, context=None, name=None):\n    payload = {\n        "language": language,\n        "content": content,\n        "context": context or {},\n        "name": name\n    }\n\n    response = requests.post(\n        "http://localhost:9091/config/render",\n        json=payload\n    )\n\n    return response.json()\n\n# Example usage\nresult = render_config(\n    "nickel",\n    '{name = "server", cpu = 4}',\n    {"name": "prod-server"},\n    "my-config"\n)\n\nif result["error"]:\n    print(f"Error: {result['error']}")\nelse:\n    print(f"Rendered in {result['execution_time_ms']}ms")\n    print(result["rendered"])\n```\n\n### Using with Curl\n\n```\n#!/bin/bash\n\n# Function to render config\nrender_config() {\n    local language=$1\n    local content=$2\n    local name=${3:-"unnamed"}\n\n    curl -X POST http://localhost:9091/config/render \\n        -H "Content-Type: application/json" \\n        -d @- << EOF\n{\n  "language": "$language",\n  "content": $(echo "$content" | jq -Rs .),\n  "name": "$name"\n}\nEOF\n}\n\n# Usage\nrender_config "nickel" "{name = \"my-server\"}"  "server-config"\n```\n\n## Troubleshooting\n\n### Daemon Won't Start\n\n**Check log level**:\n\n```\nPROVISIONING_LOG_LEVEL=debug ./target/release/cli-daemon\n```\n\n**Verify Nushell binary**:\n\n```\nwhich nu\n# or set explicit path\nNUSHELL_PATH=/usr/local/bin/nu ./target/release/cli-daemon\n```\n\n### Very Slow Rendering\n\n**Check cache hit rate**:\n\n```\ncurl http://localhost:9091/config/stats | jq '.nickel_cache_hits / .nickel_renders'\n```\n\n**If low cache hit rate**: Rendering same configs repeatedly?\n\n**Monitor execution time**:\n\n```\ncurl http://localhost:9091/config/render ... | jq '.execution_time_ms'\n```\n\n### Rendering Hangs\n\n**Set timeout** (depends on client):\n\n```\ncurl --max-time 10 -X POST http://localhost:9091/config/render ...\n```\n\n**Check daemon logs** for stuck processes.\n\n### Out of Memory\n\n**Reduce cache size** (rebuild with modified config) or restart daemon.\n\n## Best Practices\n\n1. **Choose right language for task**:\n   - Nickel: Large configs with lazy evaluation, type-safe infrastructure definitions\n   - Tera: Simple templating, fastest for rendering\n\n2. **Use context variables** instead of hardcoding values:\n\n   ```json\n   "context": {\n     "environment": "production",\n     "replica_count": 3\n   }\n   ```\n\n1. **Monitor statistics** to understand performance:\n\n   ```bash\n   watch -n 1 'curl -s http://localhost:9091/config/stats | jq'\n   ```\n\n2. **Cache warming**: Pre-render common configs on startup\n\n3. **Error handling**: Always check `error` field in response\n\n## See Also\n\n- [Nickel User Manual](https://nickel-lang.org/user-manual/introduction/)\n- [Tera Template Engine](https://keats.github.io/tera/)\n- CLI Daemon Architecture: `provisioning/platform/cli-daemon/README.md`\n\n---\n\n## Quick Reference\n\n### API Endpoint\n\n```\nPOST http://localhost:9091/config/render\n```\n\n### Request Template\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "nickel|tera",\n    "content": "...",\n    "context": {...},\n    "name": "optional-name"\n  }'\n```\n\n### Quick Examples\n\n#### Nickel - Simple Config\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "nickel",\n    "content": "{name = \"server\", cpu = 4, memory = 8192}"\n  }'\n```\n\n#### Tera - Template with Loops\n\n```\ncurl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d '{\n    "language": "tera",\n    "content": "{% for task in tasks %}{{ task }}\n{% endfor %}",\n    "context": {"tasks": ["kubernetes", "postgres", "redis"]}\n  }'\n```\n\n### Statistics\n\n```\n# Get stats\ncurl http://localhost:9091/config/stats\n\n# Reset stats\ncurl -X POST http://localhost:9091/config/stats/reset\n\n# Watch stats in real-time\nwatch -n 1 'curl -s http://localhost:9091/config/stats | jq'\n```\n\n### Performance Guide\n\n| Language | Cold | Cached | Use Case |\n| ---------- | ------ | -------- | ---------- |\n| **Nickel** | 30-60 ms | 1-5 ms | Type-safe configs, lazy evaluation |\n| **Tera** | 5-20 ms | 1-5 ms | Simple templating |\n\n### Status Codes\n\n| Code | Meaning |\n| ------ | --------- |\n| 200 | Success (check `error` field for evaluation errors) |\n| 400 | Invalid request |\n| 500 | Daemon error |\n\n### Response Fields\n\n```\n{\n  "rendered": "...output or null on error",\n  "error": "...error message or null on success",\n  "language": "nickel|tera",\n  "execution_time_ms": 23\n}\n```\n\n### Languages Comparison\n\n#### Nickel\n\n```\n{\n  name = "server",\n  type = "web",\n  cpu = 4,\n  memory = 8192,\n  tags = {\n    env = "prod",\n    team = "platform"\n  }\n}\n```\n\n**Pros**: Lazy evaluation, functional style, compact\n**Cons**: Different paradigm, smaller ecosystem\n\n#### Tera\n\n```jinja2\nServer: {{ name }}\nType: {{ type | upper }}\n{% for tag_name, tag_value in tags %}\n- {{ tag_name }}: {{ tag_value }}\n{% endfor %}\n```\n\n**Pros**: Fast, simple, familiar template syntax\n**Cons**: No validation, template-only\n\n### Caching\n\n**How it works**: SHA256(content + context) → cached result\n\n**Cache hit**: < 5 ms\n**Cache miss**: 20-60 ms (language dependent)\n**Cache size**: 100 entries per language\n\n**Cache stats**:\n\n```\ncurl -s http://localhost:9091/config/stats | jq '{\n  nickel_cache_hits: .nickel_cache_hits,\n  nickel_renders: .nickel_renders,\n  nickel_hit_ratio: (.nickel_cache_hits / .nickel_renders * 100)\n}'\n```\n\n### Common Tasks\n\n#### Batch Rendering\n\n```\n#!/bin/bash\nfor config in configs/*.ncl; do\n  curl -X POST http://localhost:9091/config/render \\n    -H "Content-Type: application/json" \\n    -d "$(jq -n --arg content \"$(cat $config)\" \\n      '{language: "nickel", content: $content}')"\ndone\n```\n\n#### Validate Before Rendering\n\n```\n# Nickel validation\nnickel typecheck my-config.ncl\n\n# Daemon validation (via first render)\ncurl ... # catches errors in response\n```\n\n#### Monitor Cache Performance\n\n```\n#!/bin/bash\nwhile true; do\n  STATS=$(curl -s http://localhost:9091/config/stats)\n  HIT_RATIO=$( echo "$STATS" | jq '.nickel_cache_hits / .nickel_renders * 100')\n  echo "Cache hit ratio: ${HIT_RATIO}%"\n  sleep 5\ndone\n```\n\n### Error Examples\n\n#### Missing Binary\n\n```\n{\n  "error": "Nickel binary not found. Install Nickel or set NICKEL_PATH",\n  "rendered": null\n}\n```\n\n**Fix**: `export NICKEL_PATH=/path/to/nickel` or install Nickel\n\n#### Syntax Error\n\n```\n{\n  "error": "Nickel type checking failed: Type mismatch at line 3",\n  "rendered": null\n}\n```\n\n**Fix**: Check Nickel syntax, run `nickel typecheck file.ncl` directly\n\n### Integration Quick Start\n\n#### Nushell\n\n```\nuse lib_provisioning\n\nlet config = open server.ncl | into string\nlet result = (curl -X POST http://localhost:9091/config/render \\n  -H "Content-Type: application/json" \\n  -d {language: "nickel", content: $config} | from json)\n\nif ($result.error != null) {\n  error $result.error\n} else {\n  print $result.rendered\n}\n```\n\n#### Python\n\n```\nimport requests\n\nresp = requests.post("http://localhost:9091/config/render", json={\n    "language": "nickel",\n    "content": '{name = "server"}',\n    "context": {}\n})\nresult = resp.json()\nprint(result["rendered"] if not result["error"] else f"Error: {result['error']}")\n```\n\n#### Bash\n\n```\nrender() {\n  curl -s -X POST http://localhost:9091/config/render \\n    -H "Content-Type: application/json" \\n    -d "$1" | jq '.'\n}\n\n# Usage\nrender '{"language":"nickel","content":"{name = \"server\"}"}'\n```\n\n### Environment Variables\n\n```\n# Daemon configuration\nPROVISIONING_LOG_LEVEL=debug        # Log level\nDAEMON_BIND=127.0.0.1:9091         # Bind address\nNUSHELL_PATH=/usr/local/bin/nu      # Nushell binary\nNICKEL_PATH=/usr/local/bin/nickel   # Nickel binary\n```\n\n### Useful Commands\n\n```\n# Health check\ncurl http://localhost:9091/health\n\n# Daemon info\ncurl http://localhost:9091/info\n\n# View stats\ncurl http://localhost:9091/config/stats | jq '.'\n\n# Pretty print stats\ncurl -s http://localhost:9091/config/stats | jq '{\n  total: .total_renders,\n  success_rate: (.successful_renders / .total_renders * 100),\n  avg_time: .average_time_ms,\n  cache_hit_rate: ((.nickel_cache_hits + .tera_cache_hits) / (.nickel_renders + .tera_renders) * 100)\n}'\n```\n\n### Troubleshooting Checklist\n\n- [ ] Daemon running? `curl http://localhost:9091/health`\n- [ ] Correct content for language?\n- [ ] Valid JSON in context?\n- [ ] Nickel or Tera binary available?\n- [ ] Check log level? `PROVISIONING_LOG_LEVEL=debug`\n- [ ] Cache hit rate? `/config/stats`\n- [ ] Error in response? Check `error` field
