923 lines
25 KiB
Markdown
923 lines
25 KiB
Markdown
# Configuration Workflow: TypeDialog → Nickel → TOML → Rust
|
|
|
|
Complete documentation of the configuration pipeline that transforms interactive user input into production Rust service configurations.
|
|
|
|
## Overview
|
|
|
|
The provisioning platform uses a **four-stage configuration workflow** that leverages TypeDialog for interactive configuration,
|
|
Nickel for type-safe composition, and TOML for service consumption:
|
|
|
|
```nickel
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Stage 1: User Interaction (TypeDialog) │
|
|
│ - Can use Nickel configuration as default values │
|
|
│ if use provisioning/platform/config/ it will be updated │
|
|
│ - Interactive form (web/tui/cli) │
|
|
│ - Real-time constraint validation │
|
|
│ - Generates Nickel configuration │
|
|
└────────────────┬────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Stage 2: Composition (Nickel) │
|
|
│ - Base defaults imported │
|
|
│ - Mode overlay applied │
|
|
│ - Validators enforce business rules │
|
|
│ - Produces Nickel config file │
|
|
└────────────────┬────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Stage 3: Export (Nickel → TOML) │
|
|
│ - Nickel config evaluated │
|
|
│ - Exported to TOML format │
|
|
│ - Saved to provisioning/platform/config/ │
|
|
└────────────────┬────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Stage 4: Runtime (Rust Services) │
|
|
│ - Services load TOML configuration │
|
|
│ - Environment variables override specific values │
|
|
│ - Start services with final configuration │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Stage 1: User Interaction (TypeDialog)
|
|
|
|
### Purpose
|
|
|
|
Collect configuration from users through an interactive, constraint-aware interface.
|
|
|
|
### Workflow
|
|
|
|
```bash
|
|
# Launch interactive configuration wizard
|
|
nu scripts/configure.nu orchestrator solo --backend web
|
|
```
|
|
|
|
### What Happens
|
|
|
|
1. **Form Loads**
|
|
- TypeDialog reads `forms/orchestrator-form.toml`
|
|
- Form displays configuration sections
|
|
- Constraints from `constraints.toml` enforce min/max values
|
|
- Environment variables populate initial defaults
|
|
|
|
2. **User Interaction**
|
|
- User fills in form fields (workspace name, server port, etc.)
|
|
- Real-time validation on each field
|
|
- Constraint interpolation shows valid ranges:
|
|
- `${constraint.orchestrator.workers.min}` → `1`
|
|
- `${constraint.orchestrator.workers.max}` → `32`
|
|
|
|
3. **Configuration Submission**
|
|
- User submits form
|
|
- TypeDialog validates all fields against schemas
|
|
- Generates Nickel configuration output
|
|
|
|
4. **Output Generation**
|
|
- Nickel config saved to `values/{service}.{mode}.ncl`
|
|
- Example: `values/orchestrator.solo.ncl`
|
|
- File becomes source of truth for user customizations
|
|
|
|
### Form Structure Example
|
|
|
|
```bash
|
|
# forms/orchestrator-form.toml
|
|
name = "orchestrator_configuration"
|
|
description = "Configure orchestrator service"
|
|
|
|
[[items]]
|
|
name = "workspace_group"
|
|
type = "group"
|
|
includes = ["fragments/workspace-section.toml"]
|
|
|
|
[[items]]
|
|
name = "server_group"
|
|
type = "group"
|
|
includes = ["fragments/server-section.toml"]
|
|
|
|
[[items]]
|
|
name = "queue_group"
|
|
type = "group"
|
|
includes = ["fragments/orchestrator/queue-section.toml"]
|
|
```
|
|
|
|
### Fragment with Constraint Interpolation
|
|
|
|
```bash
|
|
# forms/fragments/orchestrator/queue-section.toml
|
|
[[elements]]
|
|
name = "max_concurrent_tasks"
|
|
type = "number"
|
|
prompt = "Maximum Concurrent Tasks"
|
|
default = 5
|
|
min = "${constraint.orchestrator.queue.concurrent_tasks.min}"
|
|
max = "${constraint.orchestrator.queue.concurrent_tasks.max}"
|
|
required = true
|
|
help = "Range: ${constraint.orchestrator.queue.concurrent_tasks.min}-${constraint.orchestrator.queue.concurrent_tasks.max}"
|
|
nickel_path = ["orchestrator", "queue", "max_concurrent_tasks"]
|
|
```
|
|
|
|
### Generated Nickel Output (from TypeDialog)
|
|
|
|
TypeDialog's `nickel-roundtrip` pattern generates:
|
|
|
|
```nickel
|
|
# values/orchestrator.solo.ncl
|
|
# Auto-generated by TypeDialog
|
|
{
|
|
orchestrator = {
|
|
workspace = {
|
|
name = "dev-workspace",
|
|
path = "/home/developer/provisioning/data/orchestrator",
|
|
enabled = true,
|
|
},
|
|
server = {
|
|
host = "127.0.0.1",
|
|
port = 9090,
|
|
workers = 2,
|
|
},
|
|
queue = {
|
|
max_concurrent_tasks = 3,
|
|
retry_attempts = 2,
|
|
retry_delay = 1000,
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Stage 2: Composition (Nickel)
|
|
|
|
### Purpose
|
|
|
|
Compose the user input with defaults, validators, and schemas to create a complete, validated configuration.
|
|
|
|
### Workflow
|
|
|
|
```bash
|
|
# The nickel typecheck command validates the composition
|
|
nickel typecheck values/orchestrator.solo.ncl
|
|
```
|
|
|
|
### Composition Layers
|
|
|
|
The final configuration is built by merging layers in priority order:
|
|
|
|
#### Layer 1: Schema Import
|
|
|
|
```bash
|
|
# Ensures type safety and required fields
|
|
let schemas = import "../schemas/orchestrator.ncl" in
|
|
```
|
|
|
|
#### Layer 2: Base Defaults
|
|
|
|
```bash
|
|
# Default values for all orchestrator configurations
|
|
let defaults = import "../defaults/orchestrator-defaults.ncl" in
|
|
```
|
|
|
|
#### Layer 3: Mode Overlay
|
|
|
|
```bash
|
|
# Solo-specific overrides and adjustments
|
|
let solo_defaults = import "../defaults/deployment/solo-defaults.ncl" in
|
|
```
|
|
|
|
#### Layer 4: Validators Import
|
|
|
|
```bash
|
|
# Business rule validation (ranges, uniqueness, dependencies)
|
|
let validators = import "../validators/orchestrator-validator.ncl" in
|
|
```
|
|
|
|
#### Layer 5: User Values
|
|
|
|
```bash
|
|
# User input from TypeDialog (values/orchestrator.solo.ncl)
|
|
# Loaded and merged with defaults
|
|
```
|
|
|
|
### Composition Example
|
|
|
|
```bash
|
|
# configs/orchestrator.solo.ncl (generated composition)
|
|
|
|
let schemas = import "../schemas/orchestrator.ncl" in
|
|
let defaults = import "../defaults/orchestrator-defaults.ncl" in
|
|
let solo_defaults = import "../defaults/deployment/solo-defaults.ncl" in
|
|
let validators = import "../validators/orchestrator-validator.ncl" in
|
|
|
|
# Composition: Base defaults + mode overlay + user input
|
|
{
|
|
orchestrator = defaults.orchestrator & {
|
|
# User input from TypeDialog values/orchestrator.solo.ncl
|
|
workspace = {
|
|
name = "dev-workspace",
|
|
path = "/home/developer/provisioning/data/orchestrator",
|
|
},
|
|
|
|
# Solo mode overrides
|
|
server = {
|
|
workers = validators.ValidWorkers 2,
|
|
max_connections = 128,
|
|
},
|
|
|
|
queue = {
|
|
max_concurrent_tasks = validators.ValidConcurrentTasks 3,
|
|
},
|
|
|
|
# Fallback to defaults for unspecified fields
|
|
},
|
|
} | schemas.OrchestratorConfig # Validate against schema
|
|
```
|
|
|
|
### Validation During Composition
|
|
|
|
Each field is validated through multiple validation layers:
|
|
|
|
```bash
|
|
# validators/orchestrator-validator.ncl
|
|
let constraints = import "../constraints/constraints.toml" in
|
|
|
|
{
|
|
# Validate workers within allowed range
|
|
ValidWorkers = fun workers =>
|
|
if workers < constraints.orchestrator.workers.min then
|
|
error "Workers below minimum"
|
|
else if workers > constraints.orchestrator.workers.max then
|
|
error "Workers above maximum"
|
|
else
|
|
workers,
|
|
|
|
# Validate concurrent tasks
|
|
ValidConcurrentTasks = fun tasks =>
|
|
if tasks < constraints.orchestrator.queue.concurrent_tasks.min then
|
|
error "Tasks below minimum"
|
|
else if tasks > constraints.orchestrator.queue.concurrent_tasks.max then
|
|
error "Tasks above maximum"
|
|
else
|
|
tasks,
|
|
}
|
|
```
|
|
|
|
### Constraints: Single Source of Truth
|
|
|
|
```bash
|
|
# constraints/constraints.toml
|
|
[orchestrator.workers]
|
|
min = 1
|
|
max = 32
|
|
|
|
[orchestrator.queue.concurrent_tasks]
|
|
min = 1
|
|
max = 100
|
|
|
|
[common.server.port]
|
|
min = 1024
|
|
max = 65535
|
|
```
|
|
|
|
These values are referenced in:
|
|
- Form constraints (constraint interpolation)
|
|
- Validators (ValidWorkers, ValidConcurrentTasks)
|
|
- Default values (appropriate for each mode)
|
|
|
|
---
|
|
|
|
## Stage 3: Export (Nickel → TOML)
|
|
|
|
### Purpose
|
|
|
|
Convert validated Nickel configuration to TOML format for consumption by Rust services.
|
|
|
|
### Workflow
|
|
|
|
```bash
|
|
# Export Nickel to TOML
|
|
nu scripts/generate-configs.nu orchestrator solo
|
|
```
|
|
|
|
### Command Chain
|
|
|
|
```bash
|
|
# What happens internally:
|
|
|
|
# 1. Typecheck the Nickel config (catch errors early)
|
|
nickel typecheck provisioning/.typedialog/provisioning/platform/configs/orchestrator.solo.ncl
|
|
|
|
# 2. Export to TOML format
|
|
nickel export --format toml provisioning/.typedialog/provisioning/platform/configs/orchestrator.solo.ncl
|
|
|
|
# 3. Save to output location
|
|
# → provisioning/platform/config/orchestrator.solo.toml
|
|
```
|
|
|
|
### Input: Nickel Configuration
|
|
|
|
```nickel
|
|
# From: configs/orchestrator.solo.ncl
|
|
{
|
|
orchestrator = {
|
|
workspace = {
|
|
name = "dev-workspace",
|
|
path = "/home/developer/provisioning/data/orchestrator",
|
|
enabled = true,
|
|
multi_workspace = false,
|
|
},
|
|
server = {
|
|
host = "127.0.0.1",
|
|
port = 9090,
|
|
workers = 2,
|
|
keep_alive = 75,
|
|
max_connections = 128,
|
|
},
|
|
storage = {
|
|
backend = "filesystem",
|
|
path = "/home/developer/provisioning/data/orchestrator",
|
|
},
|
|
queue = {
|
|
max_concurrent_tasks = 3,
|
|
retry_attempts = 2,
|
|
retry_delay = 1000,
|
|
task_timeout = 1800000,
|
|
},
|
|
monitoring = {
|
|
enabled = true,
|
|
metrics = {
|
|
enabled = false,
|
|
},
|
|
health_check = {
|
|
enabled = true,
|
|
interval = 60,
|
|
},
|
|
},
|
|
logging = {
|
|
level = "debug",
|
|
format = "text",
|
|
outputs = [
|
|
{
|
|
destination = "stdout",
|
|
level = "debug",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
### Output: TOML Configuration
|
|
|
|
```toml
|
|
# To: provisioning/platform/config/orchestrator.solo.toml
|
|
[orchestrator.workspace]
|
|
name = "dev-workspace"
|
|
path = "/home/developer/provisioning/data/orchestrator"
|
|
enabled = true
|
|
multi_workspace = false
|
|
|
|
[orchestrator.server]
|
|
host = "127.0.0.1"
|
|
port = 9090
|
|
workers = 2
|
|
keep_alive = 75
|
|
max_connections = 128
|
|
|
|
[orchestrator.storage]
|
|
backend = "filesystem"
|
|
path = "/home/developer/provisioning/data/orchestrator"
|
|
|
|
[orchestrator.queue]
|
|
max_concurrent_tasks = 3
|
|
retry_attempts = 2
|
|
retry_delay = 1000
|
|
task_timeout = 1800000
|
|
|
|
[orchestrator.monitoring]
|
|
enabled = true
|
|
|
|
[orchestrator.monitoring.metrics]
|
|
enabled = false
|
|
|
|
[orchestrator.monitoring.health_check]
|
|
enabled = true
|
|
interval = 60
|
|
|
|
[orchestrator.logging]
|
|
level = "debug"
|
|
format = "text"
|
|
|
|
[[orchestrator.logging.outputs]]
|
|
destination = "stdout"
|
|
level = "debug"
|
|
```
|
|
|
|
### Output Location
|
|
|
|
```bash
|
|
provisioning/platform/config/
|
|
├── orchestrator.solo.toml # Exported from configs/orchestrator.solo.ncl
|
|
├── orchestrator.multiuser.toml # Exported from configs/orchestrator.multiuser.ncl
|
|
├── orchestrator.cicd.toml # Exported from configs/orchestrator.cicd.ncl
|
|
├── orchestrator.enterprise.toml # Exported from configs/orchestrator.enterprise.ncl
|
|
├── control-center.solo.toml # Similar structure for each service
|
|
├── control-center.multiuser.toml
|
|
├── mcp-server.solo.toml
|
|
└── mcp-server.enterprise.toml
|
|
```
|
|
|
|
### Validation During Export
|
|
|
|
The `generate-configs.nu` script:
|
|
|
|
1. **Typechecks** - Ensures Nickel is syntactically valid
|
|
2. **Evaluates** - Computes final values
|
|
3. **Exports** - Converts to TOML format
|
|
4. **Saves** - Writes to `provisioning/platform/config/`
|
|
|
|
---
|
|
|
|
## Stage 4: Runtime (Rust Services)
|
|
|
|
### Purpose
|
|
|
|
Load TOML configuration and start Rust services with validated settings.
|
|
|
|
### Configuration Loading Hierarchy
|
|
|
|
Rust services load configuration in this priority order:
|
|
|
|
#### 1. Runtime Arguments (Highest Priority)
|
|
|
|
```bash
|
|
ORCHESTRATOR_CONFIG=/path/to/config.toml cargo run --bin orchestrator
|
|
```
|
|
|
|
#### 2. Environment Variables
|
|
|
|
```bash
|
|
# Environment variable overrides specific TOML values
|
|
export ORCHESTRATOR_SERVER_PORT=9999
|
|
export ORCHESTRATOR_LOG_LEVEL=debug
|
|
|
|
ORCHESTRATOR_CONFIG=orchestrator.solo.toml cargo run --bin orchestrator
|
|
```
|
|
|
|
Environment variable format: `ORCHESTRATOR_{SECTION}_{KEY}=value`
|
|
|
|
Example mappings:
|
|
- `ORCHESTRATOR_SERVER_PORT=9999` → `orchestrator.server.port = 9999`
|
|
- `ORCHESTRATOR_LOG_LEVEL=debug` → `orchestrator.logging.level = "debug"`
|
|
- `ORCHESTRATOR_QUEUE_MAX_CONCURRENT_TASKS=10` → `orchestrator.queue.max_concurrent_tasks = 10`
|
|
|
|
#### 3. TOML Configuration File
|
|
|
|
```toml
|
|
# Load from TOML (medium priority)
|
|
ORCHESTRATOR_CONFIG=orchestrator.solo.toml cargo run --bin orchestrator
|
|
```
|
|
|
|
#### 4. Compiled Defaults (Lowest Priority)
|
|
|
|
```bash
|
|
// In Rust code - fallback for unspecified values
|
|
let config = Config::from_file(config_path)
|
|
.unwrap_or_else(|_| Config::default());
|
|
```
|
|
|
|
### Example: Solo Mode Startup
|
|
|
|
```bash
|
|
# Step 1: User generates config through TypeDialog
|
|
nu scripts/configure.nu orchestrator solo --backend web
|
|
|
|
# Step 2: Export to TOML
|
|
nu scripts/generate-configs.nu orchestrator solo
|
|
|
|
# Step 3: Set environment variables for environment-specific overrides
|
|
export ORCHESTRATOR_SERVER_PORT=9090
|
|
export ORCHESTRATOR_LOG_LEVEL=debug
|
|
|
|
# Step 4: Start the Rust service
|
|
ORCHESTRATOR_CONFIG=provisioning/platform/config/orchestrator.solo.toml cargo run --bin orchestrator
|
|
```
|
|
|
|
### Rust Service Configuration Loading
|
|
|
|
```rust
|
|
// In orchestrator/src/config.rs
|
|
|
|
use config::{Config, ConfigError, Environment, File};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct OrchestratorConfig {
|
|
pub orchestrator: OrchestratorService,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct OrchestratorService {
|
|
pub workspace: Workspace,
|
|
pub server: Server,
|
|
pub storage: Storage,
|
|
pub queue: Queue,
|
|
}
|
|
|
|
impl OrchestratorConfig {
|
|
pub fn load(config_path: Option<&str>) -> Result<Self, ConfigError> {
|
|
let mut builder = Config::builder();
|
|
|
|
// 1. Load TOML file if provided
|
|
if let Some(path) = config_path {
|
|
builder = builder.add_source(File::from(Path::new(path)));
|
|
} else {
|
|
// Fallback to defaults
|
|
builder = builder.add_source(File::with_name("config/orchestrator.defaults.toml"));
|
|
}
|
|
|
|
// 2. Apply environment variable overrides
|
|
builder = builder.add_source(
|
|
Environment::with_prefix("ORCHESTRATOR")
|
|
.separator("_")
|
|
);
|
|
|
|
let config = builder.build()?;
|
|
config.try_deserialize()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Configuration Validation in Rust
|
|
|
|
```rust
|
|
impl OrchestratorConfig {
|
|
pub fn validate(&self) -> Result<(), ConfigError> {
|
|
// Validate server configuration
|
|
if self.orchestrator.server.port < 1024 || self.orchestrator.server.port > 65535 {
|
|
return Err(ConfigError::Message(
|
|
"Server port must be between 1024 and 65535".to_string()
|
|
));
|
|
}
|
|
|
|
// Validate queue configuration
|
|
if self.orchestrator.queue.max_concurrent_tasks == 0 {
|
|
return Err(ConfigError::Message(
|
|
"max_concurrent_tasks must be > 0".to_string()
|
|
));
|
|
}
|
|
|
|
// Validate storage configuration
|
|
match self.orchestrator.storage.backend.as_str() {
|
|
"filesystem" | "surrealdb" | "rocksdb" => {
|
|
// Valid backend
|
|
},
|
|
backend => {
|
|
return Err(ConfigError::Message(
|
|
format!("Unknown storage backend: {}", backend)
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
```
|
|
|
|
### Runtime Startup Sequence
|
|
|
|
```bash
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// Load configuration
|
|
let config = OrchestratorConfig::load(
|
|
std::env::var("ORCHESTRATOR_CONFIG").ok().as_deref()
|
|
)?;
|
|
|
|
// Validate configuration
|
|
config.validate()?;
|
|
|
|
// Initialize logging
|
|
init_logging(&config.orchestrator.logging)?;
|
|
|
|
// Start HTTP server
|
|
let server = Server::new(
|
|
config.orchestrator.server.host.clone(),
|
|
config.orchestrator.server.port,
|
|
);
|
|
|
|
// Initialize storage backend
|
|
let storage = Storage::new(&config.orchestrator.storage)?;
|
|
|
|
// Start the service
|
|
server.start(storage).await?;
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Complete Example: Solo Mode End-to-End
|
|
|
|
### Step 1: Interactive Configuration
|
|
|
|
```toml
|
|
$ nu scripts/configure.nu orchestrator solo --backend web
|
|
|
|
# TypeDialog launches web interface
|
|
# User fills in form:
|
|
# - Workspace name: "dev-workspace"
|
|
# - Server host: "127.0.0.1"
|
|
# - Server port: 9090
|
|
# - Storage backend: "filesystem"
|
|
# - Storage path: "/home/developer/provisioning/data/orchestrator"
|
|
# - Max concurrent tasks: 3
|
|
# - Log level: "debug"
|
|
|
|
# Saves to: values/orchestrator.solo.ncl
|
|
```
|
|
|
|
### Step 2: Generated Nickel Configuration
|
|
|
|
```nickel
|
|
# values/orchestrator.solo.ncl
|
|
{
|
|
orchestrator = {
|
|
workspace = {
|
|
name = "dev-workspace",
|
|
path = "/home/developer/provisioning/data/orchestrator",
|
|
enabled = true,
|
|
multi_workspace = false,
|
|
},
|
|
server = {
|
|
host = "127.0.0.1",
|
|
port = 9090,
|
|
workers = 2,
|
|
keep_alive = 75,
|
|
max_connections = 128,
|
|
},
|
|
storage = {
|
|
backend = "filesystem",
|
|
path = "/home/developer/provisioning/data/orchestrator",
|
|
},
|
|
queue = {
|
|
max_concurrent_tasks = 3,
|
|
retry_attempts = 2,
|
|
retry_delay = 1000,
|
|
task_timeout = 1800000,
|
|
},
|
|
logging = {
|
|
level = "debug",
|
|
format = "text",
|
|
outputs = [{
|
|
destination = "stdout",
|
|
level = "debug",
|
|
}],
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
### Step 3: Composition and Validation
|
|
|
|
```bash
|
|
$ nickel typecheck provisioning/.typedialog/provisioning/platform/configs/orchestrator.solo.ncl
|
|
|
|
# Validation passes:
|
|
# - Workspace name: valid string ✓
|
|
# - Port 9090: within range 1024-65535 ✓
|
|
# - Max concurrent tasks 3: within range 1-100 ✓
|
|
# - Log level: recognized level ✓
|
|
```
|
|
|
|
### Step 4: Export to TOML
|
|
|
|
```toml
|
|
$ nu scripts/generate-configs.nu orchestrator solo
|
|
|
|
# Generates: provisioning/platform/config/orchestrator.solo.toml
|
|
```
|
|
|
|
### Step 5: TOML File Created
|
|
|
|
```toml
|
|
# provisioning/platform/config/orchestrator.solo.toml
|
|
[orchestrator.workspace]
|
|
name = "dev-workspace"
|
|
path = "/home/developer/provisioning/data/orchestrator"
|
|
enabled = true
|
|
multi_workspace = false
|
|
|
|
[orchestrator.server]
|
|
host = "127.0.0.1"
|
|
port = 9090
|
|
workers = 2
|
|
keep_alive = 75
|
|
max_connections = 128
|
|
|
|
[orchestrator.storage]
|
|
backend = "filesystem"
|
|
path = "/home/developer/provisioning/data/orchestrator"
|
|
|
|
[orchestrator.queue]
|
|
max_concurrent_tasks = 3
|
|
retry_attempts = 2
|
|
retry_delay = 1000
|
|
task_timeout = 1800000
|
|
|
|
[orchestrator.logging]
|
|
level = "debug"
|
|
format = "text"
|
|
|
|
[[orchestrator.logging.outputs]]
|
|
destination = "stdout"
|
|
level = "debug"
|
|
```
|
|
|
|
### Step 6: Runtime Startup
|
|
|
|
```bash
|
|
$ export ORCHESTRATOR_LOG_LEVEL=debug
|
|
$ ORCHESTRATOR_CONFIG=provisioning/platform/config/orchestrator.solo.toml cargo run --bin orchestrator
|
|
|
|
# Service loads orchestrator.solo.toml
|
|
# Environment variable overrides ORCHESTRATOR_LOG_LEVEL to "debug"
|
|
# Service starts and begins accepting requests on 127.0.0.1:9090
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Modification Workflow
|
|
|
|
### Scenario: User Wants to Change Port
|
|
|
|
#### Option A: Modify TypeDialog Form and Regenerate
|
|
|
|
```bash
|
|
# 1. Re-run interactive configuration
|
|
nu scripts/configure.nu orchestrator solo --backend web
|
|
|
|
# 2. User changes port to 9999 in form
|
|
# 3. TypeDialog generates new values/orchestrator.solo.ncl
|
|
|
|
# 4. Export updated config
|
|
nu scripts/generate-configs.nu orchestrator solo
|
|
|
|
# 5. New TOML created with port: 9999
|
|
# 6. Restart service
|
|
ORCHESTRATOR_CONFIG=provisioning/platform/config/orchestrator.solo.toml cargo run --bin orchestrator
|
|
```
|
|
|
|
#### Option B: Direct TOML Edit
|
|
|
|
```toml
|
|
# 1. Edit TOML directly
|
|
vi provisioning/platform/config/orchestrator.solo.toml
|
|
# Change: port = 9999
|
|
|
|
# 2. Restart service (no Nickel re-export needed)
|
|
ORCHESTRATOR_CONFIG=provisioning/platform/config/orchestrator.solo.toml cargo run --bin orchestrator
|
|
```
|
|
|
|
#### Option C: Environment Variable Override
|
|
|
|
```bash
|
|
# 1. No file changes needed
|
|
# 2. Just override environment variable
|
|
export ORCHESTRATOR_SERVER_PORT=9999
|
|
|
|
# 3. Restart service
|
|
ORCHESTRATOR_CONFIG=provisioning/platform/config/orchestrator.solo.toml cargo run --bin orchestrator
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture Relationships
|
|
|
|
### Component Interactions
|
|
|
|
```bash
|
|
TypeDialog Forms Nickel Schemas
|
|
(forms/*.toml) ←shares→ (schemas/*.ncl)
|
|
│ │
|
|
│ user input │ type definitions
|
|
│ │
|
|
▼ ▼
|
|
values/*.ncl ←─ constraint validation ─→ constraints.toml
|
|
│ (single source of truth)
|
|
│ │
|
|
│ │
|
|
├──→ imported into composition ────────────┤
|
|
│ (configs/*.ncl) │
|
|
│ │
|
|
│ base defaults ───→ defaults/*.ncl │
|
|
│ mode overlay ─────→ deployment/*.ncl │
|
|
│ validators ──────→ validators/*.ncl │
|
|
│ │
|
|
└──→ typecheck + export ──────────────→─────┘
|
|
nickel export --format toml
|
|
│
|
|
▼
|
|
provisioning/platform/config/
|
|
*.toml files
|
|
│
|
|
│ loaded by Rust services
|
|
│ at runtime
|
|
▼
|
|
Running Service
|
|
(orchestrator, control-center, mcp-server)
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### 1. Always Validate Before Deploying
|
|
|
|
```bash
|
|
# Typecheck Nickel before export
|
|
nickel typecheck provisioning/.typedialog/provisioning/platform/configs/orchestrator.solo.ncl
|
|
|
|
# Validate TOML before loading in Rust
|
|
cargo run --bin orchestrator -- --validate-config orchestrator.solo.toml
|
|
```
|
|
|
|
### 2. Use Version Control for TOML Configs
|
|
|
|
```toml
|
|
# Commit generated TOML files
|
|
git add provisioning/platform/config/orchestrator.solo.toml
|
|
git commit -m "Update orchestrator solo configuration"
|
|
|
|
# But NOT the values/*.ncl files
|
|
echo "values/*.ncl" >> provisioning/.typedialog/provisioning/platform/.gitignore
|
|
```
|
|
|
|
### 3. Document Configuration Changes
|
|
|
|
```toml
|
|
# In TypeDialog form, add comments
|
|
[[items]]
|
|
name = "max_concurrent_tasks"
|
|
type = "number"
|
|
prompt = "Max concurrent tasks (3 for dev, 50+ for production)"
|
|
help = "Increased from 3 to 10 for higher throughput testing"
|
|
```
|
|
|
|
### 4. Environment Variables for Sensitive Data
|
|
|
|
Never hardcode secrets in TOML:
|
|
|
|
```toml
|
|
# Instead of:
|
|
# [orchestrator.security]
|
|
# jwt_secret = "hardcoded-secret"
|
|
|
|
# Use environment variable:
|
|
export ORCHESTRATOR_SECURITY_JWT_SECRET="actual-secret"
|
|
|
|
# TOML can reference it:
|
|
# [orchestrator.security]
|
|
# jwt_secret = "${JWT_SECRET}"
|
|
```
|
|
|
|
### 5. Test Configuration Changes in Staging First
|
|
|
|
```toml
|
|
# Generate staging config
|
|
nu scripts/configure.nu orchestrator multiuser --backend web
|
|
|
|
# Export to staging TOML
|
|
nu scripts/generate-configs.nu orchestrator multiuser
|
|
|
|
# Test in staging environment
|
|
ORCHESTRATOR_CONFIG=orchestrator.multiuser.toml cargo run --bin orchestrator
|
|
# Monitor logs and verify behavior
|
|
|
|
# Then deploy to production
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
The four-stage workflow provides:
|
|
|
|
1. **User-Friendly Interface**: TypeDialog forms with real-time validation
|
|
2. **Type Safety**: Nickel schemas and validators catch configuration errors early
|
|
3. **Flexibility**: TOML format can be edited manually or generated programmatically
|
|
4. **Runtime Configurability**: Environment variables allow deployment-time overrides
|
|
5. **Single Source of Truth**: Constraints, schemas, and validators all reference shared definitions
|
|
|
|
This layered approach ensures that:
|
|
- Invalid configurations are caught before deployment
|
|
- Users can modify configuration safely
|
|
- Different deployment modes have appropriate defaults
|
|
- Configuration changes can be version-controlled
|
|
- Services can be reconfigured without code changes |