provisioning/docs/book/configuration/workspace-config-architecture.html
Jesús Pérez 6a59d34bb1
chore: update provisioning configuration and documentation
Update configuration files, templates, and internal documentation
for the provisioning repository system.

Configuration Updates:
- KMS configuration modernization
- Plugin system settings
- Service port mappings
- Test cluster topologies
- Installation configuration examples
- VM configuration defaults
- Cedar authorization policies

Documentation Updates:
- Library module documentation
- Extension API guides
- AI system documentation
- Service management guides
- Test environment setup
- Plugin usage guides
- Validator configuration documentation

All changes are backward compatible.
2025-12-11 21:50:42 +00:00

552 lines
28 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="ayu sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Workspace Config Architecture - 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/configuration/workspace-config-architecture.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="workspace-configuration-architecture"><a class="header" href="#workspace-configuration-architecture">Workspace Configuration Architecture</a></h1>
<p><strong>Version</strong>: 2.0.0
<strong>Date</strong>: 2025-10-06
<strong>Status</strong>: Implemented</p>
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
<p>The provisioning system now uses a <strong>workspace-based configuration architecture</strong> where each workspace has its own complete configuration structure. This replaces the old ENV-based and template-only system.</p>
<h2 id="critical-design-principle"><a class="header" href="#critical-design-principle">Critical Design Principle</a></h2>
<p><strong><code>config.defaults.toml</code> is ONLY a template, NEVER loaded at runtime</strong></p>
<p>This file exists solely as a reference template for generating workspace configurations. The system does NOT load it during operation.</p>
<h2 id="configuration-hierarchy"><a class="header" href="#configuration-hierarchy">Configuration Hierarchy</a></h2>
<p>Configuration is loaded in the following order (lowest to highest priority):</p>
<ol>
<li><strong>Workspace Config</strong> (Base): <code>{workspace}/config/provisioning.yaml</code></li>
<li><strong>Provider Configs</strong>: <code>{workspace}/config/providers/*.toml</code></li>
<li><strong>Platform Configs</strong>: <code>{workspace}/config/platform/*.toml</code></li>
<li><strong>User Context</strong>: <code>~/Library/Application Support/provisioning/ws_{name}.yaml</code></li>
<li><strong>Environment Variables</strong>: <code>PROVISIONING_*</code> (highest priority)</li>
</ol>
<h2 id="workspace-structure"><a class="header" href="#workspace-structure">Workspace Structure</a></h2>
<p>When a workspace is initialized, the following structure is created:</p>
<pre><code>{workspace}/
├── config/
│ ├── provisioning.yaml # Main workspace config (generated from template)
│ ├── providers/ # Provider-specific configs
│ │ ├── aws.toml
│ │ ├── local.toml
│ │ └── upcloud.toml
│ ├── platform/ # Platform service configs
│ │ ├── orchestrator.toml
│ │ └── mcp.toml
│ └── kms.toml # KMS configuration
├── infra/ # Infrastructure definitions
├── .cache/ # Cache directory
├── .runtime/ # Runtime data
│ ├── taskservs/
│ └── clusters/
├── .providers/ # Provider state
├── .kms/ # Key management
│ └── keys/
├── generated/ # Generated files
└── .gitignore # Workspace gitignore
</code></pre>
<h2 id="template-system"><a class="header" href="#template-system">Template System</a></h2>
<p>Templates are located at: <code>/Users/Akasha/project-provisioning/provisioning/config/templates/</code></p>
<h3 id="available-templates"><a class="header" href="#available-templates">Available Templates</a></h3>
<ol>
<li><strong>workspace-provisioning.yaml.template</strong> - Main workspace configuration</li>
<li><strong>provider-aws.toml.template</strong> - AWS provider configuration</li>
<li><strong>provider-local.toml.template</strong> - Local provider configuration</li>
<li><strong>provider-upcloud.toml.template</strong> - UpCloud provider configuration</li>
<li><strong>kms.toml.template</strong> - KMS configuration</li>
<li><strong>user-context.yaml.template</strong> - User context configuration</li>
</ol>
<h3 id="template-variables"><a class="header" href="#template-variables">Template Variables</a></h3>
<p>Templates support the following interpolation variables:</p>
<ul>
<li><code>{{workspace.name}}</code> - Workspace name</li>
<li><code>{{workspace.path}}</code> - Absolute path to workspace</li>
<li><code>{{now.iso}}</code> - Current timestamp in ISO format</li>
<li><code>{{env.HOME}}</code> - Users home directory</li>
<li><code>{{env.*}}</code> - Environment variables (safe list only)</li>
<li><code>{{paths.base}}</code> - Base path (after config load)</li>
</ul>
<h2 id="workspace-initialization"><a class="header" href="#workspace-initialization">Workspace Initialization</a></h2>
<h3 id="command"><a class="header" href="#command">Command</a></h3>
<pre><code class="language-bash"># Using the workspace init function
nu -c "use provisioning/core/nulib/lib_provisioning/workspace/init.nu *; workspace-init 'my-workspace' '/path/to/workspace' --providers ['aws' 'local'] --activate"
</code></pre>
<h3 id="process"><a class="header" href="#process">Process</a></h3>
<ol>
<li><strong>Create Directory Structure</strong>: All necessary directories</li>
<li><strong>Generate Config from Template</strong>: Creates <code>config/provisioning.yaml</code></li>
<li><strong>Generate Provider Configs</strong>: For each specified provider</li>
<li><strong>Generate KMS Config</strong>: Security configuration</li>
<li><strong>Create User Context</strong> (if activate): User-specific overrides</li>
<li><strong>Create .gitignore</strong>: Ignore runtime/cache files</li>
</ol>
<h2 id="user-context"><a class="header" href="#user-context">User Context</a></h2>
<p>User context files are stored per workspace:</p>
<p><strong>Location</strong>: <code>~/Library/Application Support/provisioning/ws_{workspace_name}.yaml</code></p>
<h3 id="purpose"><a class="header" href="#purpose">Purpose</a></h3>
<ul>
<li>Store user-specific overrides (debug settings, output preferences)</li>
<li>Mark active workspace</li>
<li>Override workspace paths if needed</li>
</ul>
<h3 id="example"><a class="header" href="#example">Example</a></h3>
<pre><code class="language-yaml">workspace:
name: "my-workspace"
path: "/path/to/my-workspace"
active: true
debug:
enabled: true
log_level: "debug"
output:
format: "json"
providers:
default: "aws"
</code></pre>
<h2 id="configuration-loading-process"><a class="header" href="#configuration-loading-process">Configuration Loading Process</a></h2>
<h3 id="1-determine-active-workspace"><a class="header" href="#1-determine-active-workspace">1. Determine Active Workspace</a></h3>
<pre><code class="language-nushell"># Check user config directory for active workspace
let user_config_dir = ~/Library/Application Support/provisioning/
let active_workspace = (find workspace with active: true in ws_*.yaml files)
</code></pre>
<h3 id="2-load-workspace-config"><a class="header" href="#2-load-workspace-config">2. Load Workspace Config</a></h3>
<pre><code class="language-nushell"># Load main workspace config
let workspace_config = {workspace.path}/config/provisioning.yaml
</code></pre>
<h3 id="3-load-provider-configs"><a class="header" href="#3-load-provider-configs">3. Load Provider Configs</a></h3>
<pre><code class="language-nushell"># Merge all provider configs
for provider in {workspace.path}/config/providers/*.toml {
merge provider config
}
</code></pre>
<h3 id="4-load-platform-configs"><a class="header" href="#4-load-platform-configs">4. Load Platform Configs</a></h3>
<pre><code class="language-nushell"># Merge all platform configs
for platform in {workspace.path}/config/platform/*.toml {
merge platform config
}
</code></pre>
<h3 id="5-apply-user-context"><a class="header" href="#5-apply-user-context">5. Apply User Context</a></h3>
<pre><code class="language-nushell"># Apply user-specific overrides
let user_context = ~/Library/Application Support/provisioning/ws_{name}.yaml
merge user_context (highest config priority)
</code></pre>
<h3 id="6-apply-environment-variables"><a class="header" href="#6-apply-environment-variables">6. Apply Environment Variables</a></h3>
<pre><code class="language-nushell"># Final overrides from environment
PROVISIONING_DEBUG=true
PROVISIONING_LOG_LEVEL=debug
PROVISIONING_PROVIDER=aws
# etc.
</code></pre>
<h2 id="migration-from-old-system"><a class="header" href="#migration-from-old-system">Migration from Old System</a></h2>
<h3 id="before-env-based"><a class="header" href="#before-env-based">Before (ENV-based)</a></h3>
<pre><code class="language-bash">export PROVISIONING=/usr/local/provisioning
export PROVISIONING_INFRA_PATH=/path/to/infra
export PROVISIONING_DEBUG=true
# ... many ENV variables
</code></pre>
<h3 id="after-workspace-based"><a class="header" href="#after-workspace-based">After (Workspace-based)</a></h3>
<pre><code class="language-bash"># Initialize workspace
workspace-init "production" "/workspaces/prod" --providers ["aws"] --activate
# All config is now in workspace
# No ENV variables needed (except for overrides)
</code></pre>
<h3 id="breaking-changes"><a class="header" href="#breaking-changes">Breaking Changes</a></h3>
<ol>
<li><strong><code>config.defaults.toml</code> NOT loaded</strong> - Only used as template</li>
<li><strong>Workspace required</strong> - Must have active workspace or be in workspace directory</li>
<li><strong>New config locations</strong> - User config in <code>~/Library/Application Support/provisioning/</code></li>
<li><strong>YAML main config</strong> - <code>provisioning.yaml</code> instead of TOML</li>
</ol>
<h2 id="workspace-management-commands"><a class="header" href="#workspace-management-commands">Workspace Management Commands</a></h2>
<h3 id="initialize-workspace"><a class="header" href="#initialize-workspace">Initialize Workspace</a></h3>
<pre><code class="language-nushell">use provisioning/core/nulib/lib_provisioning/workspace/init.nu *
workspace-init "my-workspace" "/path/to/workspace" --providers ["aws" "local"] --activate
</code></pre>
<h3 id="list-workspaces"><a class="header" href="#list-workspaces">List Workspaces</a></h3>
<pre><code class="language-nushell">workspace-list
</code></pre>
<h3 id="activate-workspace"><a class="header" href="#activate-workspace">Activate Workspace</a></h3>
<pre><code class="language-nushell">workspace-activate "my-workspace"
</code></pre>
<h3 id="get-active-workspace"><a class="header" href="#get-active-workspace">Get Active Workspace</a></h3>
<pre><code class="language-nushell">workspace-get-active
</code></pre>
<h2 id="implementation-files"><a class="header" href="#implementation-files">Implementation Files</a></h2>
<h3 id="core-files"><a class="header" href="#core-files">Core Files</a></h3>
<ol>
<li><strong>Template Directory</strong>: <code>/Users/Akasha/project-provisioning/provisioning/config/templates/</code></li>
<li><strong>Workspace Init</strong>: <code>/Users/Akasha/project-provisioning/provisioning/core/nulib/lib_provisioning/workspace/init.nu</code></li>
<li><strong>Config Loader</strong>: <code>/Users/Akasha/project-provisioning/provisioning/core/nulib/lib_provisioning/config/loader.nu</code></li>
</ol>
<h3 id="key-changes-in-config-loader"><a class="header" href="#key-changes-in-config-loader">Key Changes in Config Loader</a></h3>
<h4 id="removed"><a class="header" href="#removed">Removed</a></h4>
<ul>
<li><code>get-defaults-config-path()</code> - No longer loads config.defaults.toml</li>
<li>Old hierarchy with user/project/infra TOML files</li>
</ul>
<h4 id="added"><a class="header" href="#added">Added</a></h4>
<ul>
<li><code>get-active-workspace()</code> - Finds active workspace from user config</li>
<li>Support for YAML config files</li>
<li>Provider and platform config merging</li>
<li>User context loading</li>
</ul>
<h2 id="configuration-schema"><a class="header" href="#configuration-schema">Configuration Schema</a></h2>
<h3 id="main-workspace-config-provisioningyaml"><a class="header" href="#main-workspace-config-provisioningyaml">Main Workspace Config (provisioning.yaml)</a></h3>
<pre><code class="language-yaml">workspace:
name: string
version: string
created: timestamp
paths:
base: string
infra: string
cache: string
runtime: string
# ... all paths
core:
version: string
name: string
debug:
enabled: bool
log_level: string
# ... debug settings
providers:
active: [string]
default: string
# ... all other sections
</code></pre>
<h3 id="provider-config-providerstoml"><a class="header" href="#provider-config-providerstoml">Provider Config (providers/*.toml)</a></h3>
<pre><code class="language-toml">[provider]
name = "aws"
enabled = true
workspace = "workspace-name"
[provider.auth]
profile = "default"
region = "us-east-1"
[provider.paths]
base = "{workspace}/.providers/aws"
cache = "{workspace}/.providers/aws/cache"
</code></pre>
<h3 id="user-context-ws_nameyaml"><a class="header" href="#user-context-ws_nameyaml">User Context (ws_{name}.yaml)</a></h3>
<pre><code class="language-yaml">workspace:
name: string
path: string
active: bool
debug:
enabled: bool
log_level: string
output:
format: string
</code></pre>
<h2 id="benefits"><a class="header" href="#benefits">Benefits</a></h2>
<ol>
<li><strong>No Template Loading</strong>: config.defaults.toml is template-only</li>
<li><strong>Workspace Isolation</strong>: Each workspace is self-contained</li>
<li><strong>Explicit Configuration</strong>: No hidden defaults from ENV</li>
<li><strong>Clear Hierarchy</strong>: Predictable override behavior</li>
<li><strong>Multi-Workspace Support</strong>: Easy switching between workspaces</li>
<li><strong>User Overrides</strong>: Per-workspace user preferences</li>
<li><strong>Version Control</strong>: Workspace configs can be committed (except secrets)</li>
</ol>
<h2 id="security-considerations"><a class="header" href="#security-considerations">Security Considerations</a></h2>
<h3 id="generated-gitignore"><a class="header" href="#generated-gitignore">Generated .gitignore</a></h3>
<p>The workspace .gitignore excludes:</p>
<ul>
<li><code>.cache/</code> - Cache files</li>
<li><code>.runtime/</code> - Runtime data</li>
<li><code>.providers/</code> - Provider state</li>
<li><code>.kms/keys/</code> - Secret keys</li>
<li><code>generated/</code> - Generated files</li>
<li><code>*.log</code> - Log files</li>
</ul>
<h3 id="secret-management"><a class="header" href="#secret-management">Secret Management</a></h3>
<ul>
<li>KMS keys stored in <code>.kms/keys/</code> (gitignored)</li>
<li>SOPS config references keys, doesnt store them</li>
<li>Provider credentials in user-specific locations (not workspace)</li>
</ul>
<h2 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h2>
<h3 id="no-active-workspace-error"><a class="header" href="#no-active-workspace-error">No Active Workspace Error</a></h3>
<pre><code>Error: No active workspace found. Please initialize or activate a workspace.
</code></pre>
<p><strong>Solution</strong>: Initialize or activate a workspace:</p>
<pre><code class="language-bash">workspace-init "my-workspace" "/path/to/workspace" --activate
</code></pre>
<h3 id="config-file-not-found"><a class="header" href="#config-file-not-found">Config File Not Found</a></h3>
<pre><code>Error: Required configuration file not found: {workspace}/config/provisioning.yaml
</code></pre>
<p><strong>Solution</strong>: The workspace config is corrupted or deleted. Re-initialize:</p>
<pre><code class="language-bash">workspace-init "workspace-name" "/existing/path" --providers ["aws"]
</code></pre>
<h3 id="provider-not-configured"><a class="header" href="#provider-not-configured">Provider Not Configured</a></h3>
<p><strong>Solution</strong>: Add provider config to workspace:</p>
<pre><code class="language-bash"># Generate provider config manually
generate-provider-config "/workspace/path" "workspace-name" "aws"
</code></pre>
<h2 id="future-enhancements"><a class="header" href="#future-enhancements">Future Enhancements</a></h2>
<ol>
<li><strong>Workspace Templates</strong>: Pre-configured workspace templates (dev, prod, test)</li>
<li><strong>Workspace Import/Export</strong>: Share workspace configurations</li>
<li><strong>Remote Workspace</strong>: Load workspace from remote Git repository</li>
<li><strong>Workspace Validation</strong>: Comprehensive workspace health checks</li>
<li><strong>Config Migration Tool</strong>: Automated migration from old ENV-based system</li>
</ol>
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
<ul>
<li><strong>config.defaults.toml is ONLY a template</strong> - Never loaded at runtime</li>
<li><strong>Workspaces are self-contained</strong> - Complete config structure generated from templates</li>
<li><strong>New hierarchy</strong>: Workspace → Provider → Platform → User Context → ENV</li>
<li><strong>User context for overrides</strong> - Stored in ~/Library/Application Support/provisioning/</li>
<li><strong>Clear, explicit configuration</strong> - No hidden defaults</li>
</ul>
<h2 id="related-documentation"><a class="header" href="#related-documentation">Related Documentation</a></h2>
<ul>
<li>Template files: <code>provisioning/config/templates/</code></li>
<li>Workspace init: <code>provisioning/core/nulib/lib_provisioning/workspace/init.nu</code></li>
<li>Config loader: <code>provisioning/core/nulib/lib_provisioning/config/loader.nu</code></li>
<li>User guide: <code>docs/user/workspace-management.md</code></li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../configuration/WORKSPACE_CONFIG_IMPLEMENTATION_SUMMARY.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../configuration/WORKSPACE_CONFIG_IMPLEMENTATION_SUMMARY.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
</nav>
</div>
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
<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>