2025-12-11 21:50:42 +00:00
<!DOCTYPE HTML>
< html lang = "en" class = "ayu sidebar-visible" dir = "ltr" >
< head >
<!-- Book generated using mdBook -->
< meta charset = "UTF-8" >
< title > Orchestrator Integration Model - Provisioning Platform Documentation< / title >
<!-- Custom HTML head -->
2026-01-14 01:56:30 +00:00
< meta name = "description" content = "Complete documentation for the Provisioning Platform - Infrastructure automation with Nushell, Nickel, and Rust" >
2025-12-11 21:50:42 +00:00
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< meta name = "theme-color" content = "#ffffff" >
< link rel = "icon" href = "../favicon.svg" >
< link rel = "shortcut icon" href = "../favicon.png" >
< link rel = "stylesheet" href = "../css/variables.css" >
< link rel = "stylesheet" href = "../css/general.css" >
< link rel = "stylesheet" href = "../css/chrome.css" >
< link rel = "stylesheet" href = "../css/print.css" media = "print" >
<!-- Fonts -->
< link rel = "stylesheet" href = "../FontAwesome/css/font-awesome.css" >
< link rel = "stylesheet" href = "../fonts/fonts.css" >
<!-- Highlight.js Stylesheets -->
< link rel = "stylesheet" id = "highlight-css" href = "../highlight.css" >
< link rel = "stylesheet" id = "tomorrow-night-css" href = "../tomorrow-night.css" >
< link rel = "stylesheet" id = "ayu-highlight-css" href = "../ayu-highlight.css" >
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
< script >
const path_to_root = "../";
const default_light_theme = "ayu";
const default_dark_theme = "navy";
< / script >
<!-- Start loading toc.js asap -->
< script src = "../toc.js" > < / script >
< / head >
< body >
< div id = "mdbook-help-container" >
< div id = "mdbook-help-popup" >
< h2 class = "mdbook-help-title" > Keyboard shortcuts< / h2 >
< div >
< p > Press < kbd > ←< / kbd > or < kbd > →< / kbd > to navigate between chapters< / p >
< p > Press < kbd > S< / kbd > or < kbd > /< / kbd > to search in the book< / p >
< p > Press < kbd > ?< / kbd > to show this help< / p >
< p > Press < kbd > Esc< / kbd > to hide this help< / p >
< / div >
< / div >
< / div >
< div id = "body-container" >
<!-- Work around some values being stored in localStorage wrapped in quotes -->
< script >
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') & & theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') & & sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
< / script >
<!-- Set the theme before any content is loaded, prevents flash -->
< script >
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('ayu')
html.classList.add(theme);
html.classList.add("js");
< / script >
< input type = "checkbox" id = "sidebar-toggle-anchor" class = "hidden" >
<!-- Hide / unhide sidebar before it is displayed -->
< script >
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
< / script >
< nav id = "sidebar" class = "sidebar" aria-label = "Table of contents" >
<!-- populated by js -->
< mdbook-sidebar-scrollbox class = "sidebar-scrollbox" > < / mdbook-sidebar-scrollbox >
< noscript >
< iframe class = "sidebar-iframe-outer" src = "../toc.html" > < / iframe >
< / noscript >
< div id = "sidebar-resize-handle" class = "sidebar-resize-handle" >
< div class = "sidebar-resize-indicator" > < / div >
< / div >
< / nav >
< div id = "page-wrapper" class = "page-wrapper" >
< div class = "page" >
< div id = "menu-bar-hover-placeholder" > < / div >
< div id = "menu-bar" class = "menu-bar sticky" >
< div class = "left-buttons" >
< label id = "sidebar-toggle" class = "icon-button" for = "sidebar-toggle-anchor" title = "Toggle Table of Contents" aria-label = "Toggle Table of Contents" aria-controls = "sidebar" >
< i class = "fa fa-bars" > < / i >
< / label >
< button id = "theme-toggle" class = "icon-button" type = "button" title = "Change theme" aria-label = "Change theme" aria-haspopup = "true" aria-expanded = "false" aria-controls = "theme-list" >
< i class = "fa fa-paint-brush" > < / i >
< / button >
< ul id = "theme-list" class = "theme-popup" aria-label = "Themes" role = "menu" >
< li role = "none" > < button role = "menuitem" class = "theme" id = "default_theme" > Auto< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "light" > Light< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "rust" > Rust< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "coal" > Coal< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "navy" > Navy< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "ayu" > Ayu< / button > < / li >
< / ul >
< button id = "search-toggle" class = "icon-button" type = "button" title = "Search (`/`)" aria-label = "Toggle Searchbar" aria-expanded = "false" aria-keyshortcuts = "/ s" aria-controls = "searchbar" >
< i class = "fa fa-search" > < / i >
< / button >
< / div >
< h1 class = "menu-title" > Provisioning Platform Documentation< / h1 >
< div class = "right-buttons" >
< a href = "../print.html" title = "Print this book" aria-label = "Print this book" >
< i id = "print-button" class = "fa fa-print" > < / i >
< / a >
< a href = "https://github.com/provisioning/provisioning-platform" title = "Git repository" aria-label = "Git repository" >
< i id = "git-repository-button" class = "fa fa-github" > < / i >
< / a >
< a href = "https://github.com/provisioning/provisioning-platform/edit/main/provisioning/docs/src/architecture/orchestrator-integration-model.md" title = "Suggest an edit" aria-label = "Suggest an edit" >
< i id = "git-edit-button" class = "fa fa-edit" > < / i >
< / a >
< / div >
< / div >
< div id = "search-wrapper" class = "hidden" >
< form id = "searchbar-outer" class = "searchbar-outer" >
< input type = "search" id = "searchbar" name = "searchbar" placeholder = "Search this book ..." aria-controls = "searchresults-outer" aria-describedby = "searchresults-header" >
< / form >
< div id = "searchresults-outer" class = "searchresults-outer hidden" >
< div id = "searchresults-header" class = "searchresults-header" > < / div >
< ul id = "searchresults" >
< / ul >
< / div >
< / div >
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
< script >
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
< / script >
< div id = "content" class = "content" >
< main >
< h1 id = "orchestrator-integration-model---deep-dive" > < a class = "header" href = "#orchestrator-integration-model---deep-dive" > Orchestrator Integration Model - Deep Dive< / a > < / h1 >
< p > < strong > Date:< / strong > 2025-10-01
< strong > Status:< / strong > Clarification Document
< strong > Related:< / strong > < a href = "multi-repo-strategy.html" > Multi-Repo Strategy< / a > , < a href = "../user/hybrid-orchestrator.html" > Hybrid Orchestrator v3.0< / a > < / p >
< h2 id = "executive-summary" > < a class = "header" href = "#executive-summary" > Executive Summary< / a > < / h2 >
2026-01-14 01:56:30 +00:00
< p > This document clarifies < strong > how the Rust orchestrator integrates with Nushell core< / strong > in both monorepo and multi-repo architectures. The orchestrator is
a < strong > critical performance layer< / strong > that coordinates Nushell business logic execution, solving deep call stack limitations while preserving all existing
functionality.< / p >
2025-12-11 21:50:42 +00:00
< hr / >
< h2 id = "current-architecture-hybrid-orchestrator-v30" > < a class = "header" href = "#current-architecture-hybrid-orchestrator-v30" > Current Architecture (Hybrid Orchestrator v3.0)< / a > < / h2 >
< h3 id = "the-problem-being-solved" > < a class = "header" href = "#the-problem-being-solved" > The Problem Being Solved< / a > < / h3 >
< p > < strong > Original Issue:< / strong > < / p >
2026-01-08 09:55:37 +00:00
< pre > < code class = "language-plaintext" > Deep call stack in Nushell (template.nu:71)
2025-12-11 21:50:42 +00:00
→ "Type not supported" errors
→ Cannot handle complex nested workflows
→ Performance bottlenecks with recursive calls
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Solution:< / strong > Rust orchestrator provides:< / p >
< ol >
< li > < strong > Task queue management< / strong > (file-based, reliable)< / li >
< li > < strong > Priority scheduling< / strong > (intelligent task ordering)< / li >
< li > < strong > Deep call stack elimination< / strong > (Rust handles recursion)< / li >
< li > < strong > Performance optimization< / strong > (async/await, parallel execution)< / li >
< li > < strong > State management< / strong > (workflow checkpointing)< / li >
< / ol >
< h3 id = "how-it-works-today-monorepo" > < a class = "header" href = "#how-it-works-today-monorepo" > How It Works Today (Monorepo)< / a > < / h3 >
< pre > < code class = "language-plaintext" > ┌─────────────────────────────────────────────────────────────┐
2025-12-11 21:50:42 +00:00
│ User │
└───────────────────────────┬─────────────────────────────────┘
│ calls
↓
┌───────────────┐
│ provisioning │ (Nushell CLI)
│ CLI │
└───────┬───────┘
│
┌───────────────────┼───────────────────┐
│ │ │
↓ ↓ ↓
┌───────────────┐ ┌───────────────┐ ┌──────────────┐
│ Direct Mode │ │Orchestrated │ │ Workflow │
│ (Simple ops) │ │ Mode │ │ Mode │
└───────────────┘ └───────┬───────┘ └──────┬───────┘
│ │
↓ ↓
┌────────────────────────────────┐
│ Rust Orchestrator Service │
│ (Background daemon) │
│ │
│ • Task Queue (file-based) │
│ • Priority Scheduler │
│ • Workflow Engine │
│ • REST API Server │
└────────┬───────────────────────┘
│ spawns
↓
┌────────────────┐
│ Nushell │
│ Business Logic │
│ │
│ • servers.nu │
│ • taskservs.nu │
│ • clusters.nu │
└────────────────┘
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "three-execution-modes" > < a class = "header" href = "#three-execution-modes" > Three Execution Modes< / a > < / h3 >
< h4 id = "mode-1-direct-mode-simple-operations" > < a class = "header" href = "#mode-1-direct-mode-simple-operations" > Mode 1: Direct Mode (Simple Operations)< / a > < / h4 >
< pre > < code class = "language-bash" > # No orchestrator needed
2025-12-11 21:50:42 +00:00
provisioning server list
provisioning env
provisioning help
# Direct Nushell execution
provisioning (CLI) → Nushell scripts → Result
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h4 id = "mode-2-orchestrated-mode-complex-operations" > < a class = "header" href = "#mode-2-orchestrated-mode-complex-operations" > Mode 2: Orchestrated Mode (Complex Operations)< / a > < / h4 >
< pre > < code class = "language-bash" > # Uses orchestrator for coordination
2025-12-11 21:50:42 +00:00
provisioning server create --orchestrated
# Flow:
provisioning CLI → Orchestrator API → Task Queue → Nushell executor
↓
Result back to user
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h4 id = "mode-3-workflow-mode-batch-operations" > < a class = "header" href = "#mode-3-workflow-mode-batch-operations" > Mode 3: Workflow Mode (Batch Operations)< / a > < / h4 >
< pre > < code class = "language-bash" > # Complex workflows with dependencies
provisioning workflow submit server-cluster.ncl
2025-12-11 21:50:42 +00:00
# Flow:
provisioning CLI → Orchestrator Workflow Engine → Dependency Graph
↓
Parallel task execution
↓
Nushell scripts for each task
↓
Checkpoint state
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< hr / >
< h2 id = "integration-patterns" > < a class = "header" href = "#integration-patterns" > Integration Patterns< / a > < / h2 >
< h3 id = "pattern-1-cli-submits-tasks-to-orchestrator" > < a class = "header" href = "#pattern-1-cli-submits-tasks-to-orchestrator" > Pattern 1: CLI Submits Tasks to Orchestrator< / a > < / h3 >
< p > < strong > Current Implementation:< / strong > < / p >
< p > < strong > Nushell CLI (< code > core/nulib/workflows/server_create.nu< / code > ):< / strong > < / p >
< pre > < code class = "language-nushell" > # Submit server creation workflow to orchestrator
2025-12-11 21:50:42 +00:00
export def server_create_workflow [
infra_name: string
--orchestrated
] {
if $orchestrated {
# Submit task to orchestrator
let task = {
type: "server_create"
infra: $infra_name
params: { ... }
}
# POST to orchestrator REST API
http post http://localhost:9090/workflows/servers/create $task
} else {
# Direct execution (old way)
do-server-create $infra_name
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Rust Orchestrator (< code > platform/orchestrator/src/api/workflows.rs< / code > ):< / strong > < / p >
< pre > < code class = "language-rust" > // Receive workflow submission from Nushell CLI
2025-12-11 21:50:42 +00:00
#[axum::debug_handler]
async fn create_server_workflow(
State(state): State< Arc< AppState> > ,
Json(request): Json< ServerCreateRequest> ,
) -> Result< Json< WorkflowResponse> , ApiError> {
// Create task
let task = Task {
id: Uuid::new_v4(),
task_type: TaskType::ServerCreate,
payload: serde_json::to_value(& request)?,
priority: Priority::Normal,
status: TaskStatus::Pending,
created_at: Utc::now(),
};
// Queue task
state.task_queue.enqueue(task).await?;
// Return immediately (async execution)
Ok(Json(WorkflowResponse {
workflow_id: task.id,
status: "queued",
}))
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< p > < strong > Flow:< / strong > < / p >
< pre > < code class = "language-plaintext" > User → provisioning server create --orchestrated
2025-12-11 21:50:42 +00:00
↓
Nushell CLI prepares task
↓
HTTP POST to orchestrator (localhost:9090)
↓
Orchestrator queues task
↓
Returns workflow ID immediately
↓
User can monitor: provisioning workflow monitor < id>
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "pattern-2-orchestrator-executes-nushell-scripts" > < a class = "header" href = "#pattern-2-orchestrator-executes-nushell-scripts" > Pattern 2: Orchestrator Executes Nushell Scripts< / a > < / h3 >
< p > < strong > Orchestrator Task Executor (< code > platform/orchestrator/src/executor.rs< / code > ):< / strong > < / p >
< pre > < code class = "language-rust" > // Orchestrator spawns Nushell to execute business logic
2025-12-11 21:50:42 +00:00
pub async fn execute_task(task: Task) -> Result< TaskResult> {
match task.task_type {
TaskType::ServerCreate => {
// Orchestrator calls Nushell script via subprocess
let output = Command::new("nu")
.arg("-c")
.arg(format!(
"use {}/servers/create.nu; create-server '{}'",
PROVISIONING_LIB_PATH,
task.payload.infra_name
))
.output()
.await?;
// Parse Nushell output
let result = parse_nushell_output(& output)?;
Ok(TaskResult {
task_id: task.id,
status: if result.success { "completed" } else { "failed" },
output: result.data,
})
}
// Other task types...
}
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< p > < strong > Flow:< / strong > < / p >
< pre > < code class = "language-plaintext" > Orchestrator task queue has pending task
2025-12-11 21:50:42 +00:00
↓
Executor picks up task
↓
Spawns Nushell subprocess: nu -c "use servers/create.nu; create-server 'wuji'"
↓
Nushell executes business logic
↓
Returns result to orchestrator
↓
Orchestrator updates task status
↓
User monitors via: provisioning workflow status < id>
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "pattern-3-bidirectional-communication" > < a class = "header" href = "#pattern-3-bidirectional-communication" > Pattern 3: Bidirectional Communication< / a > < / h3 >
< p > < strong > Nushell Calls Orchestrator API:< / strong > < / p >
< pre > < code class = "language-nushell" > # Nushell script checks orchestrator status during execution
2025-12-11 21:50:42 +00:00
export def check-orchestrator-health [] {
let response = (http get http://localhost:9090/health)
if $response.status != "healthy" {
error make { msg: "Orchestrator not available" }
}
$response
}
# Nushell script reports progress to orchestrator
export def report-progress [task_id: string, progress: int] {
http post http://localhost:9090/tasks/$task_id/progress {
progress: $progress
status: "in_progress"
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Orchestrator Monitors Nushell Execution:< / strong > < / p >
< pre > < code class = "language-rust" > // Orchestrator tracks Nushell subprocess
2025-12-11 21:50:42 +00:00
pub async fn execute_with_monitoring(task: Task) -> Result< TaskResult> {
let mut child = Command::new("nu")
.arg("-c")
.arg(& task.script)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
// Monitor stdout/stderr in real-time
let stdout = child.stdout.take().unwrap();
tokio::spawn(async move {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await.unwrap() {
// Parse progress updates from Nushell
if line.contains("PROGRESS:") {
update_task_progress(& line);
}
}
});
// Wait for completion with timeout
let result = tokio::time::timeout(
Duration::from_secs(3600),
child.wait()
).await??;
Ok(TaskResult::from_exit_status(result))
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< hr / >
< h2 id = "multi-repo-architecture-impact" > < a class = "header" href = "#multi-repo-architecture-impact" > Multi-Repo Architecture Impact< / a > < / h2 >
< h3 id = "repository-split-doesnt-change-integration-model" > < a class = "header" href = "#repository-split-doesnt-change-integration-model" > Repository Split Doesn’ t Change Integration Model< / a > < / h3 >
< p > < strong > In Multi-Repo Setup:< / strong > < / p >
< p > < strong > Repository: < code > provisioning-core< / code > < / strong > < / p >
< ul >
< li > Contains: Nushell business logic< / li >
< li > Installs to: < code > /usr/local/lib/provisioning/< / code > < / li >
< li > Package: < code > provisioning-core-3.2.1.tar.gz< / code > < / li >
< / ul >
< p > < strong > Repository: < code > provisioning-platform< / code > < / strong > < / p >
< ul >
< li > Contains: Rust orchestrator< / li >
< li > Installs to: < code > /usr/local/bin/provisioning-orchestrator< / code > < / li >
< li > Package: < code > provisioning-platform-2.5.3.tar.gz< / code > < / li >
< / ul >
< p > < strong > Runtime Integration (Same as Monorepo):< / strong > < / p >
< pre > < code class = "language-plaintext" > User installs both packages:
2025-12-11 21:50:42 +00:00
provisioning-core-3.2.1 → /usr/local/lib/provisioning/
provisioning-platform-2.5.3 → /usr/local/bin/provisioning-orchestrator
Orchestrator expects core at: /usr/local/lib/provisioning/
Core expects orchestrator at: http://localhost:9090/
No code dependencies, just runtime coordination!
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "configuration-based-integration" > < a class = "header" href = "#configuration-based-integration" > Configuration-Based Integration< / a > < / h3 >
< p > < strong > Core Package (< code > provisioning-core< / code > ) config:< / strong > < / p >
< pre > < code class = "language-toml" > # /usr/local/share/provisioning/config/config.defaults.toml
2025-12-11 21:50:42 +00:00
[orchestrator]
enabled = true
endpoint = "http://localhost:9090"
timeout = 60
auto_start = true # Start orchestrator if not running
[execution]
default_mode = "orchestrated" # Use orchestrator by default
fallback_to_direct = true # Fall back if orchestrator down
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Platform Package (< code > provisioning-platform< / code > ) config:< / strong > < / p >
< pre > < code class = "language-toml" > # /usr/local/share/provisioning/platform/config.toml
2025-12-11 21:50:42 +00:00
[orchestrator]
host = "127.0.0.1"
port = 8080
data_dir = "/var/lib/provisioning/orchestrator"
[executor]
nushell_binary = "nu" # Expects nu in PATH
provisioning_lib = "/usr/local/lib/provisioning"
max_concurrent_tasks = 10
task_timeout_seconds = 3600
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "version-compatibility" > < a class = "header" href = "#version-compatibility" > Version Compatibility< / a > < / h3 >
< p > < strong > Compatibility Matrix (< code > provisioning-distribution/versions.toml< / code > ):< / strong > < / p >
< pre > < code class = "language-toml" > [compatibility.platform."2.5.3"]
2025-12-11 21:50:42 +00:00
core = "^3.2" # Platform 2.5.3 compatible with core 3.2.x
min-core = "3.2.0"
api-version = "v1"
[compatibility.core."3.2.1"]
platform = "^2.5" # Core 3.2.1 compatible with platform 2.5.x
min-platform = "2.5.0"
orchestrator-api = "v1"
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< hr / >
< h2 id = "execution-flow-examples" > < a class = "header" href = "#execution-flow-examples" > Execution Flow Examples< / a > < / h2 >
< h3 id = "example-1-simple-server-creation-direct-mode" > < a class = "header" href = "#example-1-simple-server-creation-direct-mode" > Example 1: Simple Server Creation (Direct Mode)< / a > < / h3 >
< p > < strong > No Orchestrator Needed:< / strong > < / p >
< pre > < code class = "language-bash" > provisioning server list
2025-12-11 21:50:42 +00:00
# Flow:
CLI → servers/list.nu → Query state → Return results
(Orchestrator not involved)
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "example-2-server-creation-with-orchestrator" > < a class = "header" href = "#example-2-server-creation-with-orchestrator" > Example 2: Server Creation with Orchestrator< / a > < / h3 >
< p > < strong > Using Orchestrator:< / strong > < / p >
< pre > < code class = "language-bash" > provisioning server create --orchestrated --infra wuji
2025-12-11 21:50:42 +00:00
# Detailed Flow:
1. User executes command
↓
2. Nushell CLI (provisioning binary)
↓
3. Reads config: orchestrator.enabled = true
↓
4. Prepares task payload:
{
type: "server_create",
infra: "wuji",
params: { ... }
}
↓
5. HTTP POST → http://localhost:9090/workflows/servers/create
↓
6. Orchestrator receives request
↓
7. Creates task with UUID
↓
8. Enqueues to task queue (file-based: /var/lib/provisioning/queue/)
↓
9. Returns immediately: { workflow_id: "abc-123", status: "queued" }
↓
10. User sees: "Workflow submitted: abc-123"
↓
11. Orchestrator executor picks up task
↓
12. Spawns Nushell subprocess:
nu -c "use /usr/local/lib/provisioning/servers/create.nu; create-server 'wuji'"
↓
13. Nushell executes business logic:
2026-01-12 04:42:18 +00:00
- Reads Nickel config
2025-12-11 21:50:42 +00:00
- Calls provider API (UpCloud/AWS)
- Creates server
- Returns result
↓
14. Orchestrator captures output
↓
15. Updates task status: "completed"
↓
16. User monitors: provisioning workflow status abc-123
→ Shows: "Server wuji created successfully"
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "example-3-batch-workflow-with-dependencies" > < a class = "header" href = "#example-3-batch-workflow-with-dependencies" > Example 3: Batch Workflow with Dependencies< / a > < / h3 >
< p > < strong > Complex Workflow:< / strong > < / p >
< pre > < code class = "language-bash" > provisioning batch submit multi-cloud-deployment.ncl
2025-12-11 21:50:42 +00:00
# Workflow contains:
- Create 5 servers (parallel)
- Install Kubernetes on servers (depends on server creation)
- Deploy applications (depends on Kubernetes)
# Detailed Flow:
2026-01-12 04:42:18 +00:00
1. CLI submits Nickel workflow to orchestrator
2025-12-11 21:50:42 +00:00
↓
2. Orchestrator parses workflow
↓
3. Builds dependency graph using petgraph (Rust)
↓
4. Topological sort determines execution order
↓
5. Creates tasks for each operation
↓
6. Executes in parallel where possible:
[Server 1] [Server 2] [Server 3] [Server 4] [Server 5]
↓ ↓ ↓ ↓ ↓
(All execute in parallel via Nushell subprocesses)
↓ ↓ ↓ ↓ ↓
└──────────┴──────────┴──────────┴──────────┘
│
↓
[All servers ready]
↓
[Install Kubernetes]
(Nushell subprocess)
↓
[Kubernetes ready]
↓
[Deploy applications]
(Nushell subprocess)
↓
[Complete]
7. Orchestrator checkpoints state at each step
↓
8. If failure occurs, can retry from checkpoint
↓
9. User monitors real-time: provisioning batch monitor < id>
< / code > < / pre >
2026-01-12 04:42:18 +00:00
< hr / >
< h2 id = "why-this-architecture" > < a class = "header" href = "#why-this-architecture" > Why This Architecture< / a > < / h2 >
< h3 id = "orchestrator-benefits" > < a class = "header" href = "#orchestrator-benefits" > Orchestrator Benefits< / a > < / h3 >
< ol >
< li >
< p > < strong > Eliminates Deep Call Stack Issues< / strong > < / p >
2026-01-14 01:56:30 +00:00
< pre > < code class = "language-text" >
2026-01-12 04:42:18 +00:00
Without Orchestrator:
2025-12-11 21:50:42 +00:00
template.nu → calls → cluster.nu → calls → taskserv.nu → calls → provider.nu
2026-01-12 04:42:18 +00:00
(Deep nesting causes "Type not supported" errors)
With Orchestrator:
2025-12-11 21:50:42 +00:00
Orchestrator → spawns → Nushell subprocess (flat execution)
2026-01-12 04:42:18 +00:00
(No deep nesting, fresh Nushell context for each task)
< / code > < / pre >
< / li >
2026-01-14 01:56:30 +00:00
< li >
< p > < strong > Performance Optimization< / strong > < / p >
< pre > < code class = "language-rust" > // Orchestrator executes tasks in parallel
let tasks = vec![task1, task2, task3, task4, task5];
2025-12-11 21:50:42 +00:00
2026-01-14 01:56:30 +00:00
let results = futures::future::join_all(
tasks.iter().map(|t| execute_task(t))
).await;
2025-12-11 21:50:42 +00:00
2026-01-14 01:56:30 +00:00
// 5 Nushell subprocesses run concurrently< / code > < / pre >
< / li >
< li >
< p > < strong > Reliable State Management< / strong > < / p >
< / li >
2025-12-11 21:50:42 +00:00
< / ol >
2026-01-08 09:55:37 +00:00
< pre > < code class = "language-plaintext" > Orchestrator maintains:
- Task queue (survives crashes)
- Workflow checkpoints (resume on failure)
- Progress tracking (real-time monitoring)
- Retry logic (automatic recovery)
< / code > < / pre >
< ol >
< li > < strong > Clean Separation< / strong > < / li >
< / ol >
< pre > < code class = "language-plaintext" > Orchestrator (Rust): Performance, concurrency, state
Business Logic (Nushell): Providers, taskservs, workflows
Each does what it's best at!
< / code > < / pre >
2026-01-12 04:42:18 +00:00
< h3 id = "why-not-pure-rust" > < a class = "header" href = "#why-not-pure-rust" > Why NOT Pure Rust< / a > < / h3 >
2025-12-11 21:50:42 +00:00
< p > < strong > Question:< / strong > Why not implement everything in Rust?< / p >
< p > < strong > Answer:< / strong > < / p >
< ol >
< li >
< p > < strong > Nushell is perfect for infrastructure automation:< / strong > < / p >
< ul >
< li > Shell-like scripting for system operations< / li >
< li > Built-in structured data handling< / li >
< li > Easy template rendering< / li >
< li > Readable business logic< / li >
< / ul >
< / li >
< li >
< p > < strong > Rapid iteration:< / strong > < / p >
< ul >
< li > Change Nushell scripts without recompiling< / li >
< li > Community can contribute Nushell modules< / li >
< li > Template-based configuration generation< / li >
< / ul >
< / li >
< li >
< p > < strong > Best of both worlds:< / strong > < / p >
< ul >
< li > Rust: Performance, type safety, concurrency< / li >
< li > Nushell: Flexibility, readability, ease of use< / li >
< / ul >
< / li >
< / ol >
< hr / >
< h2 id = "multi-repo-integration-example" > < a class = "header" href = "#multi-repo-integration-example" > Multi-Repo Integration Example< / a > < / h2 >
< h3 id = "installation" > < a class = "header" href = "#installation" > Installation< / a > < / h3 >
< p > < strong > User installs bundle:< / strong > < / p >
< pre > < code class = "language-bash" > curl -fsSL https://get.provisioning.io | sh
# Installs:
1. provisioning-core-3.2.1.tar.gz
→ /usr/local/bin/provisioning (Nushell CLI)
→ /usr/local/lib/provisioning/ (Nushell libraries)
→ /usr/local/share/provisioning/ (configs, templates)
2. provisioning-platform-2.5.3.tar.gz
→ /usr/local/bin/provisioning-orchestrator (Rust binary)
→ /usr/local/share/provisioning/platform/ (platform configs)
3. Sets up systemd/launchd service for orchestrator
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "runtime-coordination" > < a class = "header" href = "#runtime-coordination" > Runtime Coordination< / a > < / h3 >
< p > < strong > Core package expects orchestrator:< / strong > < / p >
< pre > < code class = "language-nushell" > # core/nulib/lib_provisioning/orchestrator/client.nu
2025-12-11 21:50:42 +00:00
# Check if orchestrator is running
export def orchestrator-available [] {
let config = (load-config)
let endpoint = $config.orchestrator.endpoint
try {
let response = (http get $"($endpoint)/health")
$response.status == "healthy"
} catch {
false
}
}
# Auto-start orchestrator if needed
export def ensure-orchestrator [] {
if not (orchestrator-available) {
if (load-config).orchestrator.auto_start {
print "Starting orchestrator..."
^provisioning-orchestrator --daemon
sleep 2sec
}
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Platform package executes core scripts:< / strong > < / p >
< pre > < code class = "language-rust" > // platform/orchestrator/src/executor/nushell.rs
2025-12-11 21:50:42 +00:00
pub struct NushellExecutor {
provisioning_lib: PathBuf, // /usr/local/lib/provisioning
nu_binary: PathBuf, // nu (from PATH)
}
impl NushellExecutor {
pub async fn execute_script(& self, script: & str) -> Result< Output> {
Command::new(& self.nu_binary)
.env("NU_LIB_DIRS", & self.provisioning_lib)
.arg("-c")
.arg(script)
.output()
.await
}
pub async fn execute_module_function(
& self,
module: & str,
function: & str,
args: & [String],
) -> Result< Output> {
let script = format!(
"use {}/{}; {} {}",
self.provisioning_lib.display(),
module,
function,
args.join(" ")
);
self.execute_script(& script).await
}
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< hr / >
< h2 id = "configuration-examples" > < a class = "header" href = "#configuration-examples" > Configuration Examples< / a > < / h2 >
< h3 id = "core-package-config" > < a class = "header" href = "#core-package-config" > Core Package Config< / a > < / h3 >
< p > < strong > < code > /usr/local/share/provisioning/config/config.defaults.toml< / code > :< / strong > < / p >
< pre > < code class = "language-toml" > [orchestrator]
2025-12-11 21:50:42 +00:00
enabled = true
endpoint = "http://localhost:9090"
timeout_seconds = 60
auto_start = true
fallback_to_direct = true
[execution]
# Modes: "direct", "orchestrated", "auto"
default_mode = "auto" # Auto-detect based on complexity
# Operations that always use orchestrator
force_orchestrated = [
"server.create",
"cluster.create",
"batch.*",
"workflow.*"
]
# Operations that always run direct
force_direct = [
"*.list",
"*.show",
"help",
"version"
]
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "platform-package-config" > < a class = "header" href = "#platform-package-config" > Platform Package Config< / a > < / h3 >
< p > < strong > < code > /usr/local/share/provisioning/platform/config.toml< / code > :< / strong > < / p >
< pre > < code class = "language-toml" > [server]
2025-12-11 21:50:42 +00:00
host = "127.0.0.1"
port = 8080
[storage]
backend = "filesystem" # or "surrealdb"
data_dir = "/var/lib/provisioning/orchestrator"
[executor]
max_concurrent_tasks = 10
task_timeout_seconds = 3600
checkpoint_interval_seconds = 30
[nushell]
binary = "nu" # Expects nu in PATH
provisioning_lib = "/usr/local/lib/provisioning"
env_vars = { NU_LIB_DIRS = "/usr/local/lib/provisioning" }
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< hr / >
< h2 id = "key-takeaways" > < a class = "header" href = "#key-takeaways" > Key Takeaways< / a > < / h2 >
< h3 id = "1-orchestrator-is-essential" > < a class = "header" href = "#1-orchestrator-is-essential" > 1. < strong > Orchestrator is Essential< / strong > < / a > < / h3 >
< ul >
< li > Solves deep call stack problems< / li >
< li > Provides performance optimization< / li >
< li > Enables complex workflows< / li >
< li > NOT optional for production use< / li >
< / ul >
< h3 id = "2-integration-is-loose-but-coordinated" > < a class = "header" href = "#2-integration-is-loose-but-coordinated" > 2. < strong > Integration is Loose but Coordinated< / strong > < / a > < / h3 >
< ul >
< li > No code dependencies between repos< / li >
< li > Runtime integration via CLI + REST API< / li >
< li > Configuration-driven coordination< / li >
< li > Works in both monorepo and multi-repo< / li >
< / ul >
< h3 id = "3-best-of-both-worlds" > < a class = "header" href = "#3-best-of-both-worlds" > 3. < strong > Best of Both Worlds< / strong > < / a > < / h3 >
< ul >
< li > Rust: High-performance coordination< / li >
< li > Nushell: Flexible business logic< / li >
< li > Clean separation of concerns< / li >
< li > Each technology does what it’ s best at< / li >
< / ul >
< h3 id = "4-multi-repo-doesnt-change-integration" > < a class = "header" href = "#4-multi-repo-doesnt-change-integration" > 4. < strong > Multi-Repo Doesn’ t Change Integration< / strong > < / a > < / h3 >
< ul >
< li > Same runtime model as monorepo< / li >
< li > Package installation sets up paths< / li >
< li > Configuration enables discovery< / li >
< li > Versioning ensures compatibility< / li >
< / ul >
< hr / >
< h2 id = "conclusion" > < a class = "header" href = "#conclusion" > Conclusion< / a > < / h2 >
< p > The confusing example in the multi-repo doc was < strong > oversimplified< / strong > . The real architecture is:< / p >
< pre > < code class = "language-plaintext" > ✅ Orchestrator IS USED and IS ESSENTIAL
2025-12-11 21:50:42 +00:00
✅ Platform (Rust) coordinates Core (Nushell) execution
✅ Loose coupling via CLI + REST API (not code dependencies)
✅ Works identically in monorepo and multi-repo
✅ Configuration-based integration (no hardcoded paths)
< / code > < / pre >
2026-01-12 04:42:18 +00:00
< p > The orchestrator provides:< / p >
< ul >
< li > Performance layer (async, parallel execution)< / li >
< li > Workflow engine (complex dependencies)< / li >
< li > State management (checkpoints, recovery)< / li >
< li > Task queue (reliable execution)< / li >
< / ul >
< p > While Nushell provides:< / p >
< ul >
< li > Business logic (providers, taskservs, clusters)< / li >
< li > Template rendering (Jinja2 via nu_plugin_tera)< / li >
< li > Configuration management (KCL integration)< / li >
< li > User-facing scripting< / li >
< / ul >
< p > < strong > Multi-repo just splits WHERE the code lives, not HOW it works together.< / strong > < / p >
2025-12-11 21:50:42 +00:00
< / main >
< nav class = "nav-wrapper" aria-label = "Page navigation" >
<!-- Mobile navigation buttons -->
2026-01-08 09:55:37 +00:00
< a rel = "prev" href = "../architecture/integration-patterns.html" class = "mobile-nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2025-12-11 21:50:42 +00:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2026-01-08 09:55:37 +00:00
< a rel = "next prefetch" href = "../architecture/multi-repo-architecture.html" class = "mobile-nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
2025-12-11 21:50:42 +00:00
< i class = "fa fa-angle-right" > < / i >
< / a >
< div style = "clear: both" > < / div >
< / nav >
< / div >
< / div >
< nav class = "nav-wide-wrapper" aria-label = "Page navigation" >
2026-01-08 09:55:37 +00:00
< a rel = "prev" href = "../architecture/integration-patterns.html" class = "nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
2025-12-11 21:50:42 +00:00
< i class = "fa fa-angle-left" > < / i >
< / a >
2026-01-08 09:55:37 +00:00
< a rel = "next prefetch" href = "../architecture/multi-repo-architecture.html" class = "nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
2025-12-11 21:50:42 +00:00
< i class = "fa fa-angle-right" > < / i >
< / a >
< / nav >
< / div >
< script >
window.playground_copyable = true;
< / script >
< script src = "../elasticlunr.min.js" > < / script >
< script src = "../mark.min.js" > < / script >
< script src = "../searcher.js" > < / script >
< script src = "../clipboard.min.js" > < / script >
< script src = "../highlight.js" > < / script >
< script src = "../book.js" > < / script >
<!-- Custom JS scripts -->
< / div >
< / body >
< / html >