# Configuration Rendering Guide

This guide covers the unified configuration rendering system in the CLI daemon that supports Nickel and Tera template engines. KCL support is deprecated.

## Overview

The CLI daemon (`cli-daemon`) provides a high-performance REST API for rendering configurations in multiple formats:

- **Nickel**: Functional configuration language with lazy evaluation and type safety (primary choice)
- **Tera**: Jinja2-compatible template engine (simple templating)
- **KCL**: Type-safe infrastructure configuration language (legacy - deprecated)

All renderers are accessible through a single unified API endpoint with intelligent caching to minimize latency.

## Quick Start

### Starting the Daemon

The daemon runs on port 9091 by default:

```bash
# Start in background
./target/release/cli-daemon &

# Check it's running
curl http://localhost:9091/health
```plaintext

### Simple Nickel Rendering

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "nickel",
    "content": "{ name = \"my-server\", cpu = 4, memory = 8192 }",
    "name": "server-config"
  }'
```plaintext

**Response**:

```json
{
  "rendered": "{ name = \"my-server\", cpu = 4, memory = 8192 }",
  "error": null,
  "language": "nickel",
  "execution_time_ms": 23
}
```plaintext

## REST API Reference

### POST /config/render

Render a configuration in any supported language.

**Request Headers**:

```plaintext
Content-Type: application/json
```plaintext

**Request Body**:

```json
{
  "language": "nickel|tera|kcl",
  "content": "...configuration content...",
  "context": {
    "key1": "value1",
    "key2": 123
  },
  "name": "optional-config-name"
}
```plaintext

**Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `language` | string | Yes | One of: `nickel`, `tera`, `kcl` (deprecated) |
| `content` | string | Yes | The configuration or template content to render |
| `context` | object | No | Variables to pass to the configuration (JSON object) |
| `name` | string | No | Optional name for logging purposes |

**Response** (Success):

```json
{
  "rendered": "...rendered output...",
  "error": null,
  "language": "kcl",
  "execution_time_ms": 23
}
```plaintext

**Response** (Error):

```json
{
  "rendered": null,
  "error": "KCL evaluation failed: undefined variable 'name'",
  "language": "kcl",
  "execution_time_ms": 18
}
```plaintext

**Status Codes**:

- `200 OK` - Rendering completed (check `error` field in body for evaluation errors)
- `400 Bad Request` - Invalid request format
- `500 Internal Server Error` - Daemon error

### GET /config/stats

Get rendering statistics across all languages.

**Response**:

```json
{
  "total_renders": 156,
  "successful_renders": 154,
  "failed_renders": 2,
  "average_time_ms": 28,
  "kcl_renders": 78,
  "nickel_renders": 52,
  "tera_renders": 26,
  "kcl_cache_hits": 68,
  "nickel_cache_hits": 35,
  "tera_cache_hits": 18
}
```plaintext

### POST /config/stats/reset

Reset all rendering statistics.

**Response**:

```json
{
  "status": "success",
  "message": "Configuration rendering statistics reset"
}
```plaintext

## KCL Rendering (Deprecated)

**Note**: KCL is deprecated. Use Nickel for new configurations.

### Basic KCL Configuration

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "kcl",
    "content": "
name = \"production-server\"
type = \"web\"
cpu = 4
memory = 8192
disk = 50

tags = {
    environment = \"production\"
    team = \"platform\"
}
",
    "name": "prod-server-config"
  }'
```plaintext

### KCL with Context Variables

Pass context variables using the `-D` flag syntax internally:

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "kcl",
    "content": "
name = option(\"server_name\", default=\"default-server\")
environment = option(\"env\", default=\"dev\")
cpu = option(\"cpu_count\", default=2)
memory = option(\"memory_mb\", default=2048)
",
    "context": {
      "server_name": "app-server-01",
      "env": "production",
      "cpu_count": 8,
      "memory_mb": 16384
    },
    "name": "server-with-context"
  }'
```plaintext

### Expected KCL Rendering Time

- **First render (cache miss)**: 20-50 ms
- **Cached render (same content)**: 1-5 ms
- **Large configs (100+ variables)**: 50-100 ms

## Nickel Rendering

### Basic Nickel Configuration

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "nickel",
    "content": "{
  name = \"production-server\",
  type = \"web\",
  cpu = 4,
  memory = 8192,
  disk = 50,
  tags = {
    environment = \"production\",
    team = \"platform\"
  }
}",
    "name": "nickel-server-config"
  }'
```plaintext

### Nickel with Lazy Evaluation

Nickel excels at evaluating only what's needed:

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "nickel",
    "content": "{
  server = {
    name = \"db-01\",
    # Expensive computation - only computed if accessed
    health_check = std.array.fold
      (fun acc x => acc + x)
      0
      [1, 2, 3, 4, 5]
  },
  networking = {
    dns_servers = [\"8.8.8.8\", \"8.8.4.4\"],
    firewall_rules = [\"allow_ssh\", \"allow_https\"]
  }
}",
    "context": {
      "only_server": true
    }
  }'
```plaintext

### Expected Nickel Rendering Time

- **First render (cache miss)**: 30-60 ms
- **Cached render (same content)**: 1-5 ms
- **Large configs with lazy evaluation**: 40-80 ms

**Advantage**: Nickel only computes fields that are actually used in the output

## Tera Template Rendering

### Basic Tera Template

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "tera",
    "content": "
Server Configuration
====================

Name: {{ server_name }}
Environment: {{ environment | default(value=\"development\") }}
Type: {{ server_type }}

Assigned Tasks:
{% for task in tasks %}
  - {{ task }}
{% endfor %}

{% if enable_monitoring %}
Monitoring: ENABLED
  - Prometheus: true
  - Grafana: true
{% else %}
Monitoring: DISABLED
{% endif %}
",
    "context": {
      "server_name": "prod-web-01",
      "environment": "production",
      "server_type": "web",
      "tasks": ["kubernetes", "prometheus", "cilium"],
      "enable_monitoring": true
    },
    "name": "server-template"
  }'
```plaintext

### Tera Filters and Functions

Tera supports Jinja2-compatible filters and functions:

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "tera",
    "content": "
Configuration for {{ environment | upper }}
Servers: {{ server_count | default(value=1) }}
Cost estimate: \${{ monthly_cost | round(precision=2) }}

{% for server in servers | reverse %}
- {{ server.name }}: {{ server.cpu }} CPUs
{% endfor %}
",
    "context": {
      "environment": "production",
      "server_count": 5,
      "monthly_cost": 1234.567,
      "servers": [
        {"name": "web-01", "cpu": 4},
        {"name": "db-01", "cpu": 8},
        {"name": "cache-01", "cpu": 2}
      ]
    }
  }'
```plaintext

### Expected Tera Rendering Time

- **Simple templates**: 4-10 ms
- **Complex templates with loops**: 10-20 ms
- **Always fast** (template is pre-compiled)

## Performance Characteristics

### Caching Strategy

All three renderers use LRU (Least Recently Used) caching:

- **Cache Size**: 100 entries per renderer
- **Cache Key**: SHA256 hash of (content + context)
- **Cache Hit**: Typically < 5 ms
- **Cache Miss**: Language-dependent (20-60 ms)

**To maximize cache hits**:

1. Render the same config multiple times → hits after first render
2. Use static content when possible → better cache reuse
3. Monitor cache hit ratio via `/config/stats`

### Benchmarks

Comparison of rendering times (on commodity hardware):

| Scenario | KCL | Nickel | Tera |
|----------|-----|--------|------|
| Simple config (10 vars) | 20 ms | 30 ms | 5 ms |
| Medium config (50 vars) | 35 ms | 45 ms | 8 ms |
| Large config (100+ vars) | 50-100 ms | 50-80 ms | 10 ms |
| Cached render | 1-5 ms | 1-5 ms | 1-5 ms |

### Memory Usage

- Each renderer keeps 100 cached entries in memory
- Average config size in cache: ~5 KB
- Maximum memory per renderer: ~500 KB + overhead

## Error Handling

### Common Errors

#### KCL Binary Not Found

**Error Response**:

```json
{
  "rendered": null,
  "error": "KCL binary not found in PATH. Install KCL or set KCL_PATH environment variable",
  "language": "kcl",
  "execution_time_ms": 0
}
```plaintext

**Solution**:

```bash
# Install KCL
kcl version

# Or set explicit path
export KCL_PATH=/usr/local/bin/kcl
```plaintext

#### Invalid KCL Syntax

**Error Response**:

```json
{
  "rendered": null,
  "error": "KCL evaluation failed: Parse error at line 3: expected '='",
  "language": "kcl",
  "execution_time_ms": 12
}
```plaintext

**Solution**: Verify Nickel syntax. Run `nickel eval file.ncl` directly for better error messages.

#### Missing Context Variable

**Error Response**:

```json
{
  "rendered": null,
  "error": "KCL evaluation failed: undefined variable 'required_var'",
  "language": "kcl",
  "execution_time_ms": 8
}
```plaintext

**Solution**: Provide required context variables or use `option()` with defaults.

#### Invalid JSON in Context

**HTTP Status**: `400 Bad Request`
**Body**: Error message about invalid JSON

**Solution**: Ensure context is valid JSON.

## Integration Examples

### Using with Nushell

```nushell
# Render a Nickel config from Nushell
let config = open workspace/config/provisioning.ncl | into string
let response = curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d $"{{ language: \"nickel\", content: $config }}" | from json

print $response.rendered
```plaintext

### Using with Python

```python
import requests
import json

def render_config(language, content, context=None, name=None):
    payload = {
        "language": language,
        "content": content,
        "context": context or {},
        "name": name
    }

    response = requests.post(
        "http://localhost:9091/config/render",
        json=payload
    )

    return response.json()

# Example usage
result = render_config(
    "nickel",
    '{name = "server", cpu = 4}',
    {"name": "prod-server"},
    "my-config"
)

if result["error"]:
    print(f"Error: {result['error']}")
else:
    print(f"Rendered in {result['execution_time_ms']}ms")
    print(result["rendered"])
```plaintext

### Using with Curl

```bash
#!/bin/bash

# Function to render config
render_config() {
    local language=$1
    local content=$2
    local name=${3:-"unnamed"}

    curl -X POST http://localhost:9091/config/render \
        -H "Content-Type: application/json" \
        -d @- << EOF
{
  "language": "$language",
  "content": $(echo "$content" | jq -Rs .),
  "name": "$name"
}
EOF
}

# Usage
render_config "nickel" "{name = \"my-server\"}"  "server-config"
```plaintext

## Troubleshooting

### Daemon Won't Start

**Check log level**:

```bash
PROVISIONING_LOG_LEVEL=debug ./target/release/cli-daemon
```plaintext

**Verify Nushell binary**:

```bash
which nu
# or set explicit path
NUSHELL_PATH=/usr/local/bin/nu ./target/release/cli-daemon
```plaintext

### Very Slow Rendering

**Check cache hit rate**:

```bash
curl http://localhost:9091/config/stats | jq '.nickel_cache_hits / .nickel_renders'
```plaintext

**If low cache hit rate**: Rendering same configs repeatedly?

**Monitor execution time**:

```bash
curl http://localhost:9091/config/render ... | jq '.execution_time_ms'
```plaintext

### Rendering Hangs

**Set timeout** (depends on client):

```bash
curl --max-time 10 -X POST http://localhost:9091/config/render ...
```plaintext

**Check daemon logs** for stuck processes.

### Out of Memory

**Reduce cache size** (rebuild with modified config) or restart daemon.

## Best Practices

1. **Choose right language for task**:
   - KCL: Familiar, type-safe, use if already in ecosystem
   - Nickel: Large configs with lazy evaluation needs
   - Tera: Simple templating, fastest

2. **Use context variables** instead of hardcoding values:

   ```json
   "context": {
     "environment": "production",
     "replica_count": 3
   }
   ```

1. **Monitor statistics** to understand performance:

   ```bash
   watch -n 1 'curl -s http://localhost:9091/config/stats | jq'
   ```

2. **Cache warming**: Pre-render common configs on startup

3. **Error handling**: Always check `error` field in response

## See Also

- [KCL Documentation](https://www.kcl-lang.io/docs/user_docs/getting-started/intro)
- [Nickel User Manual](https://nickel-lang.org/user-manual/introduction/)
- [Tera Template Engine](https://keats.github.io/tera/)
- CLI Daemon Architecture: `provisioning/platform/cli-daemon/README.md`

---

## Quick Reference

### API Endpoint

```plaintext
POST http://localhost:9091/config/render
```plaintext

### Request Template

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "kcl|nickel|tera",
    "content": "...",
    "context": {...},
    "name": "optional-name"
  }'
```plaintext

### Quick Examples

#### KCL - Simple Config

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "kcl",
    "content": "name = \"server\"\ncpu = 4\nmemory = 8192"
  }'
```plaintext

#### KCL - With Context

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "kcl",
    "content": "name = option(\"server_name\")\nenvironment = option(\"env\", default=\"dev\")",
    "context": {"server_name": "prod-01", "env": "production"}
  }'
```plaintext

#### Nickel - Simple Config

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "nickel",
    "content": "{name = \"server\", cpu = 4, memory = 8192}"
  }'
```plaintext

#### Tera - Template with Loops

```bash
curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d '{
    "language": "tera",
    "content": "{% for task in tasks %}{{ task }}\n{% endfor %}",
    "context": {"tasks": ["kubernetes", "postgres", "redis"]}
  }'
```plaintext

### Statistics

```bash
# Get stats
curl http://localhost:9091/config/stats

# Reset stats
curl -X POST http://localhost:9091/config/stats/reset

# Watch stats in real-time
watch -n 1 'curl -s http://localhost:9091/config/stats | jq'
```plaintext

### Performance Guide

| Language | Cold | Cached | Use Case |
|----------|------|--------|----------|
| **KCL** | 20-50 ms | 1-5 ms | Type-safe infrastructure configs |
| **Nickel** | 30-60 ms | 1-5 ms | Large configs, lazy evaluation |
| **Tera** | 5-20 ms | 1-5 ms | Simple templating |

### Status Codes

| Code | Meaning |
|------|---------|
| 200 | Success (check `error` field for evaluation errors) |
| 400 | Invalid request |
| 500 | Daemon error |

### Response Fields

```json
{
  "rendered": "...output or null on error",
  "error": "...error message or null on success",
  "language": "kcl|nickel|tera",
  "execution_time_ms": 23
}
```plaintext

### Languages Comparison

#### KCL

```kcl
name = "server"
type = "web"
cpu = 4
memory = 8192

tags = {
    env = "prod"
    team = "platform"
}
```plaintext

**Pros**: Familiar syntax, type-safe, existing patterns
**Cons**: Eager evaluation, verbose for simple cases

#### Nickel

```nickel
{
  name = "server",
  type = "web",
  cpu = 4,
  memory = 8192,
  tags = {
    env = "prod",
    team = "platform"
  }
}
```plaintext

**Pros**: Lazy evaluation, functional style, compact
**Cons**: Different paradigm, smaller ecosystem

#### Tera

```jinja2
Server: {{ name }}
Type: {{ type | upper }}
{% for tag_name, tag_value in tags %}
- {{ tag_name }}: {{ tag_value }}
{% endfor %}
```plaintext

**Pros**: Fast, simple, familiar template syntax
**Cons**: No validation, template-only

### Caching

**How it works**: SHA256(content + context) → cached result

**Cache hit**: < 5 ms
**Cache miss**: 20-60 ms (language dependent)
**Cache size**: 100 entries per language

**Cache stats**:

```bash
curl -s http://localhost:9091/config/stats | jq '{
  kcl_cache_hits: .kcl_cache_hits,
  kcl_renders: .kcl_renders,
  kcl_hit_ratio: (.kcl_cache_hits / .kcl_renders * 100)
}'
```plaintext

### Common Tasks

#### Batch Rendering

```bash
#!/bin/bash
for config in configs/*.ncl; do
  curl -X POST http://localhost:9091/config/render \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg content \"$(cat $config)\" \
      '{language: "nickel", content: $content}')"
done
```plaintext

#### Validate Before Rendering

```bash
# Nickel validation
nickel typecheck my-config.ncl

# Daemon validation (via first render)
curl ... # catches errors in response
```plaintext

#### Monitor Cache Performance

```bash
#!/bin/bash
while true; do
  STATS=$(curl -s http://localhost:9091/config/stats)
  HIT_RATIO=$( echo "$STATS" | jq '.nickel_cache_hits / .nickel_renders * 100')
  echo "Cache hit ratio: ${HIT_RATIO}%"
  sleep 5
done
```plaintext

### Error Examples

#### Missing Binary

```json
{
  "error": "Nickel binary not found. Install Nickel or set NICKEL_PATH",
  "rendered": null
}
```plaintext

**Fix**: `export NICKEL_PATH=/path/to/nickel` or install Nickel

#### Syntax Error

```json
{
  "error": "Nickel type checking failed: Type mismatch at line 3",
  "rendered": null
}
```plaintext

**Fix**: Check Nickel syntax, run `nickel typecheck file.ncl` directly

#### Missing Variable

```json
{
  "error": "Nickel evaluation failed: undefined variable 'name'",
  "rendered": null
}
```plaintext

**Fix**: Provide in `context` or define as optional field with default

### Integration Quick Start

#### Nushell

```nushell
use lib_provisioning

let config = open server.ncl | into string
let result = (curl -X POST http://localhost:9091/config/render \
  -H "Content-Type: application/json" \
  -d {language: "nickel", content: $config} | from json)

if ($result.error != null) {
  error $result.error
} else {
  print $result.rendered
}
```plaintext

#### Python

```python
import requests

resp = requests.post("http://localhost:9091/config/render", json={
    "language": "nickel",
    "content": '{name = "server"}',
    "context": {}
})
result = resp.json()
print(result["rendered"] if not result["error"] else f"Error: {result['error']}")
```plaintext

#### Bash

```bash
render() {
  curl -s -X POST http://localhost:9091/config/render \
    -H "Content-Type: application/json" \
    -d "$1" | jq '.'
}

# Usage
render '{"language":"nickel","content":"{name = \"server\"}"}'
```plaintext

### Environment Variables

```bash
# Daemon configuration
PROVISIONING_LOG_LEVEL=debug        # Log level
DAEMON_BIND=127.0.0.1:9091         # Bind address
NUSHELL_PATH=/usr/local/bin/nu      # Nushell binary
NICKEL_PATH=/usr/local/bin/nickel   # Nickel binary
```plaintext

### Useful Commands

```bash
# Health check
curl http://localhost:9091/health

# Daemon info
curl http://localhost:9091/info

# View stats
curl http://localhost:9091/config/stats | jq '.'

# Pretty print stats
curl -s http://localhost:9091/config/stats | jq '{
  total: .total_renders,
  success_rate: (.successful_renders / .total_renders * 100),
  avg_time: .average_time_ms,
  cache_hit_rate: ((.nickel_cache_hits + .tera_cache_hits) / (.nickel_renders + .tera_renders) * 100)
}'
```plaintext

### Troubleshooting Checklist

- [ ] Daemon running? `curl http://localhost:9091/health`
- [ ] Correct content for language?
- [ ] Valid JSON in context?
- [ ] Binary available? (KCL/Nickel)
- [ ] Check log level? `PROVISIONING_LOG_LEVEL=debug`
- [ ] Cache hit rate? `/config/stats`
- [ ] Error in response? Check `error` field
