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 > Integration - Provisioning Platform Documentation< / title >
<!-- Custom HTML head -->
< meta name = "description" content = "Complete documentation for the Provisioning Platform - Infrastructure automation with Nushell, KCL, and Rust" >
< 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/development/integration.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 = "integration-guide" > < a class = "header" href = "#integration-guide" > Integration Guide< / a > < / h1 >
< p > This document explains how the new project structure integrates with existing systems, API compatibility and versioning, database migration strategies, deployment considerations, and monitoring and observability.< / p >
< h2 id = "table-of-contents" > < a class = "header" href = "#table-of-contents" > Table of Contents< / a > < / h2 >
< ol >
< li > < a href = "#overview" > Overview< / a > < / li >
< li > < a href = "#existing-system-integration" > Existing System Integration< / a > < / li >
< li > < a href = "#api-compatibility-and-versioning" > API Compatibility and Versioning< / a > < / li >
< li > < a href = "#database-migration-strategies" > Database Migration Strategies< / a > < / li >
< li > < a href = "#deployment-considerations" > Deployment Considerations< / a > < / li >
< li > < a href = "#monitoring-and-observability" > Monitoring and Observability< / a > < / li >
< li > < a href = "#legacy-system-bridge" > Legacy System Bridge< / a > < / li >
< li > < a href = "#migration-pathways" > Migration Pathways< / a > < / li >
< li > < a href = "#troubleshooting-integration-issues" > Troubleshooting Integration Issues< / a > < / li >
< / ol >
< h2 id = "overview" > < a class = "header" href = "#overview" > Overview< / a > < / h2 >
< p > Provisioning has been designed with integration as a core principle, ensuring seamless compatibility between new development-focused components and existing production systems while providing clear migration pathways.< / p >
< p > < strong > Integration Principles< / strong > :< / p >
< ul >
< li > < strong > Backward Compatibility< / strong > : All existing APIs and interfaces remain functional< / li >
< li > < strong > Gradual Migration< / strong > : Systems can be migrated incrementally without disruption< / li >
< li > < strong > Dual Operation< / strong > : New and legacy systems operate side-by-side during transition< / li >
< li > < strong > Zero Downtime< / strong > : Migrations occur without service interruption< / li >
< li > < strong > Data Integrity< / strong > : All data migrations are atomic and reversible< / li >
< / ul >
< p > < strong > Integration Architecture< / strong > :< / p >
2026-01-08 09:55:37 +00:00
< pre > < code class = "language-plaintext" > Integration Ecosystem
2025-12-11 21:50:42 +00:00
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Legacy Core │ ←→ │ Bridge Layer │ ←→ │ New Systems │
│ │ │ │ │ │
│ - ENV config │ │ - Compatibility │ │ - TOML config │
│ - Direct calls │ │ - Translation │ │ - Orchestrator │
│ - File-based │ │ - Monitoring │ │ - Workflows │
│ - Simple logging│ │ - Validation │ │ - REST APIs │
└─────────────────┘ └─────────────────┘ └─────────────────┘
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "existing-system-integration" > < a class = "header" href = "#existing-system-integration" > Existing System Integration< / a > < / h2 >
< h3 id = "command-line-interface-integration" > < a class = "header" href = "#command-line-interface-integration" > Command-Line Interface Integration< / a > < / h3 >
< p > < strong > Seamless CLI Compatibility< / strong > :< / p >
< pre > < code class = "language-bash" > # All existing commands continue to work unchanged
./core/nulib/provisioning server create web-01 2xCPU-4 GB
2025-12-11 21:50:42 +00:00
./core/nulib/provisioning taskserv install kubernetes
./core/nulib/provisioning cluster create buildkit
# New commands available alongside existing ones
2026-01-12 04:42:18 +00:00
./src/core/nulib/provisioning server create web-01 2xCPU-4 GB --orchestrated
2025-12-11 21:50:42 +00:00
nu workspace/tools/workspace.nu health --detailed
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Path Resolution Integration< / strong > :< / p >
< pre > < code class = "language-nushell" > # Automatic path resolution between systems
2025-12-11 21:50:42 +00:00
use workspace/lib/path-resolver.nu
# Resolves to workspace path if available, falls back to core
let config_path = (path-resolver resolve_path "config" "user" --fallback-to-core)
# Seamless extension discovery
let provider_path = (path-resolver resolve_extension "providers" "upcloud")
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "configuration-system-bridge" > < a class = "header" href = "#configuration-system-bridge" > Configuration System Bridge< / a > < / h3 >
< p > < strong > Dual Configuration Support< / strong > :< / p >
< pre > < code class = "language-nushell" > # Configuration bridge supports both ENV and TOML
2025-12-11 21:50:42 +00:00
def get-config-value-bridge [key: string, default: string = ""] -> string {
# Try new TOML configuration first
let toml_value = try {
get-config-value $key
} catch { null }
if $toml_value != null {
return $toml_value
}
# Fall back to ENV variable (legacy support)
let env_key = ($key | str replace "." "_" | str upcase | $"PROVISIONING_($in)")
let env_value = ($env | get $env_key | default null)
if $env_value != null {
return $env_value
}
# Use default if provided
if $default != "" {
return $default
}
# Error with helpful migration message
error make {
msg: $"Configuration not found: ($key)",
help: $"Migrate from ($env_key) environment variable to ($key) in config file"
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "data-integration" > < a class = "header" href = "#data-integration" > Data Integration< / a > < / h3 >
< p > < strong > Shared Data Access< / strong > :< / p >
< pre > < code class = "language-nushell" > # Unified data access across old and new systems
2025-12-11 21:50:42 +00:00
def get-server-info [server_name: string] -> record {
# Try new orchestrator data store first
let orchestrator_data = try {
get-orchestrator-server-data $server_name
} catch { null }
if $orchestrator_data != null {
return $orchestrator_data
}
# Fall back to legacy file-based storage
let legacy_data = try {
get-legacy-server-data $server_name
} catch { null }
if $legacy_data != null {
return ($legacy_data | migrate-to-new-format)
}
error make {msg: $"Server not found: ($server_name)"}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "process-integration" > < a class = "header" href = "#process-integration" > Process Integration< / a > < / h3 >
< p > < strong > Hybrid Process Management< / strong > :< / p >
< pre > < code class = "language-nushell" > # Orchestrator-aware process management
2025-12-11 21:50:42 +00:00
def create-server-integrated [
name: string,
plan: string,
--orchestrated: bool = false
] -> record {
if $orchestrated and (check-orchestrator-available) {
# Use new orchestrator workflow
return (create-server-workflow $name $plan)
} else {
# Use legacy direct creation
return (create-server-direct $name $plan)
}
}
def check-orchestrator-available [] -> bool {
try {
http get "http://localhost:9090/health" | get status == "ok"
} catch {
false
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "api-compatibility-and-versioning" > < a class = "header" href = "#api-compatibility-and-versioning" > API Compatibility and Versioning< / a > < / h2 >
< h3 id = "rest-api-versioning" > < a class = "header" href = "#rest-api-versioning" > REST API Versioning< / a > < / h3 >
< p > < strong > API Version Strategy< / strong > :< / p >
< ul >
< li > < strong > v1< / strong > : Legacy compatibility API (existing functionality)< / li >
< li > < strong > v2< / strong > : Enhanced API with orchestrator features< / li >
< li > < strong > v3< / strong > : Full workflow and batch operation support< / li >
< / ul >
< p > < strong > Version Header Support< / strong > :< / p >
< pre > < code class = "language-bash" > # API calls with version specification
2025-12-11 21:50:42 +00:00
curl -H "API-Version: v1" http://localhost:9090/servers
curl -H "API-Version: v2" http://localhost:9090/workflows/servers/create
curl -H "API-Version: v3" http://localhost:9090/workflows/batch/submit
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "api-compatibility-layer" > < a class = "header" href = "#api-compatibility-layer" > API Compatibility Layer< / a > < / h3 >
< p > < strong > Backward Compatible Endpoints< / strong > :< / p >
< pre > < code class = "language-rust" > // Rust API compatibility layer
2025-12-11 21:50:42 +00:00
#[derive(Debug, Serialize, Deserialize)]
struct ApiRequest {
version: Option< String> ,
#[serde(flatten)]
payload: serde_json::Value,
}
async fn handle_versioned_request(
headers: HeaderMap,
req: ApiRequest,
) -> Result< ApiResponse, ApiError> {
let api_version = headers
.get("API-Version")
.and_then(|v| v.to_str().ok())
.unwrap_or("v1");
match api_version {
"v1" => handle_v1_request(req.payload).await,
"v2" => handle_v2_request(req.payload).await,
"v3" => handle_v3_request(req.payload).await,
_ => Err(ApiError::UnsupportedVersion(api_version.to_string())),
}
}
// V1 compatibility endpoint
async fn handle_v1_request(payload: serde_json::Value) -> Result< ApiResponse, ApiError> {
// Transform request to legacy format
let legacy_request = transform_to_legacy_format(payload)?;
// Execute using legacy system
let result = execute_legacy_operation(legacy_request).await?;
// Transform response to v1 format
Ok(transform_to_v1_response(result))
2026-01-12 04:42:18 +00:00
}< / code > < / pre >
< h3 id = "schema-evolution" > < a class = "header" href = "#schema-evolution" > Schema Evolution< / a > < / h3 >
< p > < strong > Backward Compatible Schema Changes< / strong > :< / p >
< pre > < code class = "language-nickel" > # API schema with version support
let ServerCreateRequest = {
2025-12-11 21:50:42 +00:00
# V1 fields (always supported)
2026-01-12 04:42:18 +00:00
name | string,
plan | string,
zone | string | default = "auto",
2025-12-11 21:50:42 +00:00
# V2 additions (optional for backward compatibility)
2026-01-12 04:42:18 +00:00
orchestrated | bool | default = false,
workflow_options | { } | optional,
2025-12-11 21:50:42 +00:00
# V3 additions
2026-01-12 04:42:18 +00:00
batch_options | { } | optional,
dependencies | array | default = [],
2025-12-11 21:50:42 +00:00
# Version constraints
2026-01-12 04:42:18 +00:00
api_version | string | default = "v1",
} in
ServerCreateRequest
2025-12-11 21:50:42 +00:00
# Conditional validation based on API version
2026-01-12 04:42:18 +00:00
let WorkflowOptions = {
wait_for_completion | bool | default = true,
timeout_seconds | number | default = 300,
retry_count | number | default = 3,
} in
WorkflowOptions
< / code > < / pre >
< h3 id = "client-sdk-compatibility" > < a class = "header" href = "#client-sdk-compatibility" > Client SDK Compatibility< / a > < / h3 >
< p > < strong > Multi-Version Client Support< / strong > :< / p >
< pre > < code class = "language-nushell" > # Nushell client with version support
2025-12-11 21:50:42 +00:00
def "client create-server" [
name: string,
plan: string,
--api-version: string = "v1",
--orchestrated: bool = false
] -> record {
let endpoint = match $api_version {
"v1" => "/servers",
"v2" => "/workflows/servers/create",
"v3" => "/workflows/batch/submit",
_ => (error make {msg: $"Unsupported API version: ($api_version)"})
}
let request_body = match $api_version {
"v1" => {name: $name, plan: $plan},
"v2" => {name: $name, plan: $plan, orchestrated: $orchestrated},
"v3" => {
operations: [{
id: "create_server",
type: "server_create",
config: {name: $name, plan: $plan}
}]
},
_ => (error make {msg: $"Unsupported API version: ($api_version)"})
}
http post $"http://localhost:9090($endpoint)" $request_body
--headers {
"Content-Type": "application/json",
"API-Version": $api_version
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "database-migration-strategies" > < a class = "header" href = "#database-migration-strategies" > Database Migration Strategies< / a > < / h2 >
< h3 id = "database-architecture-evolution" > < a class = "header" href = "#database-architecture-evolution" > Database Architecture Evolution< / a > < / h3 >
< p > < strong > Migration Strategy< / strong > :< / p >
< pre > < code class = "language-plaintext" > Database Evolution Path
2025-12-11 21:50:42 +00:00
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ File-based │ → │ SQLite │ → │ SurrealDB │
│ Storage │ │ Migration │ │ Full Schema │
│ │ │ │ │ │
│ - JSON files │ │ - Structured │ │ - Graph DB │
│ - Text logs │ │ - Transactions │ │ - Real-time │
│ - Simple state │ │ - Backup/restore│ │ - Clustering │
└─────────────────┘ └─────────────────┘ └─────────────────┘
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "migration-scripts" > < a class = "header" href = "#migration-scripts" > Migration Scripts< / a > < / h3 >
< p > < strong > Automated Database Migration< / strong > :< / p >
< pre > < code class = "language-nushell" > # Database migration orchestration
2025-12-11 21:50:42 +00:00
def migrate-database [
--from: string = "filesystem",
--to: string = "surrealdb",
--backup-first: bool = true,
--verify: bool = true
] -> record {
if $backup_first {
print "Creating backup before migration..."
let backup_result = (create-database-backup $from)
print $"Backup created: ($backup_result.path)"
}
print $"Migrating from ($from) to ($to)..."
match [$from, $to] {
["filesystem", "sqlite"] => migrate_filesystem_to_sqlite,
["filesystem", "surrealdb"] => migrate_filesystem_to_surrealdb,
["sqlite", "surrealdb"] => migrate_sqlite_to_surrealdb,
_ => (error make {msg: $"Unsupported migration path: ($from) → ($to)"})
}
if $verify {
print "Verifying migration integrity..."
let verification = (verify-migration $from $to)
if not $verification.success {
error make {
msg: $"Migration verification failed: ($verification.errors)",
help: "Restore from backup and retry migration"
}
}
}
print $"Migration from ($from) to ($to) completed successfully"
{from: $from, to: $to, status: "completed", migrated_at: (date now)}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > File System to SurrealDB Migration< / strong > :< / p >
< pre > < code class = "language-nushell" > def migrate_filesystem_to_surrealdb [] -> record {
2025-12-11 21:50:42 +00:00
# Initialize SurrealDB connection
let db = (connect-surrealdb)
# Migrate server data
let server_files = (ls data/servers/*.json)
let migrated_servers = []
for server_file in $server_files {
let server_data = (open $server_file.name | from json)
# Transform to new schema
let server_record = {
id: $server_data.id,
name: $server_data.name,
plan: $server_data.plan,
zone: ($server_data.zone? | default "unknown"),
status: $server_data.status,
ip_address: $server_data.ip_address?,
created_at: $server_data.created_at,
updated_at: (date now),
metadata: ($server_data.metadata? | default {}),
tags: ($server_data.tags? | default [])
}
# Insert into SurrealDB
let insert_result = try {
query-surrealdb $"CREATE servers:($server_record.id) CONTENT ($server_record | to json)"
} catch { |e|
print $"Warning: Failed to migrate server ($server_data.name): ($e.msg)"
}
$migrated_servers = ($migrated_servers | append $server_record.id)
}
# Migrate workflow data
migrate_workflows_to_surrealdb $db
# Migrate state data
migrate_state_to_surrealdb $db
{
migrated_servers: ($migrated_servers | length),
migrated_workflows: (migrate_workflows_to_surrealdb $db).count,
status: "completed"
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "data-integrity-verification" > < a class = "header" href = "#data-integrity-verification" > Data Integrity Verification< / a > < / h3 >
< p > < strong > Migration Verification< / strong > :< / p >
< pre > < code class = "language-nushell" > def verify-migration [from: string, to: string] -> record {
2025-12-11 21:50:42 +00:00
print "Verifying data integrity..."
let source_data = (read-source-data $from)
let target_data = (read-target-data $to)
let errors = []
# Verify record counts
if $source_data.servers.count != $target_data.servers.count {
$errors = ($errors | append "Server count mismatch")
}
# Verify key records
for server in $source_data.servers {
let target_server = ($target_data.servers | where id == $server.id | first)
if ($target_server | is-empty) {
$errors = ($errors | append $"Missing server: ($server.id)")
} else {
# Verify critical fields
if $target_server.name != $server.name {
$errors = ($errors | append $"Name mismatch for server ($server.id)")
}
if $target_server.status != $server.status {
$errors = ($errors | append $"Status mismatch for server ($server.id)")
}
}
}
{
success: ($errors | length) == 0,
errors: $errors,
verified_at: (date now)
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "deployment-considerations" > < a class = "header" href = "#deployment-considerations" > Deployment Considerations< / a > < / h2 >
< h3 id = "deployment-architecture" > < a class = "header" href = "#deployment-architecture" > Deployment Architecture< / a > < / h3 >
< p > < strong > Hybrid Deployment Model< / strong > :< / p >
< pre > < code class = "language-plaintext" > Deployment Architecture
2025-12-11 21:50:42 +00:00
┌─────────────────────────────────────────────────────────────────┐
│ Load Balancer / Reverse Proxy │
└─────────────────────┬───────────────────────────────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌───▼────┐ ┌─────▼─────┐ ┌───▼────┐
│Legacy │ │Orchestrator│ │New │
│System │ ←→ │Bridge │ ←→ │Systems │
│ │ │ │ │ │
│- CLI │ │- API Gate │ │- REST │
│- Files │ │- Compat │ │- DB │
│- Logs │ │- Monitor │ │- Queue │
└────────┘ └────────────┘ └────────┘
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "deployment-strategies" > < a class = "header" href = "#deployment-strategies" > Deployment Strategies< / a > < / h3 >
< p > < strong > Blue-Green Deployment< / strong > :< / p >
< pre > < code class = "language-bash" > # Blue-Green deployment with integration bridge
2025-12-11 21:50:42 +00:00
# Phase 1: Deploy new system alongside existing (Green environment)
cd src/tools
make all
make create-installers
# Install new system without disrupting existing
./packages/installers/install-provisioning-2.0.0.sh \
--install-path /opt/provisioning-v2 \
--no-replace-existing \
--enable-bridge-mode
# Phase 2: Start orchestrator and validate integration
/opt/provisioning-v2/bin/orchestrator start --bridge-mode --legacy-path /opt/provisioning-v1
# Phase 3: Gradual traffic shift
# Route 10% traffic to new system
nginx-traffic-split --new-backend 10%
# Validate metrics and gradually increase
nginx-traffic-split --new-backend 50%
nginx-traffic-split --new-backend 90%
# Phase 4: Complete cutover
nginx-traffic-split --new-backend 100%
/opt/provisioning-v1/bin/orchestrator stop
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Rolling Update< / strong > :< / p >
< pre > < code class = "language-nushell" > def rolling-deployment [
2025-12-11 21:50:42 +00:00
--target-version: string,
--batch-size: int = 3,
--health-check-interval: duration = 30sec
] -> record {
let nodes = (get-deployment-nodes)
let batches = ($nodes | group_by --chunk-size $batch_size)
let deployment_results = []
for batch in $batches {
print $"Deploying to batch: ($batch | get name | str join ', ')"
# Deploy to batch
for node in $batch {
deploy-to-node $node $target_version
}
# Wait for health checks
sleep $health_check_interval
# Verify batch health
let batch_health = ($batch | each { |node| check-node-health $node })
let healthy_nodes = ($batch_health | where healthy == true | length)
if $healthy_nodes != ($batch | length) {
# Rollback batch on failure
print $"Health check failed, rolling back batch"
for node in $batch {
rollback-node $node
}
error make {msg: "Rolling deployment failed at batch"}
}
print $"Batch deployed successfully"
$deployment_results = ($deployment_results | append {
batch: $batch,
status: "success",
deployed_at: (date now)
})
}
{
strategy: "rolling",
target_version: $target_version,
batches: ($deployment_results | length),
status: "completed",
completed_at: (date now)
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "configuration-deployment" > < a class = "header" href = "#configuration-deployment" > Configuration Deployment< / a > < / h3 >
< p > < strong > Environment-Specific Deployment< / strong > :< / p >
< pre > < code class = "language-bash" > # Development deployment
2025-12-11 21:50:42 +00:00
PROVISIONING_ENV=dev ./deploy.sh \
--config-source config.dev.toml \
--enable-debug \
--enable-hot-reload
# Staging deployment
PROVISIONING_ENV=staging ./deploy.sh \
--config-source config.staging.toml \
--enable-monitoring \
--backup-before-deploy
# Production deployment
PROVISIONING_ENV=prod ./deploy.sh \
--config-source config.prod.toml \
--zero-downtime \
--enable-all-monitoring \
--backup-before-deploy \
--health-check-timeout 5m
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "container-integration" > < a class = "header" href = "#container-integration" > Container Integration< / a > < / h3 >
< p > < strong > Docker Deployment with Bridge< / strong > :< / p >
< pre > < code class = "language-dockerfile" > # Multi-stage Docker build supporting both systems
2025-12-11 21:50:42 +00:00
FROM rust:1.70 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM ubuntu:22.04 as runtime
WORKDIR /app
# Install both legacy and new systems
COPY --from=builder /app/target/release/orchestrator /app/bin/
COPY legacy-provisioning/ /app/legacy/
COPY config/ /app/config/
# Bridge script for dual operation
COPY bridge-start.sh /app/bin/
ENV PROVISIONING_BRIDGE_MODE=true
ENV PROVISIONING_LEGACY_PATH=/app/legacy
ENV PROVISIONING_NEW_PATH=/app/bin
EXPOSE 8080
CMD ["/app/bin/bridge-start.sh"]
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Kubernetes Integration< / strong > :< / p >
< pre > < code class = "language-yaml" > # Kubernetes deployment with bridge sidecar
2025-12-11 21:50:42 +00:00
apiVersion: apps/v1
kind: Deployment
metadata:
name: provisioning-system
spec:
replicas: 3
template:
spec:
containers:
- name: orchestrator
image: provisioning-system:2.0.0
ports:
- containerPort: 8080
env:
- name: PROVISIONING_BRIDGE_MODE
value: "true"
volumeMounts:
- name: config
mountPath: /app/config
- name: legacy-data
mountPath: /app/legacy/data
- name: legacy-bridge
image: provisioning-legacy:1.0.0
env:
- name: BRIDGE_ORCHESTRATOR_URL
value: "http://localhost:9090"
volumeMounts:
- name: legacy-data
mountPath: /data
volumes:
- name: config
configMap:
name: provisioning-config
- name: legacy-data
persistentVolumeClaim:
claimName: provisioning-data
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "monitoring-and-observability" > < a class = "header" href = "#monitoring-and-observability" > Monitoring and Observability< / a > < / h2 >
< h3 id = "integrated-monitoring-architecture" > < a class = "header" href = "#integrated-monitoring-architecture" > Integrated Monitoring Architecture< / a > < / h3 >
< p > < strong > Monitoring Stack Integration< / strong > :< / p >
< pre > < code class = "language-plaintext" > Observability Architecture
2025-12-11 21:50:42 +00:00
┌─────────────────────────────────────────────────────────────────┐
│ Monitoring Dashboard │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Grafana │ │ Jaeger │ │ AlertMgr │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────┬───────────────┬───────────────┬─────────────────┘
│ │ │
┌──────────▼──────────┐ │ ┌───────────▼───────────┐
│ Prometheus │ │ │ Jaeger │
│ (Metrics) │ │ │ (Tracing) │
└──────────┬──────────┘ │ └───────────┬───────────┘
│ │ │
┌─────────────▼─────────────┐ │ ┌─────────────▼─────────────┐
│ Legacy │ │ │ New System │
│ Monitoring │ │ │ Monitoring │
│ │ │ │ │
│ - File-based logs │ │ │ - Structured logs │
│ - Simple metrics │ │ │ - Prometheus metrics │
│ - Basic health checks │ │ │ - Distributed tracing │
└───────────────────────────┘ │ └───────────────────────────┘
│
┌─────────▼─────────┐
│ Bridge Monitor │
│ │
│ - Integration │
│ - Compatibility │
│ - Migration │
└───────────────────┘
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "metrics-integration" > < a class = "header" href = "#metrics-integration" > Metrics Integration< / a > < / h3 >
< p > < strong > Unified Metrics Collection< / strong > :< / p >
< pre > < code class = "language-nushell" > # Metrics bridge for legacy and new systems
2025-12-11 21:50:42 +00:00
def collect-system-metrics [] -> record {
let legacy_metrics = collect-legacy-metrics
let new_metrics = collect-new-metrics
let bridge_metrics = collect-bridge-metrics
{
timestamp: (date now),
legacy: $legacy_metrics,
new: $new_metrics,
bridge: $bridge_metrics,
integration: {
compatibility_rate: (calculate-compatibility-rate $bridge_metrics),
migration_progress: (calculate-migration-progress),
system_health: (assess-overall-health $legacy_metrics $new_metrics)
}
}
}
def collect-legacy-metrics [] -> record {
let log_files = (ls logs/*.log)
let process_stats = (get-process-stats "legacy-provisioning")
{
active_processes: $process_stats.count,
log_file_sizes: ($log_files | get size | math sum),
last_activity: (get-last-log-timestamp),
error_count: (count-log-errors "last 1h"),
performance: {
avg_response_time: (calculate-avg-response-time),
throughput: (calculate-throughput)
}
}
}
def collect-new-metrics [] -> record {
let orchestrator_stats = try {
http get "http://localhost:9090/metrics"
} catch {
{status: "unavailable"}
}
{
orchestrator: $orchestrator_stats,
workflow_stats: (get-workflow-metrics),
api_stats: (get-api-metrics),
database_stats: (get-database-metrics)
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "logging-integration" > < a class = "header" href = "#logging-integration" > Logging Integration< / a > < / h3 >
< p > < strong > Unified Logging Strategy< / strong > :< / p >
< pre > < code class = "language-nushell" > # Structured logging bridge
2025-12-11 21:50:42 +00:00
def log-integrated [
level: string,
message: string,
--component: string = "bridge",
--legacy-compat: bool = true
] {
let log_entry = {
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S%.3f"),
level: $level,
component: $component,
message: $message,
system: "integrated",
correlation_id: (generate-correlation-id)
}
# Write to structured log (new system)
$log_entry | to json | save --append logs/integrated.jsonl
if $legacy_compat {
# Write to legacy log format
let legacy_entry = $"[($log_entry.timestamp)] [($level)] ($component): ($message)"
$legacy_entry | save --append logs/legacy.log
}
# Send to monitoring system
send-to-monitoring $log_entry
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "health-check-integration" > < a class = "header" href = "#health-check-integration" > Health Check Integration< / a > < / h3 >
< p > < strong > Comprehensive Health Monitoring< / strong > :< / p >
< pre > < code class = "language-nushell" > def health-check-integrated [] -> record {
2025-12-11 21:50:42 +00:00
let health_checks = [
{name: "legacy-system", check: (check-legacy-health)},
{name: "orchestrator", check: (check-orchestrator-health)},
{name: "database", check: (check-database-health)},
{name: "bridge-compatibility", check: (check-bridge-health)},
{name: "configuration", check: (check-config-health)}
]
let results = ($health_checks | each { |check|
let result = try {
do $check.check
} catch { |e|
{status: "unhealthy", error: $e.msg}
}
{name: $check.name, result: $result}
})
let healthy_count = ($results | where result.status == "healthy" | length)
let total_count = ($results | length)
{
overall_status: (if $healthy_count == $total_count { "healthy" } else { "degraded" }),
healthy_services: $healthy_count,
total_services: $total_count,
services: $results,
checked_at: (date now)
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "legacy-system-bridge" > < a class = "header" href = "#legacy-system-bridge" > Legacy System Bridge< / a > < / h2 >
< h3 id = "bridge-architecture" > < a class = "header" href = "#bridge-architecture" > Bridge Architecture< / a > < / h3 >
< p > < strong > Bridge Component Design< / strong > :< / p >
< pre > < code class = "language-nushell" > # Legacy system bridge module
2025-12-11 21:50:42 +00:00
export module bridge {
# Bridge state management
export def init-bridge [] -> record {
let bridge_config = get-config-section "bridge"
{
legacy_path: ($bridge_config.legacy_path? | default "/opt/provisioning-v1"),
new_path: ($bridge_config.new_path? | default "/opt/provisioning-v2"),
mode: ($bridge_config.mode? | default "compatibility"),
monitoring_enabled: ($bridge_config.monitoring? | default true),
initialized_at: (date now)
}
}
# Command translation layer
export def translate-command [
legacy_command: list< string>
] -> list< string> {
match $legacy_command {
["provisioning", "server", "create", $name, $plan, ...$args] => {
let new_args = ($args | each { |arg|
match $arg {
"--dry-run" => "--dry-run",
"--wait" => "--wait",
$zone if ($zone | str starts-with "--zone=") => $zone,
_ => $arg
}
})
["provisioning", "server", "create", $name, $plan] ++ $new_args ++ ["--orchestrated"]
},
_ => $legacy_command # Pass through unchanged
}
}
# Data format translation
export def translate-response [
legacy_response: record,
target_format: string = "v2"
] -> record {
match $target_format {
"v2" => {
id: ($legacy_response.id? | default (generate-uuid)),
name: $legacy_response.name,
status: $legacy_response.status,
created_at: ($legacy_response.created_at? | default (date now)),
metadata: ($legacy_response | reject name status created_at),
version: "v2-compat"
},
_ => $legacy_response
}
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "bridge-operation-modes" > < a class = "header" href = "#bridge-operation-modes" > Bridge Operation Modes< / a > < / h3 >
< p > < strong > Compatibility Mode< / strong > :< / p >
< pre > < code class = "language-nushell" > # Full compatibility with legacy system
2025-12-11 21:50:42 +00:00
def run-compatibility-mode [] {
print "Starting bridge in compatibility mode..."
# Intercept legacy commands
let legacy_commands = monitor-legacy-commands
for command in $legacy_commands {
let translated = (bridge translate-command $command)
try {
let result = (execute-new-system $translated)
let legacy_result = (bridge translate-response $result "v1")
respond-to-legacy $legacy_result
} catch { |e|
# Fall back to legacy system on error
let fallback_result = (execute-legacy-system $command)
respond-to-legacy $fallback_result
}
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Migration Mode< / strong > :< / p >
< pre > < code class = "language-nushell" > # Gradual migration with traffic splitting
2025-12-11 21:50:42 +00:00
def run-migration-mode [
--new-system-percentage: int = 50
] {
print $"Starting bridge in migration mode (($new_system_percentage)% new system)"
let commands = monitor-all-commands
for command in $commands {
let route_to_new = ((random integer 1..100) < = $new_system_percentage)
if $route_to_new {
try {
execute-new-system $command
} catch {
# Fall back to legacy on failure
execute-legacy-system $command
}
} else {
execute-legacy-system $command
}
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "migration-pathways" > < a class = "header" href = "#migration-pathways" > Migration Pathways< / a > < / h2 >
< h3 id = "migration-phases" > < a class = "header" href = "#migration-phases" > Migration Phases< / a > < / h3 >
< p > < strong > Phase 1: Parallel Deployment< / strong > < / p >
< ul >
< li > Deploy new system alongside existing< / li >
< li > Enable bridge for compatibility< / li >
< li > Begin data synchronization< / li >
< li > Monitor integration health< / li >
< / ul >
< p > < strong > Phase 2: Gradual Migration< / strong > < / p >
< ul >
< li > Route increasing traffic to new system< / li >
< li > Migrate data in background< / li >
< li > Validate consistency< / li >
< li > Address integration issues< / li >
< / ul >
< p > < strong > Phase 3: Full Migration< / strong > < / p >
< ul >
< li > Complete traffic cutover< / li >
< li > Decommission legacy system< / li >
< li > Clean up bridge components< / li >
< li > Finalize data migration< / li >
< / ul >
< h3 id = "migration-automation" > < a class = "header" href = "#migration-automation" > Migration Automation< / a > < / h3 >
< p > < strong > Automated Migration Orchestration< / strong > :< / p >
< pre > < code class = "language-nushell" > def execute-migration-plan [
2025-12-11 21:50:42 +00:00
migration_plan: string,
--dry-run: bool = false,
--skip-backup: bool = false
] -> record {
let plan = (open $migration_plan | from yaml)
if not $skip_backup {
create-pre-migration-backup
}
let migration_results = []
for phase in $plan.phases {
print $"Executing migration phase: ($phase.name)"
if $dry_run {
print $"[DRY RUN] Would execute phase: ($phase)"
continue
}
let phase_result = try {
execute-migration-phase $phase
} catch { |e|
print $"Migration phase failed: ($e.msg)"
if $phase.rollback_on_failure? | default false {
print "Rolling back migration phase..."
rollback-migration-phase $phase
}
error make {msg: $"Migration failed at phase ($phase.name): ($e.msg)"}
}
$migration_results = ($migration_results | append $phase_result)
# Wait between phases if specified
if "wait_seconds" in $phase {
sleep ($phase.wait_seconds * 1sec)
}
}
{
migration_plan: $migration_plan,
phases_completed: ($migration_results | length),
status: "completed",
completed_at: (date now),
results: $migration_results
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< p > < strong > Migration Validation< / strong > :< / p >
< pre > < code class = "language-nushell" > def validate-migration-readiness [] -> record {
2025-12-11 21:50:42 +00:00
let checks = [
{name: "backup-available", check: (check-backup-exists)},
{name: "new-system-healthy", check: (check-new-system-health)},
{name: "database-accessible", check: (check-database-connectivity)},
{name: "configuration-valid", check: (validate-migration-config)},
{name: "resources-available", check: (check-system-resources)},
{name: "network-connectivity", check: (check-network-health)}
]
let results = ($checks | each { |check|
{
name: $check.name,
result: (do $check.check),
timestamp: (date now)
}
})
let failed_checks = ($results | where result.status != "ready")
{
ready_for_migration: ($failed_checks | length) == 0,
checks: $results,
failed_checks: $failed_checks,
validated_at: (date now)
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h2 id = "troubleshooting-integration-issues" > < a class = "header" href = "#troubleshooting-integration-issues" > Troubleshooting Integration Issues< / a > < / h2 >
< h3 id = "common-integration-problems" > < a class = "header" href = "#common-integration-problems" > Common Integration Problems< / a > < / h3 >
< h4 id = "api-compatibility-issues" > < a class = "header" href = "#api-compatibility-issues" > API Compatibility Issues< / a > < / h4 >
< p > < strong > Problem< / strong > : Version mismatch between client and server< / p >
< pre > < code class = "language-bash" > # Diagnosis
2025-12-11 21:50:42 +00:00
curl -H "API-Version: v1" http://localhost:9090/health
curl -H "API-Version: v2" http://localhost:9090/health
# Solution: Check supported versions
curl http://localhost:9090/api/versions
# Update client API version
export PROVISIONING_API_VERSION=v2
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h4 id = "configuration-bridge-issues" > < a class = "header" href = "#configuration-bridge-issues" > Configuration Bridge Issues< / a > < / h4 >
< p > < strong > Problem< / strong > : Configuration not found in either system< / p >
< pre > < code class = "language-nushell" > # Diagnosis
2025-12-11 21:50:42 +00:00
def diagnose-config-issue [key: string] -> record {
let toml_result = try {
get-config-value $key
} catch { |e| {status: "failed", error: $e.msg} }
let env_key = ($key | str replace "." "_" | str upcase | $"PROVISIONING_($in)")
let env_result = try {
$env | get $env_key
} catch { |e| {status: "failed", error: $e.msg} }
{
key: $key,
toml_config: $toml_result,
env_config: $env_result,
migration_needed: ($toml_result.status == "failed" and $env_result.status != "failed")
}
}
# Solution: Migrate configuration
def migrate-single-config [key: string] {
let diagnosis = (diagnose-config-issue $key)
if $diagnosis.migration_needed {
let env_value = $diagnosis.env_config
set-config-value $key $env_value
print $"Migrated ($key) from environment variable"
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h4 id = "database-integration-issues" > < a class = "header" href = "#database-integration-issues" > Database Integration Issues< / a > < / h4 >
< p > < strong > Problem< / strong > : Data inconsistency between systems< / p >
< pre > < code class = "language-nushell" > # Diagnosis and repair
2025-12-11 21:50:42 +00:00
def repair-data-consistency [] -> record {
let legacy_data = (read-legacy-data)
let new_data = (read-new-data)
let inconsistencies = []
# Check server records
for server in $legacy_data.servers {
let new_server = ($new_data.servers | where id == $server.id | first)
if ($new_server | is-empty) {
print $"Missing server in new system: ($server.id)"
create-server-record $server
$inconsistencies = ($inconsistencies | append {type: "missing", id: $server.id})
} else if $new_server != $server {
print $"Inconsistent server data: ($server.id)"
update-server-record $server
$inconsistencies = ($inconsistencies | append {type: "inconsistent", id: $server.id})
}
}
{
inconsistencies_found: ($inconsistencies | length),
repairs_applied: ($inconsistencies | length),
repaired_at: (date now)
}
}
2026-01-12 04:42:18 +00:00
< / code > < / pre >
< h3 id = "debug-tools" > < a class = "header" href = "#debug-tools" > Debug Tools< / a > < / h3 >
< p > < strong > Integration Debug Mode< / strong > :< / p >
< pre > < code class = "language-bash" > # Enable comprehensive debugging
2025-12-11 21:50:42 +00:00
export PROVISIONING_DEBUG=true
export PROVISIONING_LOG_LEVEL=debug
export PROVISIONING_BRIDGE_DEBUG=true
export PROVISIONING_INTEGRATION_TRACE=true
# Run with integration debugging
2026-01-12 04:42:18 +00:00
provisioning server create test-server 2xCPU-4 GB --debug-integration
< / code > < / pre >
< p > < strong > Health Check Debugging< / strong > :< / p >
< pre > < code class = "language-nushell" > def debug-integration-health [] -> record {
2025-12-11 21:50:42 +00:00
print "=== Integration Health Debug ==="
# Check all integration points
let legacy_health = try {
check-legacy-system
} catch { |e| {status: "error", error: $e.msg} }
let orchestrator_health = try {
http get "http://localhost:9090/health"
} catch { |e| {status: "error", error: $e.msg} }
let bridge_health = try {
check-bridge-status
} catch { |e| {status: "error", error: $e.msg} }
let config_health = try {
validate-config-integration
} catch { |e| {status: "error", error: $e.msg} }
print $"Legacy System: ($legacy_health.status)"
print $"Orchestrator: ($orchestrator_health.status)"
print $"Bridge: ($bridge_health.status)"
print $"Configuration: ($config_health.status)"
{
legacy: $legacy_health,
orchestrator: $orchestrator_health,
bridge: $bridge_health,
configuration: $config_health,
debug_timestamp: (date now)
}
}
< / code > < / pre >
2026-01-12 04:42:18 +00:00
< p > This integration guide provides a comprehensive framework for seamlessly integrating new development components with existing production systems while maintaining reliability, compatibility, and clear migration pathways.< / p >
2025-12-11 21:50:42 +00:00
< / main >
< nav class = "nav-wrapper" aria-label = "Page navigation" >
<!-- Mobile navigation buttons -->
< a rel = "prev" href = "../development/workflow.html" class = "mobile-nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
< i class = "fa fa-angle-left" > < / i >
< / a >
2026-01-08 09:55:37 +00:00
< a rel = "next prefetch" href = "../development/build-system.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" >
< a rel = "prev" href = "../development/workflow.html" class = "nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
< i class = "fa fa-angle-left" > < / i >
< / a >
2026-01-08 09:55:37 +00:00
< a rel = "next prefetch" href = "../development/build-system.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 >