boxmux 0.240.3373

YAML-driven terminal UI framework for rich, interactive CLI applications and dashboards with PTY support
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
---
title: Variable System Guide
description: Complete guide to BoxMux variable system - hierarchical variables, environment integration, template-driven configuration, and dynamic deployments
---


## Table of Contents

- [Overview]#overview
- [Variable Syntax]#variable-syntax
- [Hierarchical Precedence]#hierarchical-precedence
- [Practical Examples]#practical-examples
- [Advanced Patterns]#advanced-patterns
- [Best Practices]#best-practices
- [Troubleshooting]#troubleshooting

## Overview

BoxMux variables provide these capabilities:

- Create reusable configurations that adapt to different environments
- Reduce duplication through hierarchical inheritance
- Enable template-driven deployments with dynamic content
- Integrate with existing environment variables
- Provide fallback values for robust configuration management

## Variable Syntax

### Basic Patterns

```yaml
# Standard variable substitution
title: '${VARIABLE_NAME}'

# Variable with default value
content: '${DATABASE_HOST:localhost}'

# Variable with empty default
script: ['echo "Value: ${OPTIONAL_VAR:}"']

# Legacy environment variable support
command: '$HOME/scripts/deploy.sh'
```

### Supported Fields

Variables work in all string and string array fields:

```yaml
- id: 'dynamic_box'
  title: '${SERVICE_NAME} Monitor'          # Box titles
  content: 'Status: ${SERVICE_STATUS}'      # Box content
  script:                                   # Script commands
    - 'systemctl status ${SERVICE_NAME}'
    - 'journalctl -u ${SERVICE_NAME} -n 10'
  redirect_output: '${SERVICE_NAME}_logs'   # Output redirection
  choices:
    - content: 'Restart ${SERVICE_NAME}'    # Choice labels
      script: ['systemctl restart ${SERVICE_NAME}'] # Choice scripts
```

## Hierarchical Precedence

Variables are resolved in strict hierarchical order, allowing fine-grained control:

### Precedence Order (Highest to Lowest)

1. **Box-specific variables** - Most granular control
2. **Parent box variables** - Inherited through box hierarchy
3. **Layout-level variables** - Layout scope (future enhancement)
4. **Application-global variables** - App-wide scope
5. **Environment variables** - System fallback
6. **Default values** - Built-in fallbacks

### Inheritance Example

```yaml
app:
  variables:
    ENVIRONMENT: "production"        # App-level: available everywhere
    DEFAULT_PORT: "8080"
    
  layouts:
    - id: 'services'
      children:
        - id: 'web_tier'
          variables:
            TIER: "frontend"         # Parent level: inherited by children
            DEFAULT_PORT: "80"       # Overrides app-level DEFAULT_PORT
          children:
            - id: 'nginx'
              variables:
                SERVICE: "nginx"     # Child level: highest precedence
                PORT: "443"          # Overrides parent DEFAULT_PORT
              title: '${SERVICE} (${TIER}) - ${ENVIRONMENT}'
              # Resolves to: "nginx (frontend) - production"
              script:
                - 'echo "Starting ${SERVICE} on port ${PORT:${DEFAULT_PORT}}"'
                # Resolves to: "Starting nginx on port 443"
                
            - id: 'apache'
              variables:
                SERVICE: "apache2"
              title: '${SERVICE} (${TIER}) - ${ENVIRONMENT}'
              # Resolves to: "apache2 (frontend) - production"
              script:
                - 'echo "Starting ${SERVICE} on port ${PORT:${DEFAULT_PORT}}"'
                # Resolves to: "Starting apache2 on port 80" (uses parent DEFAULT_PORT)
```

## Practical Examples

### Environment-Specific Configuration

Create a single configuration that works across multiple environments:

```yaml
app:
  variables:
    # Override these via environment variables for different deployments
    ENVIRONMENT: "development"
    API_BASE_URL: "http://localhost:3000"
    DATABASE_URL: "postgres://localhost:5432/myapp_dev"
    LOG_LEVEL: "debug"
    
  layouts:
    - id: 'deployment_status'
      title: 'Deployment Status - ${ENVIRONMENT}'
      children:
        - id: 'api_health'
          variables:
            SERVICE_NAME: "API Gateway"
          title: '${SERVICE_NAME} Health Check'
          script:
            - 'echo "Environment: ${ENVIRONMENT}"'
            - 'echo "Checking API at: ${API_BASE_URL}"'
            - 'curl -f ${API_BASE_URL}/health || echo "API Down"'
            
        - id: 'database_status'
          variables:
            SERVICE_NAME: "Database"
          title: '${SERVICE_NAME} Connection'
          script:
            - 'echo "Testing connection to: ${DATABASE_URL}"'
            - 'pg_isready -d "${DATABASE_URL}" && echo "Connected" || echo "Failed"'
```

**Deploy to different environments:**

```bash
# Development
./boxmux my-config.yaml

# Staging
ENVIRONMENT="staging" API_BASE_URL="https://api-staging.company.com" \
DATABASE_URL="postgres://staging-db:5432/myapp" ./boxmux my-config.yaml

# Production
ENVIRONMENT="production" API_BASE_URL="https://api.company.com" \
DATABASE_URL="postgres://prod-db:5432/myapp" LOG_LEVEL="info" ./boxmux my-config.yaml
```

### Multi-Service Monitoring Dashboard

```yaml
app:
  variables:
    MONITORING_USER: "monitor"
    SSH_KEY_PATH: "~/.ssh/monitoring_key"
    LOG_RETENTION_DAYS: "7"
    
  layouts:
    - id: 'infrastructure_overview'
      title: 'Infrastructure Monitoring'
      children:
        # Web servers section
        - id: 'web_servers'
          variables:
            SERVER_TYPE: "web"
            DEFAULT_PORT: "80"
          children:
            - id: 'web1'
              variables:
                HOSTNAME: "web1.company.com"
                SERVICE: "nginx"
                PORT: "443"
              title: '${SERVICE}@${HOSTNAME}:${PORT}'
              script:
                - 'ssh -i ${SSH_KEY_PATH} ${MONITORING_USER}@${HOSTNAME} "systemctl is-active ${SERVICE}"'
                - 'ssh -i ${SSH_KEY_PATH} ${MONITORING_USER}@${HOSTNAME} "ss -tulpn | grep :${PORT}"'
                
            - id: 'web2'
              variables:
                HOSTNAME: "web2.company.com"
                SERVICE: "apache2"
                # PORT not defined, will use parent DEFAULT_PORT (80)
              title: '${SERVICE}@${HOSTNAME}:${PORT:${DEFAULT_PORT}}'
              script:
                - 'ssh -i ${SSH_KEY_PATH} ${MONITORING_USER}@${HOSTNAME} "systemctl is-active ${SERVICE}"'
                - 'ssh -i ${SSH_KEY_PATH} ${MONITORING_USER}@${HOSTNAME} "ss -tulpn | grep :${PORT:${DEFAULT_PORT}}"'
                
        # Database servers section
        - id: 'database_servers'
          variables:
            SERVER_TYPE: "database"
            DEFAULT_PORT: "5432"
          children:
            - id: 'db_primary'
              variables:
                HOSTNAME: "db1.company.com"
                ROLE: "primary"
                SERVICE: "postgresql"
              title: '${SERVICE} ${ROLE}@${HOSTNAME}'
              script:
                - 'ssh -i ${SSH_KEY_PATH} ${MONITORING_USER}@${HOSTNAME} "sudo -u postgres psql -c \"SELECT version();\""'
                - 'ssh -i ${SSH_KEY_PATH} ${MONITORING_USER}@${HOSTNAME} "sudo -u postgres psql -c \"SELECT pg_is_in_recovery();\""'
```

### Template-Driven Deployment Pipeline

```yaml
app:
  variables:
    DEPLOYMENT_BRANCH: "main"
    DOCKER_REGISTRY: "registry.company.com"
    NAMESPACE: "default"
    REPLICAS: "2"
    
  layouts:
    - id: 'deployment_pipeline'
      title: 'Deployment Pipeline - ${DEPLOYMENT_BRANCH}'
      children:
        - id: 'build_stage'
          variables:
            STAGE: "build"
            IMAGE_TAG: "${DEPLOYMENT_BRANCH}-${BUILD_ID:latest}"
          title: '${STAGE} Stage'
          script:
            - 'echo "Building from branch: ${DEPLOYMENT_BRANCH}"'
            - 'git checkout ${DEPLOYMENT_BRANCH}'
            - 'docker build -t ${DOCKER_REGISTRY}/myapp:${IMAGE_TAG} .'
            - 'docker push ${DOCKER_REGISTRY}/myapp:${IMAGE_TAG}'
            
        - id: 'deploy_frontend'
          variables:
            COMPONENT: "frontend"
            PORT: "3000"
            HEALTH_PATH: "/"
          title: 'Deploy ${COMPONENT}'
          script:
            - 'echo "Deploying ${COMPONENT} to ${NAMESPACE}"'
            - 'kubectl set image deployment/${COMPONENT} ${COMPONENT}=${DOCKER_REGISTRY}/myapp:${IMAGE_TAG:latest} -n ${NAMESPACE}'
            - 'kubectl scale deployment/${COMPONENT} --replicas=${REPLICAS} -n ${NAMESPACE}'
            - 'kubectl rollout status deployment/${COMPONENT} -n ${NAMESPACE}'
            - 'echo "Health check: http://${COMPONENT}:${PORT}${HEALTH_PATH}"'
            
        - id: 'deploy_backend'
          variables:
            COMPONENT: "backend"
            PORT: "8080"
            HEALTH_PATH: "/api/health"
          title: 'Deploy ${COMPONENT}'
          script:
            - 'echo "Deploying ${COMPONENT} to ${NAMESPACE}"'
            - 'kubectl set image deployment/${COMPONENT} ${COMPONENT}=${DOCKER_REGISTRY}/myapp:${IMAGE_TAG:latest} -n ${NAMESPACE}'
            - 'kubectl scale deployment/${COMPONENT} --replicas=${REPLICAS} -n ${NAMESPACE}'
            - 'kubectl rollout status deployment/${COMPONENT} -n ${NAMESPACE}'
            - 'echo "Health check: http://${COMPONENT}:${PORT}${HEALTH_PATH}"'
```

## Advanced Patterns

### Conditional Logic with Defaults

```yaml
# Use environment-specific settings with intelligent defaults
app:
  variables:
    # Development defaults
    DEBUG_MODE: "true"
    REPLICA_COUNT: "1"
    RESOURCE_LIMITS: "false"
    
  layouts:
    - id: 'app_deployment'
      children:
        - id: 'application'
          script:
            # Use production values if set, otherwise development defaults
            - 'echo "Debug mode: ${DEBUG_MODE}"'
            - 'echo "Replicas: ${REPLICA_COUNT}"'
            - 'echo "Resource limits: ${ENABLE_RESOURCE_LIMITS:${RESOURCE_LIMITS}}"'
            - |
              if [ "${DEBUG_MODE}" = "true" ]; then
                echo "Running in debug mode"
              else
                echo "Running in production mode"
              fi
```

### Dynamic Service Discovery

```yaml
app:
  variables:
    CONSUL_ENDPOINT: "http://consul.service.consul:8500"
    SERVICE_DISCOVERY: "consul"
    
  layouts:
    - id: 'service_mesh'
      children:
        - id: 'service_registry'
          variables:
            QUERY_PATH: "/v1/catalog/services"
          title: 'Service Registry (${SERVICE_DISCOVERY})'
          script:
            - 'curl -s ${CONSUL_ENDPOINT}${QUERY_PATH} | jq "keys"'
            
        - id: 'service_health'
          variables:
            SERVICE_NAME: "web-api"
            QUERY_PATH: "/v1/health/service"
          title: '${SERVICE_NAME} Health'
          script:
            - 'curl -s ${CONSUL_ENDPOINT}${QUERY_PATH}/${SERVICE_NAME} | jq ".[].Checks[].Status"'
```

### Multi-Environment Configuration Matrix

```yaml
app:
  variables:
    # Base configuration
    APP_NAME: "myapp"
    DEFAULT_MEMORY: "512Mi"
    DEFAULT_CPU: "0.5"
    
  layouts:
    - id: 'environment_matrix'
      children:
        - id: 'development'
          variables:
            ENV: "dev"
            REPLICAS: "1"
            MEMORY_LIMIT: "256Mi"
            CPU_LIMIT: "0.25"
          title: '${APP_NAME}-${ENV} (${REPLICAS} replicas)'
          script:
            - 'echo "Environment: ${ENV}"'
            - 'echo "Resources: CPU=${CPU_LIMIT}, Memory=${MEMORY_LIMIT}"'
            
        - id: 'staging'
          variables:
            ENV: "staging"
            REPLICAS: "2"
            # Uses DEFAULT_MEMORY and DEFAULT_CPU from app level
          title: '${APP_NAME}-${ENV} (${REPLICAS} replicas)'
          script:
            - 'echo "Environment: ${ENV}"'
            - 'echo "Resources: CPU=${CPU_LIMIT:${DEFAULT_CPU}}, Memory=${MEMORY_LIMIT:${DEFAULT_MEMORY}}"'
            
        - id: 'production'
          variables:
            ENV: "prod"
            REPLICAS: "5"
            MEMORY_LIMIT: "1Gi"
            CPU_LIMIT: "1.0"
          title: '${APP_NAME}-${ENV} (${REPLICAS} replicas)'
          script:
            - 'echo "Environment: ${ENV}"'
            - 'echo "Resources: CPU=${CPU_LIMIT}, Memory=${MEMORY_LIMIT}"'
```

## Best Practices

### 1. Use Meaningful Variable Names

```yaml
# Good: Descriptive and scoped
variables:
  DATABASE_CONNECTION_STRING: "postgres://localhost:5432/myapp"
  API_GATEWAY_ENDPOINT: "https://api.company.com"
  LOG_RETENTION_DAYS: "30"

# Avoid: Generic or ambiguous names
variables:
  URL: "https://api.company.com"      # Too generic
  CONFIG: "some_value"                # Unclear purpose
  X: "30"                             # Meaningless
```

### 2. Provide Sensible Defaults

```yaml
# Always provide fallback values for optional configuration
script:
  - 'echo "Timeout: ${REQUEST_TIMEOUT:30}s"'
  - 'echo "Retries: ${MAX_RETRIES:3}"'
  - 'echo "Log level: ${LOG_LEVEL:info}"'
```

### 3. Group Related Variables by Scope

```yaml
app:
  variables:
    # Global application settings
    APP_NAME: "myapp"
    VERSION: "1.0.0"
    ENVIRONMENT: "production"
    
  layouts:
    - id: 'database_tier'
      children:
        - id: 'postgres'
          variables:
            # Database-specific configuration
            DB_HOST: "postgres.internal"
            DB_PORT: "5432"
            DB_NAME: "myapp_prod"
            CONNECTION_POOL_SIZE: "10"
            
        - id: 'redis'
          variables:
            # Cache-specific configuration
            REDIS_HOST: "redis.internal"
            REDIS_PORT: "6379"
            REDIS_DB: "0"
            CACHE_TTL: "3600"
```

### 4. Use Environment Variables for Secrets

```yaml
# Never store secrets in YAML files
# Use environment variables with meaningful defaults for non-secrets
app:
  variables:
    DATABASE_HOST: "localhost"        # OK: Default host
    DATABASE_PORT: "5432"             # OK: Default port
    # DATABASE_PASSWORD: "secret123"  # NEVER: Use $DATABASE_PASSWORD instead
    
  layouts:
    - id: 'database_box'
      script:
        # Reference secrets via environment variables
        - 'psql -h ${DATABASE_HOST} -p ${DATABASE_PORT} -U ${DATABASE_USER} ${DATABASE_NAME}'
        # $DATABASE_USER and $DATABASE_PASSWORD come from environment
```

### 5. Leverage Hierarchical Inheritance

```yaml
# Define common settings at higher levels, specifics at lower levels
app:
  variables:
    COMPANY_DOMAIN: "company.com"
    MONITORING_ENABLED: "true"
    
  layouts:
    - id: 'microservices'
      children:
        - id: 'service_group_a'
          variables:
            SERVICE_GROUP: "frontend"
            DEFAULT_PORT: "3000"
          children:
            - id: 'react_app'
              variables:
                SERVICE_NAME: "react-ui"
                # Inherits: COMPANY_DOMAIN, MONITORING_ENABLED, SERVICE_GROUP, DEFAULT_PORT
              title: '${SERVICE_NAME} (${SERVICE_GROUP})'
              
            - id: 'vue_app'
              variables:
                SERVICE_NAME: "vue-dashboard"
                PORT: "3001"  # Overrides DEFAULT_PORT for this service
              title: '${SERVICE_NAME} (${SERVICE_GROUP})'
```

## Troubleshooting

### Common Issues and Solutions

#### Issue: Variable Not Resolving

```yaml
# Problem: Variable shows as literal text
content: 'Server: ${SERVER_NAME}'
# Output: "Server: ${SERVER_NAME}"

# Solution 1: Check variable is defined
app:
  variables:
    SERVER_NAME: "prod-server"

# Solution 2: Use default value
content: 'Server: ${SERVER_NAME:default-server}'
```

#### Issue: Nested Variables Not Supported

```yaml
# Problem: This doesn't work
variables:
  BASE_URL: "https://api.company.com"
  ENDPOINT: "${BASE_URL}/v1"  # Nested variable reference

# Solution: Use environment variables for composition
# Set in shell: export ENDPOINT="${BASE_URL}/v1"
```

#### Issue: Environment Variable Override Not Working

```bash
# Problem: Environment variable not taking precedence
export DATABASE_HOST="override-host"
./boxmux config.yaml

# Solution 1: Check variable name matches exactly
# YAML: DATABASE_HOST vs Environment: DATABASE_HOST ✓
# YAML: db_host vs Environment: DATABASE_HOST ✗

# Solution 2: Verify export is in same shell session
env | grep DATABASE_HOST  # Should show your value
```

#### Issue: Complex Variable Expressions

```yaml
# Problem: Trying to do complex operations
content: '${PORT + 1000}'  # Not supported

# Solution: Use script logic instead
script:
  - 'PORT_PLUS_1000=$((${PORT:8080} + 1000))'
  - 'echo "Adjusted port: $PORT_PLUS_1000"'
```

### Debugging Variable Resolution

#### Enable Debug Output

```bash
# Run with debug logging to see variable resolution
RUST_LOG=debug ./boxmux config.yaml 2>&1 | grep -i variable
```

#### Test Variable Resolution

```yaml
# Create a debug box to test variable values
- id: 'debug_vars'
  title: 'Variable Debug'
  script:
    - 'echo "All variables:"'
    - 'echo "APP_NAME: ${APP_NAME:not_set}"'
    - 'echo "ENVIRONMENT: ${ENVIRONMENT:not_set}"'
    - 'echo "DATABASE_HOST: ${DATABASE_HOST:not_set}"'
    - 'echo "Environment variables:"'
    - 'env | grep -E "(APP_NAME|ENVIRONMENT|DATABASE_HOST)"'
```

#### Validate Configuration

```bash
# Test configuration with known environment
APP_NAME="test" ENVIRONMENT="debug" ./boxmux config.yaml
```