# Orchestrator Integration Model - Deep Dive\n\n**Date:** 2025-10-01\n**Status:** Clarification Document\n**Related:** [Multi-Repo Strategy](multi-repo-strategy.md), [Hybrid Orchestrator v3.0](../user/hybrid-orchestrator.md)\n\n## Executive Summary\n\nThis document clarifies **how the Rust orchestrator integrates with Nushell core** in both monorepo and multi-repo architectures. The orchestrator is\na **critical performance layer** that coordinates Nushell business logic execution, solving deep call stack limitations while preserving all existing\nfunctionality.\n\n---\n\n## Current Architecture (Hybrid Orchestrator v3.0)\n\n### The Problem Being Solved\n\n**Original Issue:**\n\n```\nDeep call stack in Nushell (template.nu:71)\n→ "Type not supported" errors\n→ Cannot handle complex nested workflows\n→ Performance bottlenecks with recursive calls\n```\n\n**Solution:** Rust orchestrator provides:\n\n1. **Task queue management** (file-based, reliable)\n2. **Priority scheduling** (intelligent task ordering)\n3. **Deep call stack elimination** (Rust handles recursion)\n4. **Performance optimization** (async/await, parallel execution)\n5. **State management** (workflow checkpointing)\n\n### How It Works Today (Monorepo)\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│ User │\n└───────────────────────────┬─────────────────────────────────┘\n │ calls\n ↓\n ┌───────────────┐\n │ provisioning │ (Nushell CLI)\n │ CLI │\n └───────┬───────┘\n │\n ┌───────────────────┼───────────────────┐\n │ │ │\n ↓ ↓ ↓\n┌───────────────┐ ┌───────────────┐ ┌──────────────┐\n│ Direct Mode │ │Orchestrated │ │ Workflow │\n│ (Simple ops) │ │ Mode │ │ Mode │\n└───────────────┘ └───────┬───────┘ └──────┬───────┘\n │ │\n ↓ ↓\n ┌────────────────────────────────┐\n │ Rust Orchestrator Service │\n │ (Background daemon) │\n │ │\n │ • Task Queue (file-based) │\n │ • Priority Scheduler │\n │ • Workflow Engine │\n │ • REST API Server │\n └────────┬───────────────────────┘\n │ spawns\n ↓\n ┌────────────────┐\n │ Nushell │\n │ Business Logic │\n │ │\n │ • servers.nu │\n │ • taskservs.nu │\n │ • clusters.nu │\n └────────────────┘\n```\n\n### Three Execution Modes\n\n#### Mode 1: Direct Mode (Simple Operations)\n\n```\n# No orchestrator needed\nprovisioning server list\nprovisioning env\nprovisioning help\n\n# Direct Nushell execution\nprovisioning (CLI) → Nushell scripts → Result\n```\n\n#### Mode 2: Orchestrated Mode (Complex Operations)\n\n```\n# Uses orchestrator for coordination\nprovisioning server create --orchestrated\n\n# Flow:\nprovisioning CLI → Orchestrator API → Task Queue → Nushell executor\n ↓\n Result back to user\n```\n\n#### Mode 3: Workflow Mode (Batch Operations)\n\n```\n# Complex workflows with dependencies\nprovisioning workflow submit server-cluster.ncl\n\n# Flow:\nprovisioning CLI → Orchestrator Workflow Engine → Dependency Graph\n ↓\n Parallel task execution\n ↓\n Nushell scripts for each task\n ↓\n Checkpoint state\n```\n\n---\n\n## Integration Patterns\n\n### Pattern 1: CLI Submits Tasks to Orchestrator\n\n**Current Implementation:**\n\n**Nushell CLI (`core/nulib/workflows/server_create.nu`):**\n\n```\n# Submit server creation workflow to orchestrator\nexport def server_create_workflow [\n infra_name: string\n --orchestrated\n] {\n if $orchestrated {\n # Submit task to orchestrator\n let task = {\n type: "server_create"\n infra: $infra_name\n params: { ... }\n }\n\n # POST to orchestrator REST API\n http post http://localhost:9090/workflows/servers/create $task\n } else {\n # Direct execution (old way)\n do-server-create $infra_name\n }\n}\n```\n\n**Rust Orchestrator (`platform/orchestrator/src/api/workflows.rs`):**\n\n```\n// Receive workflow submission from Nushell CLI\n#[axum::debug_handler]\nasync fn create_server_workflow(\n State(state): State>,\n Json(request): Json,\n) -> Result, ApiError> {\n // Create task\n let task = Task {\n id: Uuid::new_v4(),\n task_type: TaskType::ServerCreate,\n payload: serde_json::to_value(&request)?,\n priority: Priority::Normal,\n status: TaskStatus::Pending,\n created_at: Utc::now(),\n };\n\n // Queue task\n state.task_queue.enqueue(task).await?;\n\n // Return immediately (async execution)\n Ok(Json(WorkflowResponse {\n workflow_id: task.id,\n status: "queued",\n }))\n}\n```\n\n**Flow:**\n\n```\nUser → provisioning server create --orchestrated\n ↓\nNushell CLI prepares task\n ↓\nHTTP POST to orchestrator (localhost:9090)\n ↓\nOrchestrator queues task\n ↓\nReturns workflow ID immediately\n ↓\nUser can monitor: provisioning workflow monitor \n```\n\n### Pattern 2: Orchestrator Executes Nushell Scripts\n\n**Orchestrator Task Executor (`platform/orchestrator/src/executor.rs`):**\n\n```\n// Orchestrator spawns Nushell to execute business logic\npub async fn execute_task(task: Task) -> Result {\n match task.task_type {\n TaskType::ServerCreate => {\n // Orchestrator calls Nushell script via subprocess\n let output = Command::new("nu")\n .arg("-c")\n .arg(format!(\n "use {}/servers/create.nu; create-server '{}'",\n PROVISIONING_LIB_PATH,\n task.payload.infra_name\n ))\n .output()\n .await?;\n\n // Parse Nushell output\n let result = parse_nushell_output(&output)?;\n\n Ok(TaskResult {\n task_id: task.id,\n status: if result.success { "completed" } else { "failed" },\n output: result.data,\n })\n }\n // Other task types...\n }\n}\n```\n\n**Flow:**\n\n```\nOrchestrator task queue has pending task\n ↓\nExecutor picks up task\n ↓\nSpawns Nushell subprocess: nu -c "use servers/create.nu; create-server 'wuji'"\n ↓\nNushell executes business logic\n ↓\nReturns result to orchestrator\n ↓\nOrchestrator updates task status\n ↓\nUser monitors via: provisioning workflow status \n```\n\n### Pattern 3: Bidirectional Communication\n\n**Nushell Calls Orchestrator API:**\n\n```\n# Nushell script checks orchestrator status during execution\nexport def check-orchestrator-health [] {\n let response = (http get http://localhost:9090/health)\n\n if $response.status != "healthy" {\n error make { msg: "Orchestrator not available" }\n }\n\n $response\n}\n\n# Nushell script reports progress to orchestrator\nexport def report-progress [task_id: string, progress: int] {\n http post http://localhost:9090/tasks/$task_id/progress {\n progress: $progress\n status: "in_progress"\n }\n}\n```\n\n**Orchestrator Monitors Nushell Execution:**\n\n```\n// Orchestrator tracks Nushell subprocess\npub async fn execute_with_monitoring(task: Task) -> Result {\n let mut child = Command::new("nu")\n .arg("-c")\n .arg(&task.script)\n .stdout(Stdio::piped())\n .stderr(Stdio::piped())\n .spawn()?;\n\n // Monitor stdout/stderr in real-time\n let stdout = child.stdout.take().unwrap();\n tokio::spawn(async move {\n let reader = BufReader::new(stdout);\n let mut lines = reader.lines();\n\n while let Some(line) = lines.next_line().await.unwrap() {\n // Parse progress updates from Nushell\n if line.contains("PROGRESS:") {\n update_task_progress(&line);\n }\n }\n });\n\n // Wait for completion with timeout\n let result = tokio::time::timeout(\n Duration::from_secs(3600),\n child.wait()\n ).await??;\n\n Ok(TaskResult::from_exit_status(result))\n}\n```\n\n---\n\n## Multi-Repo Architecture Impact\n\n### Repository Split Doesn't Change Integration Model\n\n**In Multi-Repo Setup:**\n\n**Repository: `provisioning-core`**\n\n- Contains: Nushell business logic\n- Installs to: `/usr/local/lib/provisioning/`\n- Package: `provisioning-core-3.2.1.tar.gz`\n\n**Repository: `provisioning-platform`**\n\n- Contains: Rust orchestrator\n- Installs to: `/usr/local/bin/provisioning-orchestrator`\n- Package: `provisioning-platform-2.5.3.tar.gz`\n\n**Runtime Integration (Same as Monorepo):**\n\n```\nUser installs both packages:\n provisioning-core-3.2.1 → /usr/local/lib/provisioning/\n provisioning-platform-2.5.3 → /usr/local/bin/provisioning-orchestrator\n\nOrchestrator expects core at: /usr/local/lib/provisioning/\nCore expects orchestrator at: http://localhost:9090/\n\nNo code dependencies, just runtime coordination!\n```\n\n### Configuration-Based Integration\n\n**Core Package (`provisioning-core`) config:**\n\n```\n# /usr/local/share/provisioning/config/config.defaults.toml\n\n[orchestrator]\nenabled = true\nendpoint = "http://localhost:9090"\ntimeout = 60\nauto_start = true # Start orchestrator if not running\n\n[execution]\ndefault_mode = "orchestrated" # Use orchestrator by default\nfallback_to_direct = true # Fall back if orchestrator down\n```\n\n**Platform Package (`provisioning-platform`) config:**\n\n```\n# /usr/local/share/provisioning/platform/config.toml\n\n[orchestrator]\nhost = "127.0.0.1"\nport = 8080\ndata_dir = "/var/lib/provisioning/orchestrator"\n\n[executor]\nnushell_binary = "nu" # Expects nu in PATH\nprovisioning_lib = "/usr/local/lib/provisioning"\nmax_concurrent_tasks = 10\ntask_timeout_seconds = 3600\n```\n\n### Version Compatibility\n\n**Compatibility Matrix (`provisioning-distribution/versions.toml`):**\n\n```\n[compatibility.platform."2.5.3"]\ncore = "^3.2" # Platform 2.5.3 compatible with core 3.2.x\nmin-core = "3.2.0"\napi-version = "v1"\n\n[compatibility.core."3.2.1"]\nplatform = "^2.5" # Core 3.2.1 compatible with platform 2.5.x\nmin-platform = "2.5.0"\norchestrator-api = "v1"\n```\n\n---\n\n## Execution Flow Examples\n\n### Example 1: Simple Server Creation (Direct Mode)\n\n**No Orchestrator Needed:**\n\n```\nprovisioning server list\n\n# Flow:\nCLI → servers/list.nu → Query state → Return results\n(Orchestrator not involved)\n```\n\n### Example 2: Server Creation with Orchestrator\n\n**Using Orchestrator:**\n\n```\nprovisioning server create --orchestrated --infra wuji\n\n# Detailed Flow:\n1. User executes command\n ↓\n2. Nushell CLI (provisioning binary)\n ↓\n3. Reads config: orchestrator.enabled = true\n ↓\n4. Prepares task payload:\n {\n type: "server_create",\n infra: "wuji",\n params: { ... }\n }\n ↓\n5. HTTP POST → http://localhost:9090/workflows/servers/create\n ↓\n6. Orchestrator receives request\n ↓\n7. Creates task with UUID\n ↓\n8. Enqueues to task queue (file-based: /var/lib/provisioning/queue/)\n ↓\n9. Returns immediately: { workflow_id: "abc-123", status: "queued" }\n ↓\n10. User sees: "Workflow submitted: abc-123"\n ↓\n11. Orchestrator executor picks up task\n ↓\n12. Spawns Nushell subprocess:\n nu -c "use /usr/local/lib/provisioning/servers/create.nu; create-server 'wuji'"\n ↓\n13. Nushell executes business logic:\n - Reads Nickel config\n - Calls provider API (UpCloud/AWS)\n - Creates server\n - Returns result\n ↓\n14. Orchestrator captures output\n ↓\n15. Updates task status: "completed"\n ↓\n16. User monitors: provisioning workflow status abc-123\n → Shows: "Server wuji created successfully"\n```\n\n### Example 3: Batch Workflow with Dependencies\n\n**Complex Workflow:**\n\n```\nprovisioning batch submit multi-cloud-deployment.ncl\n\n# Workflow contains:\n- Create 5 servers (parallel)\n- Install Kubernetes on servers (depends on server creation)\n- Deploy applications (depends on Kubernetes)\n\n# Detailed Flow:\n1. CLI submits Nickel workflow to orchestrator\n ↓\n2. Orchestrator parses workflow\n ↓\n3. Builds dependency graph using petgraph (Rust)\n ↓\n4. Topological sort determines execution order\n ↓\n5. Creates tasks for each operation\n ↓\n6. Executes in parallel where possible:\n\n [Server 1] [Server 2] [Server 3] [Server 4] [Server 5]\n ↓ ↓ ↓ ↓ ↓\n (All execute in parallel via Nushell subprocesses)\n ↓ ↓ ↓ ↓ ↓\n └──────────┴──────────┴──────────┴──────────┘\n │\n ↓\n [All servers ready]\n ↓\n [Install Kubernetes]\n (Nushell subprocess)\n ↓\n [Kubernetes ready]\n ↓\n [Deploy applications]\n (Nushell subprocess)\n ↓\n [Complete]\n\n7. Orchestrator checkpoints state at each step\n ↓\n8. If failure occurs, can retry from checkpoint\n ↓\n9. User monitors real-time: provisioning batch monitor \n```\n\n---\n\n## Why This Architecture\n\n### Orchestrator Benefits\n\n1. **Eliminates Deep Call Stack Issues**\n\n ```text\n\n Without Orchestrator:\n template.nu → calls → cluster.nu → calls → taskserv.nu → calls → provider.nu\n (Deep nesting causes "Type not supported" errors)\n\n With Orchestrator:\n Orchestrator → spawns → Nushell subprocess (flat execution)\n (No deep nesting, fresh Nushell context for each task)\n\n ```\n\n2. **Performance Optimization**\n\n ```rust\n // Orchestrator executes tasks in parallel\n let tasks = vec![task1, task2, task3, task4, task5];\n\n let results = futures::future::join_all(\n tasks.iter().map(|t| execute_task(t))\n ).await;\n\n // 5 Nushell subprocesses run concurrently\n ```\n\n1. **Reliable State Management**\n\n```\n Orchestrator maintains:\n - Task queue (survives crashes)\n - Workflow checkpoints (resume on failure)\n - Progress tracking (real-time monitoring)\n - Retry logic (automatic recovery)\n```\n\n1. **Clean Separation**\n\n```\n Orchestrator (Rust): Performance, concurrency, state\n Business Logic (Nushell): Providers, taskservs, workflows\n\n Each does what it's best at!\n```\n\n### Why NOT Pure Rust\n\n**Question:** Why not implement everything in Rust?\n\n**Answer:**\n\n1. **Nushell is perfect for infrastructure automation:**\n - Shell-like scripting for system operations\n - Built-in structured data handling\n - Easy template rendering\n - Readable business logic\n\n2. **Rapid iteration:**\n - Change Nushell scripts without recompiling\n - Community can contribute Nushell modules\n - Template-based configuration generation\n\n3. **Best of both worlds:**\n - Rust: Performance, type safety, concurrency\n - Nushell: Flexibility, readability, ease of use\n\n---\n\n## Multi-Repo Integration Example\n\n### Installation\n\n**User installs bundle:**\n\n```\ncurl -fsSL https://get.provisioning.io | sh\n\n# Installs:\n1. provisioning-core-3.2.1.tar.gz\n → /usr/local/bin/provisioning (Nushell CLI)\n → /usr/local/lib/provisioning/ (Nushell libraries)\n → /usr/local/share/provisioning/ (configs, templates)\n\n2. provisioning-platform-2.5.3.tar.gz\n → /usr/local/bin/provisioning-orchestrator (Rust binary)\n → /usr/local/share/provisioning/platform/ (platform configs)\n\n3. Sets up systemd/launchd service for orchestrator\n```\n\n### Runtime Coordination\n\n**Core package expects orchestrator:**\n\n```\n# core/nulib/lib_provisioning/orchestrator/client.nu\n\n# Check if orchestrator is running\nexport def orchestrator-available [] {\n let config = (load-config)\n let endpoint = $config.orchestrator.endpoint\n\n try {\n let response = (http get $"($endpoint)/health")\n $response.status == "healthy"\n } catch {\n false\n }\n}\n\n# Auto-start orchestrator if needed\nexport def ensure-orchestrator [] {\n if not (orchestrator-available) {\n if (load-config).orchestrator.auto_start {\n print "Starting orchestrator..."\n ^provisioning-orchestrator --daemon\n sleep 2sec\n }\n }\n}\n```\n\n**Platform package executes core scripts:**\n\n```\n// platform/orchestrator/src/executor/nushell.rs\n\npub struct NushellExecutor {\n provisioning_lib: PathBuf, // /usr/local/lib/provisioning\n nu_binary: PathBuf, // nu (from PATH)\n}\n\nimpl NushellExecutor {\n pub async fn execute_script(&self, script: &str) -> Result {\n Command::new(&self.nu_binary)\n .env("NU_LIB_DIRS", &self.provisioning_lib)\n .arg("-c")\n .arg(script)\n .output()\n .await\n }\n\n pub async fn execute_module_function(\n &self,\n module: &str,\n function: &str,\n args: &[String],\n ) -> Result {\n let script = format!(\n "use {}/{}; {} {}",\n self.provisioning_lib.display(),\n module,\n function,\n args.join(" ")\n );\n\n self.execute_script(&script).await\n }\n}\n```\n\n---\n\n## Configuration Examples\n\n### Core Package Config\n\n**`/usr/local/share/provisioning/config/config.defaults.toml`:**\n\n```\n[orchestrator]\nenabled = true\nendpoint = "http://localhost:9090"\ntimeout_seconds = 60\nauto_start = true\nfallback_to_direct = true\n\n[execution]\n# Modes: "direct", "orchestrated", "auto"\ndefault_mode = "auto" # Auto-detect based on complexity\n\n# Operations that always use orchestrator\nforce_orchestrated = [\n "server.create",\n "cluster.create",\n "batch.*",\n "workflow.*"\n]\n\n# Operations that always run direct\nforce_direct = [\n "*.list",\n "*.show",\n "help",\n "version"\n]\n```\n\n### Platform Package Config\n\n**`/usr/local/share/provisioning/platform/config.toml`:**\n\n```\n[server]\nhost = "127.0.0.1"\nport = 8080\n\n[storage]\nbackend = "filesystem" # or "surrealdb"\ndata_dir = "/var/lib/provisioning/orchestrator"\n\n[executor]\nmax_concurrent_tasks = 10\ntask_timeout_seconds = 3600\ncheckpoint_interval_seconds = 30\n\n[nushell]\nbinary = "nu" # Expects nu in PATH\nprovisioning_lib = "/usr/local/lib/provisioning"\nenv_vars = { NU_LIB_DIRS = "/usr/local/lib/provisioning" }\n```\n\n---\n\n## Key Takeaways\n\n### 1. **Orchestrator is Essential**\n\n- Solves deep call stack problems\n- Provides performance optimization\n- Enables complex workflows\n- NOT optional for production use\n\n### 2. **Integration is Loose but Coordinated**\n\n- No code dependencies between repos\n- Runtime integration via CLI + REST API\n- Configuration-driven coordination\n- Works in both monorepo and multi-repo\n\n### 3. **Best of Both Worlds**\n\n- Rust: High-performance coordination\n- Nushell: Flexible business logic\n- Clean separation of concerns\n- Each technology does what it's best at\n\n### 4. **Multi-Repo Doesn't Change Integration**\n\n- Same runtime model as monorepo\n- Package installation sets up paths\n- Configuration enables discovery\n- Versioning ensures compatibility\n\n---\n\n## Conclusion\n\nThe confusing example in the multi-repo doc was **oversimplified**. The real architecture is:\n\n```\n✅ Orchestrator IS USED and IS ESSENTIAL\n✅ Platform (Rust) coordinates Core (Nushell) execution\n✅ Loose coupling via CLI + REST API (not code dependencies)\n✅ Works identically in monorepo and multi-repo\n✅ Configuration-based integration (no hardcoded paths)\n```\n\nThe orchestrator provides:\n\n- Performance layer (async, parallel execution)\n- Workflow engine (complex dependencies)\n- State management (checkpoints, recovery)\n- Task queue (reliable execution)\n\nWhile Nushell provides:\n\n- Business logic (providers, taskservs, clusters)\n- Template rendering (Jinja2 via nu_plugin_tera)\n- Configuration management (KCL integration)\n- User-facing scripting\n\n**Multi-repo just splits WHERE the code lives, not HOW it works together.**