2026 lines
82 KiB
HTML
2026 lines
82 KiB
HTML
|
|
<!DOCTYPE HTML>
|
|||
|
|
<html lang="en" class="ayu sidebar-visible" dir="ltr">
|
|||
|
|
<head>
|
|||
|
|
<!-- Book generated using mdBook -->
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<title>Plugin Integration Guide - 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/user/PLUGIN_INTEGRATION_GUIDE.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="nushell-plugin-integration-guide"><a class="header" href="#nushell-plugin-integration-guide">Nushell Plugin Integration Guide</a></h1>
|
|||
|
|
<p><strong>Version</strong>: 1.0.0
|
|||
|
|
<strong>Last Updated</strong>: 2025-10-09
|
|||
|
|
<strong>Target Audience</strong>: Developers, DevOps Engineers, System Administrators</p>
|
|||
|
|
<hr />
|
|||
|
|
<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="#why-native-plugins">Why Native Plugins?</a></li>
|
|||
|
|
<li><a href="#prerequisites">Prerequisites</a></li>
|
|||
|
|
<li><a href="#installation">Installation</a></li>
|
|||
|
|
<li><a href="#quick-start-5-minutes">Quick Start (5 Minutes)</a></li>
|
|||
|
|
<li><a href="#authentication-plugin-nu_plugin_auth">Authentication Plugin (nu_plugin_auth)</a></li>
|
|||
|
|
<li><a href="#kms-plugin-nu_plugin_kms">KMS Plugin (nu_plugin_kms)</a></li>
|
|||
|
|
<li><a href="#orchestrator-plugin-nu_plugin_orchestrator">Orchestrator Plugin (nu_plugin_orchestrator)</a></li>
|
|||
|
|
<li><a href="#integration-examples">Integration Examples</a></li>
|
|||
|
|
<li><a href="#best-practices">Best Practices</a></li>
|
|||
|
|
<li><a href="#troubleshooting">Troubleshooting</a></li>
|
|||
|
|
<li><a href="#migration-guide">Migration Guide</a></li>
|
|||
|
|
<li><a href="#advanced-configuration">Advanced Configuration</a></li>
|
|||
|
|
<li><a href="#security-considerations">Security Considerations</a></li>
|
|||
|
|
<li><a href="#faq">FAQ</a></li>
|
|||
|
|
</ol>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
|
|||
|
|
<p>The Provisioning Platform provides three native Nushell plugins that dramatically improve performance and user experience compared to traditional HTTP API calls:</p>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Plugin</th><th>Purpose</th><th>Performance Gain</th></tr></thead><tbody>
|
|||
|
|
<tr><td><strong>nu_plugin_auth</strong></td><td>JWT authentication, MFA, session management</td><td>20% faster</td></tr>
|
|||
|
|
<tr><td><strong>nu_plugin_kms</strong></td><td>Encryption/decryption with multiple KMS backends</td><td><strong>10x faster</strong></td></tr>
|
|||
|
|
<tr><td><strong>nu_plugin_orchestrator</strong></td><td>Orchestrator operations without HTTP overhead</td><td><strong>50x faster</strong></td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="architecture-benefits"><a class="header" href="#architecture-benefits">Architecture Benefits</a></h3>
|
|||
|
|
<pre><code>Traditional HTTP Flow:
|
|||
|
|
User Command → HTTP Request → Network → Server Processing → Response → Parse JSON
|
|||
|
|
Total: ~50-100ms per operation
|
|||
|
|
|
|||
|
|
Plugin Flow:
|
|||
|
|
User Command → Direct Rust Function Call → Return Nushell Data Structure
|
|||
|
|
Total: ~1-10ms per operation
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="key-features"><a class="header" href="#key-features">Key Features</a></h3>
|
|||
|
|
<p>✅ <strong>Performance</strong>: 10-50x faster than HTTP API
|
|||
|
|
✅ <strong>Type Safety</strong>: Full Nushell type system integration
|
|||
|
|
✅ <strong>Pipeline Support</strong>: Native Nushell data structures
|
|||
|
|
✅ <strong>Offline Capability</strong>: KMS and orchestrator work without network
|
|||
|
|
✅ <strong>OS Integration</strong>: Native keyring for secure token storage
|
|||
|
|
✅ <strong>Graceful Fallback</strong>: HTTP still available if plugins not installed</p>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="why-native-plugins"><a class="header" href="#why-native-plugins">Why Native Plugins?</a></h2>
|
|||
|
|
<h3 id="performance-comparison"><a class="header" href="#performance-comparison">Performance Comparison</a></h3>
|
|||
|
|
<p>Real-world benchmarks from production workload:</p>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Operation</th><th>HTTP API</th><th>Plugin</th><th>Improvement</th><th>Speedup</th></tr></thead><tbody>
|
|||
|
|
<tr><td><strong>KMS Encrypt (RustyVault)</strong></td><td>~50ms</td><td>~5ms</td><td>-45ms</td><td><strong>10x</strong></td></tr>
|
|||
|
|
<tr><td><strong>KMS Decrypt (RustyVault)</strong></td><td>~50ms</td><td>~5ms</td><td>-45ms</td><td><strong>10x</strong></td></tr>
|
|||
|
|
<tr><td><strong>KMS Encrypt (Age)</strong></td><td>~30ms</td><td>~3ms</td><td>-27ms</td><td><strong>10x</strong></td></tr>
|
|||
|
|
<tr><td><strong>KMS Decrypt (Age)</strong></td><td>~30ms</td><td>~3ms</td><td>-27ms</td><td><strong>10x</strong></td></tr>
|
|||
|
|
<tr><td><strong>Orchestrator Status</strong></td><td>~30ms</td><td>~1ms</td><td>-29ms</td><td><strong>30x</strong></td></tr>
|
|||
|
|
<tr><td><strong>Orchestrator Tasks List</strong></td><td>~50ms</td><td>~5ms</td><td>-45ms</td><td><strong>10x</strong></td></tr>
|
|||
|
|
<tr><td><strong>Orchestrator Validate</strong></td><td>~100ms</td><td>~10ms</td><td>-90ms</td><td><strong>10x</strong></td></tr>
|
|||
|
|
<tr><td><strong>Auth Login</strong></td><td>~100ms</td><td>~80ms</td><td>-20ms</td><td>1.25x</td></tr>
|
|||
|
|
<tr><td><strong>Auth Verify</strong></td><td>~50ms</td><td>~10ms</td><td>-40ms</td><td><strong>5x</strong></td></tr>
|
|||
|
|
<tr><td><strong>Auth MFA Verify</strong></td><td>~80ms</td><td>~60ms</td><td>-20ms</td><td>1.3x</td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="use-case-batch-processing"><a class="header" href="#use-case-batch-processing">Use Case: Batch Processing</a></h3>
|
|||
|
|
<p><strong>Scenario</strong>: Encrypt 100 configuration files</p>
|
|||
|
|
<pre><code class="language-nushell"># HTTP API approach
|
|||
|
|
ls configs/*.yaml | each { |file|
|
|||
|
|
http post http://localhost:9998/encrypt { data: (open $file) }
|
|||
|
|
} | save encrypted/
|
|||
|
|
# Total time: ~5 seconds (50ms × 100)
|
|||
|
|
|
|||
|
|
# Plugin approach
|
|||
|
|
ls configs/*.yaml | each { |file|
|
|||
|
|
kms encrypt (open $file) --backend rustyvault
|
|||
|
|
} | save encrypted/
|
|||
|
|
# Total time: ~0.5 seconds (5ms × 100)
|
|||
|
|
# Result: 10x faster
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="developer-experience-benefits"><a class="header" href="#developer-experience-benefits">Developer Experience Benefits</a></h3>
|
|||
|
|
<p><strong>1. Native Nushell Integration</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># HTTP: Parse JSON, check status codes
|
|||
|
|
let result = http post http://localhost:9998/encrypt { data: "secret" }
|
|||
|
|
if $result.status == "success" {
|
|||
|
|
$result.encrypted
|
|||
|
|
} else {
|
|||
|
|
error make { msg: $result.error }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Plugin: Direct return values
|
|||
|
|
kms encrypt "secret"
|
|||
|
|
# Returns encrypted string directly, errors use Nushell's error system
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>2. Pipeline Friendly</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># HTTP: Requires wrapping, JSON parsing
|
|||
|
|
["secret1", "secret2"] | each { |s|
|
|||
|
|
(http post http://localhost:9998/encrypt { data: $s }).encrypted
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Plugin: Natural pipeline flow
|
|||
|
|
["secret1", "secret2"] | each { |s| kms encrypt $s }
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>3. Tab Completion</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># All plugin commands have full tab completion
|
|||
|
|
kms <TAB>
|
|||
|
|
# → encrypt, decrypt, generate-key, status, backends
|
|||
|
|
|
|||
|
|
kms encrypt --<TAB>
|
|||
|
|
# → --backend, --key, --context
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h2>
|
|||
|
|
<h3 id="required-software"><a class="header" href="#required-software">Required Software</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Software</th><th>Minimum Version</th><th>Purpose</th></tr></thead><tbody>
|
|||
|
|
<tr><td><strong>Nushell</strong></td><td>0.107.1</td><td>Shell and plugin runtime</td></tr>
|
|||
|
|
<tr><td><strong>Rust</strong></td><td>1.75+</td><td>Building plugins from source</td></tr>
|
|||
|
|
<tr><td><strong>Cargo</strong></td><td>(included with Rust)</td><td>Build tool</td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="optional-dependencies"><a class="header" href="#optional-dependencies">Optional Dependencies</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Software</th><th>Purpose</th><th>Platform</th></tr></thead><tbody>
|
|||
|
|
<tr><td><strong>gnome-keyring</strong></td><td>Secure token storage</td><td>Linux</td></tr>
|
|||
|
|
<tr><td><strong>kwallet</strong></td><td>Secure token storage</td><td>Linux (KDE)</td></tr>
|
|||
|
|
<tr><td><strong>age</strong></td><td>Age encryption backend</td><td>All</td></tr>
|
|||
|
|
<tr><td><strong>RustyVault</strong></td><td>High-performance KMS</td><td>All</td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="platform-support"><a class="header" href="#platform-support">Platform Support</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Platform</th><th>Status</th><th>Notes</th></tr></thead><tbody>
|
|||
|
|
<tr><td><strong>macOS</strong></td><td>✅ Full</td><td>Keychain integration</td></tr>
|
|||
|
|
<tr><td><strong>Linux</strong></td><td>✅ Full</td><td>Requires keyring service</td></tr>
|
|||
|
|
<tr><td><strong>Windows</strong></td><td>✅ Full</td><td>Credential Manager integration</td></tr>
|
|||
|
|
<tr><td><strong>FreeBSD</strong></td><td>⚠️ Partial</td><td>No keyring integration</td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="installation"><a class="header" href="#installation">Installation</a></h2>
|
|||
|
|
<h3 id="step-1-clone-or-navigate-to-plugin-directory"><a class="header" href="#step-1-clone-or-navigate-to-plugin-directory">Step 1: Clone or Navigate to Plugin Directory</a></h3>
|
|||
|
|
<pre><code class="language-bash">cd /Users/Akasha/project-provisioning/provisioning/core/plugins/nushell-plugins
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="step-2-build-all-plugins"><a class="header" href="#step-2-build-all-plugins">Step 2: Build All Plugins</a></h3>
|
|||
|
|
<pre><code class="language-bash"># Build in release mode (optimized for performance)
|
|||
|
|
cargo build --release --all
|
|||
|
|
|
|||
|
|
# Or build individually
|
|||
|
|
cargo build --release -p nu_plugin_auth
|
|||
|
|
cargo build --release -p nu_plugin_kms
|
|||
|
|
cargo build --release -p nu_plugin_orchestrator
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Expected output:</strong></p>
|
|||
|
|
<pre><code> Compiling nu_plugin_auth v0.1.0
|
|||
|
|
Compiling nu_plugin_kms v0.1.0
|
|||
|
|
Compiling nu_plugin_orchestrator v0.1.0
|
|||
|
|
Finished release [optimized] target(s) in 2m 15s
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="step-3-register-plugins-with-nushell"><a class="header" href="#step-3-register-plugins-with-nushell">Step 3: Register Plugins with Nushell</a></h3>
|
|||
|
|
<pre><code class="language-bash"># Register all three plugins
|
|||
|
|
plugin add target/release/nu_plugin_auth
|
|||
|
|
plugin add target/release/nu_plugin_kms
|
|||
|
|
plugin add target/release/nu_plugin_orchestrator
|
|||
|
|
|
|||
|
|
# On macOS, full paths:
|
|||
|
|
plugin add $PWD/target/release/nu_plugin_auth
|
|||
|
|
plugin add $PWD/target/release/nu_plugin_kms
|
|||
|
|
plugin add $PWD/target/release/nu_plugin_orchestrator
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="step-4-verify-installation"><a class="header" href="#step-4-verify-installation">Step 4: Verify Installation</a></h3>
|
|||
|
|
<pre><code class="language-bash"># List registered plugins
|
|||
|
|
plugin list | where name =~ "auth|kms|orch"
|
|||
|
|
|
|||
|
|
# Test each plugin
|
|||
|
|
auth --help
|
|||
|
|
kms --help
|
|||
|
|
orch --help
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Expected output:</strong></p>
|
|||
|
|
<pre><code>╭───┬─────────────────────────┬─────────┬───────────────────────────────────╮
|
|||
|
|
│ # │ name │ version │ filename │
|
|||
|
|
├───┼─────────────────────────┼─────────┼───────────────────────────────────┤
|
|||
|
|
│ 0 │ nu_plugin_auth │ 0.1.0 │ .../nu_plugin_auth │
|
|||
|
|
│ 1 │ nu_plugin_kms │ 0.1.0 │ .../nu_plugin_kms │
|
|||
|
|
│ 2 │ nu_plugin_orchestrator │ 0.1.0 │ .../nu_plugin_orchestrator │
|
|||
|
|
╰───┴─────────────────────────┴─────────┴───────────────────────────────────╯
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="step-5-configure-environment-optional"><a class="header" href="#step-5-configure-environment-optional">Step 5: Configure Environment (Optional)</a></h3>
|
|||
|
|
<pre><code class="language-bash"># Add to ~/.config/nushell/env.nu
|
|||
|
|
$env.RUSTYVAULT_ADDR = "http://localhost:8200"
|
|||
|
|
$env.RUSTYVAULT_TOKEN = "your-vault-token"
|
|||
|
|
$env.CONTROL_CENTER_URL = "http://localhost:3000"
|
|||
|
|
$env.ORCHESTRATOR_DATA_DIR = "/opt/orchestrator/data"
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="quick-start-5-minutes"><a class="header" href="#quick-start-5-minutes">Quick Start (5 Minutes)</a></h2>
|
|||
|
|
<h3 id="1-authentication-workflow"><a class="header" href="#1-authentication-workflow">1. Authentication Workflow</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># Login (password prompted securely)
|
|||
|
|
auth login admin
|
|||
|
|
# ✓ Login successful
|
|||
|
|
# User: admin
|
|||
|
|
# Role: Admin
|
|||
|
|
# Expires: 2025-10-09T14:30:00Z
|
|||
|
|
|
|||
|
|
# Verify session
|
|||
|
|
auth verify
|
|||
|
|
# {
|
|||
|
|
# "active": true,
|
|||
|
|
# "user": "admin",
|
|||
|
|
# "role": "Admin",
|
|||
|
|
# "expires_at": "2025-10-09T14:30:00Z"
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Enroll in MFA (optional but recommended)
|
|||
|
|
auth mfa enroll totp
|
|||
|
|
# QR code displayed, save backup codes
|
|||
|
|
|
|||
|
|
# Verify MFA
|
|||
|
|
auth mfa verify --code 123456
|
|||
|
|
# ✓ MFA verification successful
|
|||
|
|
|
|||
|
|
# Logout
|
|||
|
|
auth logout
|
|||
|
|
# ✓ Logged out successfully
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="2-kms-operations"><a class="header" href="#2-kms-operations">2. KMS Operations</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># Encrypt data
|
|||
|
|
kms encrypt "my secret data"
|
|||
|
|
# vault:v1:8GawgGuP...
|
|||
|
|
|
|||
|
|
# Decrypt data
|
|||
|
|
kms decrypt "vault:v1:8GawgGuP..."
|
|||
|
|
# my secret data
|
|||
|
|
|
|||
|
|
# Check available backends
|
|||
|
|
kms status
|
|||
|
|
# {
|
|||
|
|
# "backend": "rustyvault",
|
|||
|
|
# "status": "healthy",
|
|||
|
|
# "url": "http://localhost:8200"
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Encrypt with specific backend
|
|||
|
|
kms encrypt "data" --backend age --key age1xxxxxxx
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="3-orchestrator-operations"><a class="header" href="#3-orchestrator-operations">3. Orchestrator Operations</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># Check orchestrator status (no HTTP call)
|
|||
|
|
orch status
|
|||
|
|
# {
|
|||
|
|
# "active_tasks": 5,
|
|||
|
|
# "completed_tasks": 120,
|
|||
|
|
# "health": "healthy"
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Validate workflow
|
|||
|
|
orch validate workflows/deploy.k
|
|||
|
|
# {
|
|||
|
|
# "valid": true,
|
|||
|
|
# "workflow": { "name": "deploy_k8s", "operations": 5 }
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# List running tasks
|
|||
|
|
orch tasks --status running
|
|||
|
|
# [ { "task_id": "task_123", "name": "deploy_k8s", "progress": 45 } ]
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="4-combined-workflow"><a class="header" href="#4-combined-workflow">4. Combined Workflow</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># Complete authenticated deployment pipeline
|
|||
|
|
auth login admin
|
|||
|
|
| if $in.success { auth verify }
|
|||
|
|
| if $in.active {
|
|||
|
|
orch validate workflows/production.k
|
|||
|
|
| if $in.valid {
|
|||
|
|
kms encrypt (open secrets.yaml | to json)
|
|||
|
|
| save production-secrets.enc
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
# ✓ Pipeline completed successfully
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="authentication-plugin-nu_plugin_auth"><a class="header" href="#authentication-plugin-nu_plugin_auth">Authentication Plugin (nu_plugin_auth)</a></h2>
|
|||
|
|
<p>The authentication plugin manages JWT-based authentication, MFA enrollment/verification, and session management with OS-native keyring integration.</p>
|
|||
|
|
<h3 id="available-commands"><a class="header" href="#available-commands">Available Commands</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Command</th><th>Purpose</th><th>Example</th></tr></thead><tbody>
|
|||
|
|
<tr><td><code>auth login</code></td><td>Login and store JWT</td><td><code>auth login admin</code></td></tr>
|
|||
|
|
<tr><td><code>auth logout</code></td><td>Logout and clear tokens</td><td><code>auth logout</code></td></tr>
|
|||
|
|
<tr><td><code>auth verify</code></td><td>Verify current session</td><td><code>auth verify</code></td></tr>
|
|||
|
|
<tr><td><code>auth sessions</code></td><td>List active sessions</td><td><code>auth sessions</code></td></tr>
|
|||
|
|
<tr><td><code>auth mfa enroll</code></td><td>Enroll in MFA</td><td><code>auth mfa enroll totp</code></td></tr>
|
|||
|
|
<tr><td><code>auth mfa verify</code></td><td>Verify MFA code</td><td><code>auth mfa verify --code 123456</code></td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="command-reference"><a class="header" href="#command-reference">Command Reference</a></h3>
|
|||
|
|
<h4 id="auth-login-username-password"><a class="header" href="#auth-login-username-password"><code>auth login <username> [password]</code></a></h4>
|
|||
|
|
<p>Login to provisioning platform and store JWT tokens securely in OS keyring.</p>
|
|||
|
|
<p><strong>Arguments:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>username</code> (required): Username for authentication</li>
|
|||
|
|
<li><code>password</code> (optional): Password (prompted if not provided)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--url <url></code>: Control center URL (default: <code>http://localhost:3000</code>)</li>
|
|||
|
|
<li><code>--password <password></code>: Password (alternative to positional argument)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Interactive password prompt (recommended)
|
|||
|
|
auth login admin
|
|||
|
|
# Password: ••••••••
|
|||
|
|
# ✓ Login successful
|
|||
|
|
# User: admin
|
|||
|
|
# Role: Admin
|
|||
|
|
# Expires: 2025-10-09T14:30:00Z
|
|||
|
|
|
|||
|
|
# Password in command (not recommended for production)
|
|||
|
|
auth login admin mypassword
|
|||
|
|
|
|||
|
|
# Custom control center URL
|
|||
|
|
auth login admin --url https://control-center.example.com
|
|||
|
|
|
|||
|
|
# Pipeline usage
|
|||
|
|
let creds = { username: "admin", password: (input --suppress-output "Password: ") }
|
|||
|
|
auth login $creds.username $creds.password
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Token Storage Locations:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><strong>macOS</strong>: Keychain Access (<code>login</code> keychain)</li>
|
|||
|
|
<li><strong>Linux</strong>: Secret Service API (gnome-keyring, kwallet)</li>
|
|||
|
|
<li><strong>Windows</strong>: Windows Credential Manager</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Security Notes:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>Tokens encrypted at rest by OS</li>
|
|||
|
|
<li>Requires user authentication to access (macOS Touch ID, Linux password)</li>
|
|||
|
|
<li>Never stored in plain text files</li>
|
|||
|
|
</ul>
|
|||
|
|
<h4 id="auth-logout"><a class="header" href="#auth-logout"><code>auth logout</code></a></h4>
|
|||
|
|
<p>Logout from current session and remove stored tokens from keyring.</p>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Simple logout
|
|||
|
|
auth logout
|
|||
|
|
# ✓ Logged out successfully
|
|||
|
|
|
|||
|
|
# Conditional logout
|
|||
|
|
if (auth verify | get active) {
|
|||
|
|
auth logout
|
|||
|
|
echo "Session terminated"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Logout all sessions (requires admin role)
|
|||
|
|
auth sessions | each { |sess|
|
|||
|
|
auth logout --session-id $sess.session_id
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="auth-verify"><a class="header" href="#auth-verify"><code>auth verify</code></a></h4>
|
|||
|
|
<p>Verify current session status and check token validity.</p>
|
|||
|
|
<p><strong>Returns:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>active</code> (bool): Whether session is active</li>
|
|||
|
|
<li><code>user</code> (string): Username</li>
|
|||
|
|
<li><code>role</code> (string): User role</li>
|
|||
|
|
<li><code>expires_at</code> (datetime): Token expiration</li>
|
|||
|
|
<li><code>mfa_verified</code> (bool): MFA verification status</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Check if logged in
|
|||
|
|
auth verify
|
|||
|
|
# {
|
|||
|
|
# "active": true,
|
|||
|
|
# "user": "admin",
|
|||
|
|
# "role": "Admin",
|
|||
|
|
# "expires_at": "2025-10-09T14:30:00Z",
|
|||
|
|
# "mfa_verified": true
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Pipeline usage
|
|||
|
|
if (auth verify | get active) {
|
|||
|
|
echo "✓ Authenticated"
|
|||
|
|
} else {
|
|||
|
|
auth login admin
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Check expiration
|
|||
|
|
let session = auth verify
|
|||
|
|
if ($session.expires_at | into datetime) < (date now) {
|
|||
|
|
echo "Session expired, re-authenticating..."
|
|||
|
|
auth login $session.user
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="auth-sessions"><a class="header" href="#auth-sessions"><code>auth sessions</code></a></h4>
|
|||
|
|
<p>List all active sessions for current user.</p>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># List all sessions
|
|||
|
|
auth sessions
|
|||
|
|
# [
|
|||
|
|
# {
|
|||
|
|
# "session_id": "sess_abc123",
|
|||
|
|
# "created_at": "2025-10-09T12:00:00Z",
|
|||
|
|
# "expires_at": "2025-10-09T14:30:00Z",
|
|||
|
|
# "ip_address": "192.168.1.100",
|
|||
|
|
# "user_agent": "nushell/0.107.1"
|
|||
|
|
# }
|
|||
|
|
# ]
|
|||
|
|
|
|||
|
|
# Filter recent sessions (last hour)
|
|||
|
|
auth sessions | where created_at > ((date now) - 1hr)
|
|||
|
|
|
|||
|
|
# Find sessions by IP
|
|||
|
|
auth sessions | where ip_address =~ "192.168"
|
|||
|
|
|
|||
|
|
# Count active sessions
|
|||
|
|
auth sessions | length
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="auth-mfa-enroll-type"><a class="header" href="#auth-mfa-enroll-type"><code>auth mfa enroll <type></code></a></h4>
|
|||
|
|
<p>Enroll in Multi-Factor Authentication (TOTP or WebAuthn).</p>
|
|||
|
|
<p><strong>Arguments:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>type</code> (required): MFA type (<code>totp</code> or <code>webauthn</code>)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>TOTP Enrollment:</strong></p>
|
|||
|
|
<pre><code class="language-nushell">auth mfa enroll totp
|
|||
|
|
# ✓ TOTP enrollment initiated
|
|||
|
|
#
|
|||
|
|
# Scan this QR code with your authenticator app:
|
|||
|
|
#
|
|||
|
|
# ████ ▄▄▄▄▄ █▀█ █▄▀▀▀▄ ▄▄▄▄▄ ████
|
|||
|
|
# ████ █ █ █▀▀▀█▄ ▀▀█ █ █ ████
|
|||
|
|
# ████ █▄▄▄█ █ █▀▄ ▀▄▄█ █▄▄▄█ ████
|
|||
|
|
# (QR code continues...)
|
|||
|
|
#
|
|||
|
|
# Or enter manually:
|
|||
|
|
# Secret: JBSWY3DPEHPK3PXP
|
|||
|
|
# URL: otpauth://totp/Provisioning:admin?secret=JBSWY3DPEHPK3PXP&issuer=Provisioning
|
|||
|
|
#
|
|||
|
|
# Backup codes (save securely):
|
|||
|
|
# 1. ABCD-EFGH-IJKL
|
|||
|
|
# 2. MNOP-QRST-UVWX
|
|||
|
|
# 3. YZAB-CDEF-GHIJ
|
|||
|
|
# (8 more codes...)
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>WebAuthn Enrollment:</strong></p>
|
|||
|
|
<pre><code class="language-nushell">auth mfa enroll webauthn
|
|||
|
|
# ✓ WebAuthn enrollment initiated
|
|||
|
|
#
|
|||
|
|
# Insert your security key and touch the button...
|
|||
|
|
# (waiting for device interaction)
|
|||
|
|
#
|
|||
|
|
# ✓ Security key registered successfully
|
|||
|
|
# Device: YubiKey 5 NFC
|
|||
|
|
# Created: 2025-10-09T13:00:00Z
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Supported Authenticator Apps:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>Google Authenticator</li>
|
|||
|
|
<li>Microsoft Authenticator</li>
|
|||
|
|
<li>Authy</li>
|
|||
|
|
<li>1Password</li>
|
|||
|
|
<li>Bitwarden</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Supported Hardware Keys:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>YubiKey (all models)</li>
|
|||
|
|
<li>Titan Security Key</li>
|
|||
|
|
<li>Feitian ePass</li>
|
|||
|
|
<li>macOS Touch ID</li>
|
|||
|
|
<li>Windows Hello</li>
|
|||
|
|
</ul>
|
|||
|
|
<h4 id="auth-mfa-verify---code-code"><a class="header" href="#auth-mfa-verify---code-code"><code>auth mfa verify --code <code></code></a></h4>
|
|||
|
|
<p>Verify MFA code (TOTP or backup code).</p>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--code <code></code> (required): 6-digit TOTP code or backup code</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Verify TOTP code
|
|||
|
|
auth mfa verify --code 123456
|
|||
|
|
# ✓ MFA verification successful
|
|||
|
|
|
|||
|
|
# Verify backup code
|
|||
|
|
auth mfa verify --code ABCD-EFGH-IJKL
|
|||
|
|
# ✓ MFA verification successful (backup code used)
|
|||
|
|
# Warning: This backup code cannot be used again
|
|||
|
|
|
|||
|
|
# Pipeline usage
|
|||
|
|
let code = input "MFA code: "
|
|||
|
|
auth mfa verify --code $code
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Error Cases:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Invalid code
|
|||
|
|
auth mfa verify --code 999999
|
|||
|
|
# Error: Invalid MFA code
|
|||
|
|
# → Verify time synchronization on your device
|
|||
|
|
|
|||
|
|
# Rate limited
|
|||
|
|
auth mfa verify --code 123456
|
|||
|
|
# Error: Too many failed attempts
|
|||
|
|
# → Wait 5 minutes before trying again
|
|||
|
|
|
|||
|
|
# No MFA enrolled
|
|||
|
|
auth mfa verify --code 123456
|
|||
|
|
# Error: MFA not enrolled for this user
|
|||
|
|
# → Run: auth mfa enroll totp
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="environment-variables"><a class="header" href="#environment-variables">Environment Variables</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Variable</th><th>Description</th><th>Default</th></tr></thead><tbody>
|
|||
|
|
<tr><td><code>USER</code></td><td>Default username</td><td>Current OS user</td></tr>
|
|||
|
|
<tr><td><code>CONTROL_CENTER_URL</code></td><td>Control center URL</td><td><code>http://localhost:3000</code></td></tr>
|
|||
|
|
<tr><td><code>AUTH_KEYRING_SERVICE</code></td><td>Keyring service name</td><td><code>provisioning-auth</code></td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="troubleshooting-authentication"><a class="header" href="#troubleshooting-authentication">Troubleshooting Authentication</a></h3>
|
|||
|
|
<p><strong>“No active session”</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Solution: Login first
|
|||
|
|
auth login <username>
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“Keyring error” (macOS)</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check Keychain Access permissions
|
|||
|
|
# System Preferences → Security & Privacy → Privacy → Full Disk Access
|
|||
|
|
# Add: /Applications/Nushell.app (or /usr/local/bin/nu)
|
|||
|
|
|
|||
|
|
# Or grant access manually
|
|||
|
|
security unlock-keychain ~/Library/Keychains/login.keychain-db
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“Keyring error” (Linux)</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Install keyring service
|
|||
|
|
sudo apt install gnome-keyring # Ubuntu/Debian
|
|||
|
|
sudo dnf install gnome-keyring # Fedora
|
|||
|
|
sudo pacman -S gnome-keyring # Arch
|
|||
|
|
|
|||
|
|
# Or use KWallet (KDE)
|
|||
|
|
sudo apt install kwalletmanager
|
|||
|
|
|
|||
|
|
# Start keyring daemon
|
|||
|
|
eval $(gnome-keyring-daemon --start)
|
|||
|
|
export $(gnome-keyring-daemon --start --components=secrets)
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“MFA verification failed”</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Check time synchronization (TOTP requires accurate time)
|
|||
|
|
# macOS:
|
|||
|
|
sudo sntp -sS time.apple.com
|
|||
|
|
|
|||
|
|
# Linux:
|
|||
|
|
sudo ntpdate pool.ntp.org
|
|||
|
|
# Or
|
|||
|
|
sudo systemctl restart systemd-timesyncd
|
|||
|
|
|
|||
|
|
# Use backup code if TOTP not working
|
|||
|
|
auth mfa verify --code ABCD-EFGH-IJKL
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="kms-plugin-nu_plugin_kms"><a class="header" href="#kms-plugin-nu_plugin_kms">KMS Plugin (nu_plugin_kms)</a></h2>
|
|||
|
|
<p>The KMS plugin provides high-performance encryption and decryption using multiple backend providers.</p>
|
|||
|
|
<h3 id="supported-backends"><a class="header" href="#supported-backends">Supported Backends</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Backend</th><th>Performance</th><th>Use Case</th><th>Setup Complexity</th></tr></thead><tbody>
|
|||
|
|
<tr><td><strong>rustyvault</strong></td><td>⚡ Very Fast (~5ms)</td><td>Production KMS</td><td>Medium</td></tr>
|
|||
|
|
<tr><td><strong>age</strong></td><td>⚡ Very Fast (~3ms)</td><td>Local development</td><td>Low</td></tr>
|
|||
|
|
<tr><td><strong>cosmian</strong></td><td>🐢 Moderate (~30ms)</td><td>Cloud KMS</td><td>Medium</td></tr>
|
|||
|
|
<tr><td><strong>aws</strong></td><td>🐢 Moderate (~50ms)</td><td>AWS environments</td><td>Medium</td></tr>
|
|||
|
|
<tr><td><strong>vault</strong></td><td>🐢 Moderate (~40ms)</td><td>Enterprise KMS</td><td>High</td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="backend-selection-guide"><a class="header" href="#backend-selection-guide">Backend Selection Guide</a></h3>
|
|||
|
|
<p><strong>Choose <code>rustyvault</code> when:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ Running in production with high throughput requirements</li>
|
|||
|
|
<li>✅ Need ~5ms encryption/decryption latency</li>
|
|||
|
|
<li>✅ Have RustyVault server deployed</li>
|
|||
|
|
<li>✅ Require key rotation and versioning</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Choose <code>age</code> when:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ Developing locally without external dependencies</li>
|
|||
|
|
<li>✅ Need simple file encryption</li>
|
|||
|
|
<li>✅ Want ~3ms latency</li>
|
|||
|
|
<li>❌ Don’t need centralized key management</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Choose <code>cosmian</code> when:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ Using Cosmian KMS service</li>
|
|||
|
|
<li>✅ Need cloud-based key management</li>
|
|||
|
|
<li>⚠️ Can accept ~30ms latency</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Choose <code>aws</code> when:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ Deployed on AWS infrastructure</li>
|
|||
|
|
<li>✅ Using AWS IAM for access control</li>
|
|||
|
|
<li>✅ Need AWS KMS integration</li>
|
|||
|
|
<li>⚠️ Can accept ~50ms latency</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Choose <code>vault</code> when:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ Using HashiCorp Vault enterprise</li>
|
|||
|
|
<li>✅ Need advanced policy management</li>
|
|||
|
|
<li>✅ Require audit trails</li>
|
|||
|
|
<li>⚠️ Can accept ~40ms latency</li>
|
|||
|
|
</ul>
|
|||
|
|
<h3 id="available-commands-1"><a class="header" href="#available-commands-1">Available Commands</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Command</th><th>Purpose</th><th>Example</th></tr></thead><tbody>
|
|||
|
|
<tr><td><code>kms encrypt</code></td><td>Encrypt data</td><td><code>kms encrypt "secret"</code></td></tr>
|
|||
|
|
<tr><td><code>kms decrypt</code></td><td>Decrypt data</td><td><code>kms decrypt "vault:v1:..."</code></td></tr>
|
|||
|
|
<tr><td><code>kms generate-key</code></td><td>Generate DEK</td><td><code>kms generate-key --spec AES256</code></td></tr>
|
|||
|
|
<tr><td><code>kms status</code></td><td>Backend status</td><td><code>kms status</code></td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="command-reference-1"><a class="header" href="#command-reference-1">Command Reference</a></h3>
|
|||
|
|
<h4 id="kms-encrypt-data---backend-backend"><a class="header" href="#kms-encrypt-data---backend-backend"><code>kms encrypt <data> [--backend <backend>]</code></a></h4>
|
|||
|
|
<p>Encrypt data using specified KMS backend.</p>
|
|||
|
|
<p><strong>Arguments:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>data</code> (required): Data to encrypt (string or binary)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--backend <backend></code>: KMS backend (<code>rustyvault</code>, <code>age</code>, <code>cosmian</code>, <code>aws</code>, <code>vault</code>)</li>
|
|||
|
|
<li><code>--key <key></code>: Key ID or recipient (backend-specific)</li>
|
|||
|
|
<li><code>--context <context></code>: Additional authenticated data (AAD)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Auto-detect backend from environment
|
|||
|
|
kms encrypt "secret configuration data"
|
|||
|
|
# vault:v1:8GawgGuP+emDKX5q...
|
|||
|
|
|
|||
|
|
# RustyVault backend
|
|||
|
|
kms encrypt "data" --backend rustyvault --key provisioning-main
|
|||
|
|
# vault:v1:abc123def456...
|
|||
|
|
|
|||
|
|
# Age backend (local encryption)
|
|||
|
|
kms encrypt "data" --backend age --key age1xxxxxxxxx
|
|||
|
|
# -----BEGIN AGE ENCRYPTED FILE-----
|
|||
|
|
# YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+...
|
|||
|
|
# -----END AGE ENCRYPTED FILE-----
|
|||
|
|
|
|||
|
|
# AWS KMS
|
|||
|
|
kms encrypt "data" --backend aws --key alias/provisioning
|
|||
|
|
# AQICAHhwbGF0Zm9ybS1wcm92aXNpb25p...
|
|||
|
|
|
|||
|
|
# With context (AAD for additional security)
|
|||
|
|
kms encrypt "data" --backend rustyvault --key provisioning-main --context "user=admin,env=production"
|
|||
|
|
|
|||
|
|
# Encrypt file contents
|
|||
|
|
kms encrypt (open config.yaml) --backend rustyvault | save config.yaml.enc
|
|||
|
|
|
|||
|
|
# Encrypt multiple files
|
|||
|
|
ls configs/*.yaml | each { |file|
|
|||
|
|
kms encrypt (open $file.name) --backend age
|
|||
|
|
| save $"encrypted/($file.name).enc"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Output Formats:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><strong>RustyVault</strong>: <code>vault:v1:base64_ciphertext</code></li>
|
|||
|
|
<li><strong>Age</strong>: <code>-----BEGIN AGE ENCRYPTED FILE-----...-----END AGE ENCRYPTED FILE-----</code></li>
|
|||
|
|
<li><strong>AWS</strong>: <code>base64_aws_kms_ciphertext</code></li>
|
|||
|
|
<li><strong>Cosmian</strong>: <code>cosmian:v1:base64_ciphertext</code></li>
|
|||
|
|
</ul>
|
|||
|
|
<h4 id="kms-decrypt-encrypted---backend-backend"><a class="header" href="#kms-decrypt-encrypted---backend-backend"><code>kms decrypt <encrypted> [--backend <backend>]</code></a></h4>
|
|||
|
|
<p>Decrypt KMS-encrypted data.</p>
|
|||
|
|
<p><strong>Arguments:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>encrypted</code> (required): Encrypted data (detects format automatically)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--backend <backend></code>: KMS backend (auto-detected from format if not specified)</li>
|
|||
|
|
<li><code>--context <context></code>: Additional authenticated data (must match encryption context)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Auto-detect backend from format
|
|||
|
|
kms decrypt "vault:v1:8GawgGuP..."
|
|||
|
|
# secret configuration data
|
|||
|
|
|
|||
|
|
# Explicit backend
|
|||
|
|
kms decrypt "vault:v1:abc123..." --backend rustyvault
|
|||
|
|
|
|||
|
|
# Age decryption
|
|||
|
|
kms decrypt "-----BEGIN AGE ENCRYPTED FILE-----..."
|
|||
|
|
# (uses AGE_IDENTITY from environment)
|
|||
|
|
|
|||
|
|
# With context (must match encryption context)
|
|||
|
|
kms decrypt "vault:v1:abc123..." --context "user=admin,env=production"
|
|||
|
|
|
|||
|
|
# Decrypt file
|
|||
|
|
kms decrypt (open config.yaml.enc) | save config.yaml
|
|||
|
|
|
|||
|
|
# Decrypt multiple files
|
|||
|
|
ls encrypted/*.enc | each { |file|
|
|||
|
|
kms decrypt (open $file.name)
|
|||
|
|
| save $"configs/(($file.name | path basename) | str replace '.enc' '')"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Pipeline decryption
|
|||
|
|
open secrets.json
|
|||
|
|
| get database_password_enc
|
|||
|
|
| kms decrypt
|
|||
|
|
| str trim
|
|||
|
|
| psql --dbname mydb --password
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Error Cases:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Invalid ciphertext
|
|||
|
|
kms decrypt "invalid_data"
|
|||
|
|
# Error: Invalid ciphertext format
|
|||
|
|
# → Verify data was encrypted with KMS
|
|||
|
|
|
|||
|
|
# Context mismatch
|
|||
|
|
kms decrypt "vault:v1:abc..." --context "wrong=context"
|
|||
|
|
# Error: Authentication failed (AAD mismatch)
|
|||
|
|
# → Verify encryption context matches
|
|||
|
|
|
|||
|
|
# Backend unavailable
|
|||
|
|
kms decrypt "vault:v1:abc..."
|
|||
|
|
# Error: Failed to connect to RustyVault at http://localhost:8200
|
|||
|
|
# → Check RustyVault is running: curl http://localhost:8200/v1/sys/health
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="kms-generate-key---spec-spec"><a class="header" href="#kms-generate-key---spec-spec"><code>kms generate-key [--spec <spec>]</code></a></h4>
|
|||
|
|
<p>Generate data encryption key (DEK) using KMS envelope encryption.</p>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--spec <spec></code>: Key specification (<code>AES128</code> or <code>AES256</code>, default: <code>AES256</code>)</li>
|
|||
|
|
<li><code>--backend <backend></code>: KMS backend</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Generate AES-256 key
|
|||
|
|
kms generate-key
|
|||
|
|
# {
|
|||
|
|
# "plaintext": "rKz3N8xPq...", # base64-encoded key
|
|||
|
|
# "ciphertext": "vault:v1:...", # encrypted DEK
|
|||
|
|
# "spec": "AES256"
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Generate AES-128 key
|
|||
|
|
kms generate-key --spec AES128
|
|||
|
|
|
|||
|
|
# Use in envelope encryption pattern
|
|||
|
|
let dek = kms generate-key
|
|||
|
|
let encrypted_data = ($data | openssl enc -aes-256-cbc -K $dek.plaintext)
|
|||
|
|
{
|
|||
|
|
data: $encrypted_data,
|
|||
|
|
encrypted_key: $dek.ciphertext
|
|||
|
|
} | save secure_data.json
|
|||
|
|
|
|||
|
|
# Later, decrypt:
|
|||
|
|
let envelope = open secure_data.json
|
|||
|
|
let dek = kms decrypt $envelope.encrypted_key
|
|||
|
|
$envelope.data | openssl enc -d -aes-256-cbc -K $dek
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Use Cases:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>Envelope encryption (encrypt large data locally, protect DEK with KMS)</li>
|
|||
|
|
<li>Database field encryption</li>
|
|||
|
|
<li>File encryption with key wrapping</li>
|
|||
|
|
</ul>
|
|||
|
|
<h4 id="kms-status"><a class="header" href="#kms-status"><code>kms status</code></a></h4>
|
|||
|
|
<p>Show KMS backend status, configuration, and health.</p>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Show current backend status
|
|||
|
|
kms status
|
|||
|
|
# {
|
|||
|
|
# "backend": "rustyvault",
|
|||
|
|
# "status": "healthy",
|
|||
|
|
# "url": "http://localhost:8200",
|
|||
|
|
# "mount_point": "transit",
|
|||
|
|
# "version": "0.1.0",
|
|||
|
|
# "latency_ms": 5
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Check all configured backends
|
|||
|
|
kms status --all
|
|||
|
|
# [
|
|||
|
|
# { "backend": "rustyvault", "status": "healthy", ... },
|
|||
|
|
# { "backend": "age", "status": "available", ... },
|
|||
|
|
# { "backend": "aws", "status": "unavailable", "error": "..." }
|
|||
|
|
# ]
|
|||
|
|
|
|||
|
|
# Filter to specific backend
|
|||
|
|
kms status | where backend == "rustyvault"
|
|||
|
|
|
|||
|
|
# Health check in automation
|
|||
|
|
if (kms status | get status) == "healthy" {
|
|||
|
|
echo "✓ KMS operational"
|
|||
|
|
} else {
|
|||
|
|
error make { msg: "KMS unhealthy" }
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="backend-configuration"><a class="header" href="#backend-configuration">Backend Configuration</a></h3>
|
|||
|
|
<h4 id="rustyvault-backend"><a class="header" href="#rustyvault-backend">RustyVault Backend</a></h4>
|
|||
|
|
<pre><code class="language-bash"># Environment variables
|
|||
|
|
export RUSTYVAULT_ADDR="http://localhost:8200"
|
|||
|
|
export RUSTYVAULT_TOKEN="hvs.xxxxxxxxxxxxx"
|
|||
|
|
export RUSTYVAULT_MOUNT="transit" # Transit engine mount point
|
|||
|
|
export RUSTYVAULT_KEY="provisioning-main" # Default key name
|
|||
|
|
</code></pre>
|
|||
|
|
<pre><code class="language-nushell"># Usage
|
|||
|
|
kms encrypt "data" --backend rustyvault --key provisioning-main
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Setup RustyVault:</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Start RustyVault
|
|||
|
|
rustyvault server -dev
|
|||
|
|
|
|||
|
|
# Enable transit engine
|
|||
|
|
rustyvault secrets enable transit
|
|||
|
|
|
|||
|
|
# Create encryption key
|
|||
|
|
rustyvault write -f transit/keys/provisioning-main
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="age-backend"><a class="header" href="#age-backend">Age Backend</a></h4>
|
|||
|
|
<pre><code class="language-bash"># Generate Age keypair
|
|||
|
|
age-keygen -o ~/.age/key.txt
|
|||
|
|
|
|||
|
|
# Environment variables
|
|||
|
|
export AGE_IDENTITY="$HOME/.age/key.txt" # Private key
|
|||
|
|
export AGE_RECIPIENT="age1xxxxxxxxx" # Public key (from key.txt)
|
|||
|
|
</code></pre>
|
|||
|
|
<pre><code class="language-nushell"># Usage
|
|||
|
|
kms encrypt "data" --backend age
|
|||
|
|
kms decrypt (open file.enc) --backend age
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="aws-kms-backend"><a class="header" href="#aws-kms-backend">AWS KMS Backend</a></h4>
|
|||
|
|
<pre><code class="language-bash"># AWS credentials
|
|||
|
|
export AWS_REGION="us-east-1"
|
|||
|
|
export AWS_ACCESS_KEY_ID="AKIAXXXXX"
|
|||
|
|
export AWS_SECRET_ACCESS_KEY="xxxxx"
|
|||
|
|
|
|||
|
|
# KMS configuration
|
|||
|
|
export AWS_KMS_KEY_ID="alias/provisioning"
|
|||
|
|
</code></pre>
|
|||
|
|
<pre><code class="language-nushell"># Usage
|
|||
|
|
kms encrypt "data" --backend aws --key alias/provisioning
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Setup AWS KMS:</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Create KMS key
|
|||
|
|
aws kms create-key --description "Provisioning Platform"
|
|||
|
|
|
|||
|
|
# Create alias
|
|||
|
|
aws kms create-alias --alias-name alias/provisioning --target-key-id <key-id>
|
|||
|
|
|
|||
|
|
# Grant permissions
|
|||
|
|
aws kms create-grant --key-id <key-id> --grantee-principal <role-arn> \
|
|||
|
|
--operations Encrypt Decrypt GenerateDataKey
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="cosmian-backend"><a class="header" href="#cosmian-backend">Cosmian Backend</a></h4>
|
|||
|
|
<pre><code class="language-bash"># Cosmian KMS configuration
|
|||
|
|
export KMS_HTTP_URL="http://localhost:9998"
|
|||
|
|
export KMS_HTTP_BACKEND="cosmian"
|
|||
|
|
export COSMIAN_API_KEY="your-api-key"
|
|||
|
|
</code></pre>
|
|||
|
|
<pre><code class="language-nushell"># Usage
|
|||
|
|
kms encrypt "data" --backend cosmian
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="vault-backend-hashicorp"><a class="header" href="#vault-backend-hashicorp">Vault Backend (HashiCorp)</a></h4>
|
|||
|
|
<pre><code class="language-bash"># Vault configuration
|
|||
|
|
export VAULT_ADDR="https://vault.example.com:8200"
|
|||
|
|
export VAULT_TOKEN="hvs.xxxxxxxxxxxxx"
|
|||
|
|
export VAULT_MOUNT="transit"
|
|||
|
|
export VAULT_KEY="provisioning"
|
|||
|
|
</code></pre>
|
|||
|
|
<pre><code class="language-nushell"># Usage
|
|||
|
|
kms encrypt "data" --backend vault --key provisioning
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="performance-benchmarks"><a class="header" href="#performance-benchmarks">Performance Benchmarks</a></h3>
|
|||
|
|
<p><strong>Test Setup:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>Data size: 1KB</li>
|
|||
|
|
<li>Iterations: 1000</li>
|
|||
|
|
<li>Hardware: Apple M1, 16GB RAM</li>
|
|||
|
|
<li>Network: localhost</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Results:</strong></p>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Backend</th><th>Encrypt (avg)</th><th>Decrypt (avg)</th><th>Throughput (ops/sec)</th></tr></thead><tbody>
|
|||
|
|
<tr><td>RustyVault</td><td>4.8ms</td><td>5.1ms</td><td>~200</td></tr>
|
|||
|
|
<tr><td>Age</td><td>2.9ms</td><td>3.2ms</td><td>~320</td></tr>
|
|||
|
|
<tr><td>Cosmian HTTP</td><td>31ms</td><td>29ms</td><td>~33</td></tr>
|
|||
|
|
<tr><td>AWS KMS</td><td>52ms</td><td>48ms</td><td>~20</td></tr>
|
|||
|
|
<tr><td>Vault</td><td>38ms</td><td>41ms</td><td>~25</td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<p><strong>Scaling Test (1000 operations):</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># RustyVault: ~5 seconds
|
|||
|
|
0..1000 | each { |_| kms encrypt "data" --backend rustyvault } | length
|
|||
|
|
# Age: ~3 seconds
|
|||
|
|
0..1000 | each { |_| kms encrypt "data" --backend age } | length
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="troubleshooting-kms"><a class="header" href="#troubleshooting-kms">Troubleshooting KMS</a></h3>
|
|||
|
|
<p><strong>“RustyVault connection failed”</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check RustyVault is running
|
|||
|
|
curl http://localhost:8200/v1/sys/health
|
|||
|
|
# Expected: { "initialized": true, "sealed": false }
|
|||
|
|
|
|||
|
|
# Check environment
|
|||
|
|
echo $env.RUSTYVAULT_ADDR
|
|||
|
|
echo $env.RUSTYVAULT_TOKEN
|
|||
|
|
|
|||
|
|
# Test authentication
|
|||
|
|
curl -H "X-Vault-Token: $RUSTYVAULT_TOKEN" $RUSTYVAULT_ADDR/v1/sys/health
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“Age encryption failed”</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check Age keys exist
|
|||
|
|
ls -la ~/.age/
|
|||
|
|
# Expected: key.txt
|
|||
|
|
|
|||
|
|
# Verify key format
|
|||
|
|
cat ~/.age/key.txt | head -1
|
|||
|
|
# Expected: # created: <date>
|
|||
|
|
# Line 2: # public key: age1xxxxx
|
|||
|
|
# Line 3: AGE-SECRET-KEY-xxxxx
|
|||
|
|
|
|||
|
|
# Extract public key
|
|||
|
|
export AGE_RECIPIENT=$(grep "public key:" ~/.age/key.txt | cut -d: -f2 | tr -d ' ')
|
|||
|
|
echo $AGE_RECIPIENT
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“AWS KMS access denied”</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Verify AWS credentials
|
|||
|
|
aws sts get-caller-identity
|
|||
|
|
# Expected: Account, UserId, Arn
|
|||
|
|
|
|||
|
|
# Check KMS key permissions
|
|||
|
|
aws kms describe-key --key-id alias/provisioning
|
|||
|
|
|
|||
|
|
# Test encryption
|
|||
|
|
aws kms encrypt --key-id alias/provisioning --plaintext "test"
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="orchestrator-plugin-nu_plugin_orchestrator"><a class="header" href="#orchestrator-plugin-nu_plugin_orchestrator">Orchestrator Plugin (nu_plugin_orchestrator)</a></h2>
|
|||
|
|
<p>The orchestrator plugin provides direct file-based access to orchestrator state, eliminating HTTP overhead for status queries and validation.</p>
|
|||
|
|
<h3 id="available-commands-2"><a class="header" href="#available-commands-2">Available Commands</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Command</th><th>Purpose</th><th>Example</th></tr></thead><tbody>
|
|||
|
|
<tr><td><code>orch status</code></td><td>Orchestrator status</td><td><code>orch status</code></td></tr>
|
|||
|
|
<tr><td><code>orch validate</code></td><td>Validate workflow</td><td><code>orch validate workflow.k</code></td></tr>
|
|||
|
|
<tr><td><code>orch tasks</code></td><td>List tasks</td><td><code>orch tasks --status running</code></td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="command-reference-2"><a class="header" href="#command-reference-2">Command Reference</a></h3>
|
|||
|
|
<h4 id="orch-status---data-dir-dir"><a class="header" href="#orch-status---data-dir-dir"><code>orch status [--data-dir <dir>]</code></a></h4>
|
|||
|
|
<p>Get orchestrator status from local files (no HTTP, ~1ms latency).</p>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--data-dir <dir></code>: Data directory (default from <code>ORCHESTRATOR_DATA_DIR</code>)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Default data directory
|
|||
|
|
orch status
|
|||
|
|
# {
|
|||
|
|
# "active_tasks": 5,
|
|||
|
|
# "completed_tasks": 120,
|
|||
|
|
# "failed_tasks": 2,
|
|||
|
|
# "pending_tasks": 3,
|
|||
|
|
# "uptime": "2d 4h 15m",
|
|||
|
|
# "health": "healthy"
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Custom data directory
|
|||
|
|
orch status --data-dir /opt/orchestrator/data
|
|||
|
|
|
|||
|
|
# Monitor in loop
|
|||
|
|
while true {
|
|||
|
|
clear
|
|||
|
|
orch status | table
|
|||
|
|
sleep 5sec
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Alert on failures
|
|||
|
|
if (orch status | get failed_tasks) > 0 {
|
|||
|
|
echo "⚠️ Failed tasks detected!"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h4 id="orch-validate-workflowk---strict"><a class="header" href="#orch-validate-workflowk---strict"><code>orch validate <workflow.k> [--strict]</code></a></h4>
|
|||
|
|
<p>Validate workflow KCL file syntax and structure.</p>
|
|||
|
|
<p><strong>Arguments:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>workflow.k</code> (required): Path to KCL workflow file</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--strict</code>: Enable strict validation (warnings as errors)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Basic validation
|
|||
|
|
orch validate workflows/deploy.k
|
|||
|
|
# {
|
|||
|
|
# "valid": true,
|
|||
|
|
# "workflow": {
|
|||
|
|
# "name": "deploy_k8s_cluster",
|
|||
|
|
# "version": "1.0.0",
|
|||
|
|
# "operations": 5
|
|||
|
|
# },
|
|||
|
|
# "warnings": [],
|
|||
|
|
# "errors": []
|
|||
|
|
# }
|
|||
|
|
|
|||
|
|
# Strict mode (warnings cause failure)
|
|||
|
|
orch validate workflows/deploy.k --strict
|
|||
|
|
# Error: Validation failed with warnings:
|
|||
|
|
# - Operation 'create_servers': Missing retry_policy
|
|||
|
|
# - Operation 'install_k8s': Resource limits not specified
|
|||
|
|
|
|||
|
|
# Validate all workflows
|
|||
|
|
ls workflows/*.k | each { |file|
|
|||
|
|
let result = orch validate $file.name
|
|||
|
|
if $result.valid {
|
|||
|
|
echo $"✓ ($file.name)"
|
|||
|
|
} else {
|
|||
|
|
echo $"✗ ($file.name): ($result.errors | str join ', ')"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# CI/CD validation
|
|||
|
|
try {
|
|||
|
|
orch validate workflow.k --strict
|
|||
|
|
echo "✓ Validation passed"
|
|||
|
|
} catch {
|
|||
|
|
echo "✗ Validation failed"
|
|||
|
|
exit 1
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Validation Checks:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ KCL syntax correctness</li>
|
|||
|
|
<li>✅ Required fields present (<code>name</code>, <code>version</code>, <code>operations</code>)</li>
|
|||
|
|
<li>✅ Dependency graph valid (no cycles)</li>
|
|||
|
|
<li>✅ Resource limits within bounds</li>
|
|||
|
|
<li>✅ Provider configurations valid</li>
|
|||
|
|
<li>✅ Operation types supported</li>
|
|||
|
|
<li>⚠️ Optional: Retry policies defined</li>
|
|||
|
|
<li>⚠️ Optional: Resource limits specified</li>
|
|||
|
|
</ul>
|
|||
|
|
<h4 id="orch-tasks---status-status---limit-n"><a class="header" href="#orch-tasks---status-status---limit-n"><code>orch tasks [--status <status>] [--limit <n>]</code></a></h4>
|
|||
|
|
<p>List orchestrator tasks from local state.</p>
|
|||
|
|
<p><strong>Flags:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li><code>--status <status></code>: Filter by status (<code>pending</code>, <code>running</code>, <code>completed</code>, <code>failed</code>)</li>
|
|||
|
|
<li><code>--limit <n></code>: Limit results (default: 100)</li>
|
|||
|
|
<li><code>--data-dir <dir></code>: Data directory</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Examples:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># All tasks (last 100)
|
|||
|
|
orch tasks
|
|||
|
|
# [
|
|||
|
|
# {
|
|||
|
|
# "task_id": "task_abc123",
|
|||
|
|
# "name": "deploy_kubernetes",
|
|||
|
|
# "status": "running",
|
|||
|
|
# "priority": 5,
|
|||
|
|
# "created_at": "2025-10-09T12:00:00Z",
|
|||
|
|
# "progress": 45
|
|||
|
|
# }
|
|||
|
|
# ]
|
|||
|
|
|
|||
|
|
# Running tasks only
|
|||
|
|
orch tasks --status running
|
|||
|
|
|
|||
|
|
# Failed tasks (last 10)
|
|||
|
|
orch tasks --status failed --limit 10
|
|||
|
|
|
|||
|
|
# Pending high-priority tasks
|
|||
|
|
orch tasks --status pending | where priority > 7
|
|||
|
|
|
|||
|
|
# Monitor active tasks
|
|||
|
|
watch {
|
|||
|
|
orch tasks --status running
|
|||
|
|
| select name progress updated_at
|
|||
|
|
| table
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Count tasks by status
|
|||
|
|
orch tasks | group-by status | each { |group|
|
|||
|
|
{ status: $group.0, count: ($group.1 | length) }
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="environment-variables-1"><a class="header" href="#environment-variables-1">Environment Variables</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Variable</th><th>Description</th><th>Default</th></tr></thead><tbody>
|
|||
|
|
<tr><td><code>ORCHESTRATOR_DATA_DIR</code></td><td>Data directory</td><td><code>provisioning/platform/orchestrator/data</code></td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<h3 id="performance-comparison-1"><a class="header" href="#performance-comparison-1">Performance Comparison</a></h3>
|
|||
|
|
<div class="table-wrapper"><table><thead><tr><th>Operation</th><th>HTTP API</th><th>Plugin</th><th>Latency Reduction</th></tr></thead><tbody>
|
|||
|
|
<tr><td>Status query</td><td>~30ms</td><td>~1ms</td><td><strong>97% faster</strong></td></tr>
|
|||
|
|
<tr><td>Validate workflow</td><td>~100ms</td><td>~10ms</td><td><strong>90% faster</strong></td></tr>
|
|||
|
|
<tr><td>List tasks</td><td>~50ms</td><td>~5ms</td><td><strong>90% faster</strong></td></tr>
|
|||
|
|
</tbody></table>
|
|||
|
|
</div>
|
|||
|
|
<p><strong>Use Case: CI/CD Pipeline</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># HTTP approach (slow)
|
|||
|
|
http get http://localhost:9090/tasks --status running
|
|||
|
|
| each { |task| http get $"http://localhost:9090/tasks/($task.id)" }
|
|||
|
|
# Total: ~500ms for 10 tasks
|
|||
|
|
|
|||
|
|
# Plugin approach (fast)
|
|||
|
|
orch tasks --status running
|
|||
|
|
# Total: ~5ms for 10 tasks
|
|||
|
|
# Result: 100x faster
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="troubleshooting-orchestrator"><a class="header" href="#troubleshooting-orchestrator">Troubleshooting Orchestrator</a></h3>
|
|||
|
|
<p><strong>“Failed to read status”</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check data directory exists
|
|||
|
|
ls -la provisioning/platform/orchestrator/data/
|
|||
|
|
|
|||
|
|
# Create if missing
|
|||
|
|
mkdir -p provisioning/platform/orchestrator/data
|
|||
|
|
|
|||
|
|
# Check permissions (must be readable)
|
|||
|
|
chmod 755 provisioning/platform/orchestrator/data
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“Workflow validation failed”</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Use strict mode for detailed errors
|
|||
|
|
orch validate workflows/deploy.k --strict
|
|||
|
|
|
|||
|
|
# Check KCL syntax manually
|
|||
|
|
kcl fmt workflows/deploy.k
|
|||
|
|
kcl run workflows/deploy.k
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“No tasks found”</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check orchestrator running
|
|||
|
|
ps aux | grep orchestrator
|
|||
|
|
|
|||
|
|
# Start orchestrator if not running
|
|||
|
|
cd provisioning/platform/orchestrator
|
|||
|
|
./scripts/start-orchestrator.nu --background
|
|||
|
|
|
|||
|
|
# Check task files
|
|||
|
|
ls provisioning/platform/orchestrator/data/tasks/
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="integration-examples"><a class="header" href="#integration-examples">Integration Examples</a></h2>
|
|||
|
|
<h3 id="example-1-complete-authenticated-deployment"><a class="header" href="#example-1-complete-authenticated-deployment">Example 1: Complete Authenticated Deployment</a></h3>
|
|||
|
|
<p>Full workflow with authentication, secrets, and deployment:</p>
|
|||
|
|
<pre><code class="language-nushell"># Step 1: Login with MFA
|
|||
|
|
auth login admin
|
|||
|
|
auth mfa verify --code (input "MFA code: ")
|
|||
|
|
|
|||
|
|
# Step 2: Verify orchestrator health
|
|||
|
|
if (orch status | get health) != "healthy" {
|
|||
|
|
error make { msg: "Orchestrator unhealthy" }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Step 3: Validate deployment workflow
|
|||
|
|
let validation = orch validate workflows/production-deploy.k --strict
|
|||
|
|
if not $validation.valid {
|
|||
|
|
error make { msg: $"Validation failed: ($validation.errors)" }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Step 4: Encrypt production secrets
|
|||
|
|
let secrets = open secrets/production.yaml
|
|||
|
|
kms encrypt ($secrets | to json) --backend rustyvault --key prod-main
|
|||
|
|
| save secrets/production.enc
|
|||
|
|
|
|||
|
|
# Step 5: Submit deployment
|
|||
|
|
provisioning cluster create production --check
|
|||
|
|
|
|||
|
|
# Step 6: Monitor progress
|
|||
|
|
while (orch tasks --status running | length) > 0 {
|
|||
|
|
orch tasks --status running
|
|||
|
|
| select name progress updated_at
|
|||
|
|
| table
|
|||
|
|
sleep 10sec
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
echo "✓ Deployment complete"
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="example-2-batch-secret-rotation"><a class="header" href="#example-2-batch-secret-rotation">Example 2: Batch Secret Rotation</a></h3>
|
|||
|
|
<p>Rotate all secrets in multiple environments:</p>
|
|||
|
|
<pre><code class="language-nushell"># Rotate database passwords
|
|||
|
|
["dev", "staging", "production"] | each { |env|
|
|||
|
|
# Generate new password
|
|||
|
|
let new_password = (openssl rand -base64 32)
|
|||
|
|
|
|||
|
|
# Encrypt with environment-specific key
|
|||
|
|
let encrypted = kms encrypt $new_password --backend rustyvault --key $"($env)-main"
|
|||
|
|
|
|||
|
|
# Save encrypted password
|
|||
|
|
{
|
|||
|
|
environment: $env,
|
|||
|
|
password_enc: $encrypted,
|
|||
|
|
rotated_at: (date now | format date "%Y-%m-%d %H:%M:%S")
|
|||
|
|
} | save $"secrets/db-password-($env).json"
|
|||
|
|
|
|||
|
|
echo $"✓ Rotated password for ($env)"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="example-3-multi-environment-deployment"><a class="header" href="#example-3-multi-environment-deployment">Example 3: Multi-Environment Deployment</a></h3>
|
|||
|
|
<p>Deploy to multiple environments with validation:</p>
|
|||
|
|
<pre><code class="language-nushell"># Define environments
|
|||
|
|
let environments = [
|
|||
|
|
{ name: "dev", validate: "basic" },
|
|||
|
|
{ name: "staging", validate: "strict" },
|
|||
|
|
{ name: "production", validate: "strict", mfa_required: true }
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# Deploy to each environment
|
|||
|
|
$environments | each { |env|
|
|||
|
|
echo $"Deploying to ($env.name)..."
|
|||
|
|
|
|||
|
|
# Authenticate if production
|
|||
|
|
if $env.mfa_required? {
|
|||
|
|
if not (auth verify | get mfa_verified) {
|
|||
|
|
auth mfa verify --code (input $"MFA code for ($env.name): ")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Validate workflow
|
|||
|
|
let validation = if $env.validate == "strict" {
|
|||
|
|
orch validate $"workflows/($env.name)-deploy.k" --strict
|
|||
|
|
} else {
|
|||
|
|
orch validate $"workflows/($env.name)-deploy.k"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if not $validation.valid {
|
|||
|
|
echo $"✗ Validation failed for ($env.name)"
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Decrypt secrets
|
|||
|
|
let secrets = kms decrypt (open $"secrets/($env.name).enc")
|
|||
|
|
|
|||
|
|
# Deploy
|
|||
|
|
provisioning cluster create $env.name
|
|||
|
|
|
|||
|
|
echo $"✓ Deployed to ($env.name)"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="example-4-automated-backup-and-encryption"><a class="header" href="#example-4-automated-backup-and-encryption">Example 4: Automated Backup and Encryption</a></h3>
|
|||
|
|
<p>Backup configuration files with encryption:</p>
|
|||
|
|
<pre><code class="language-nushell"># Backup script
|
|||
|
|
let backup_dir = $"backups/(date now | format date "%Y%m%d-%H%M%S")"
|
|||
|
|
mkdir $backup_dir
|
|||
|
|
|
|||
|
|
# Backup and encrypt configs
|
|||
|
|
ls configs/**/*.yaml | each { |file|
|
|||
|
|
let encrypted = kms encrypt (open $file.name) --backend age
|
|||
|
|
let backup_path = $"($backup_dir)/($file.name | path basename).enc"
|
|||
|
|
$encrypted | save $backup_path
|
|||
|
|
echo $"✓ Backed up ($file.name)"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Create manifest
|
|||
|
|
{
|
|||
|
|
backup_date: (date now),
|
|||
|
|
files: (ls $"($backup_dir)/*.enc" | length),
|
|||
|
|
backend: "age"
|
|||
|
|
} | save $"($backup_dir)/manifest.json"
|
|||
|
|
|
|||
|
|
echo $"✓ Backup complete: ($backup_dir)"
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="example-5-health-monitoring-dashboard"><a class="header" href="#example-5-health-monitoring-dashboard">Example 5: Health Monitoring Dashboard</a></h3>
|
|||
|
|
<p>Real-time health monitoring:</p>
|
|||
|
|
<pre><code class="language-nushell"># Health dashboard
|
|||
|
|
while true {
|
|||
|
|
clear
|
|||
|
|
|
|||
|
|
# Header
|
|||
|
|
echo "=== Provisioning Platform Health Dashboard ==="
|
|||
|
|
echo $"Updated: (date now | format date "%Y-%m-%d %H:%M:%S")"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
# Authentication status
|
|||
|
|
let auth_status = try { auth verify } catch { { active: false } }
|
|||
|
|
echo $"Auth: (if $auth_status.active { '✓ Active' } else { '✗ Inactive' })"
|
|||
|
|
|
|||
|
|
# KMS status
|
|||
|
|
let kms_health = kms status
|
|||
|
|
echo $"KMS: (if $kms_health.status == 'healthy' { '✓ Healthy' } else { '✗ Unhealthy' })"
|
|||
|
|
|
|||
|
|
# Orchestrator status
|
|||
|
|
let orch_health = orch status
|
|||
|
|
echo $"Orchestrator: (if $orch_health.health == 'healthy' { '✓ Healthy' } else { '✗ Unhealthy' })"
|
|||
|
|
echo $"Active Tasks: ($orch_health.active_tasks)"
|
|||
|
|
echo $"Failed Tasks: ($orch_health.failed_tasks)"
|
|||
|
|
|
|||
|
|
# Task summary
|
|||
|
|
echo ""
|
|||
|
|
echo "=== Running Tasks ==="
|
|||
|
|
orch tasks --status running
|
|||
|
|
| select name progress updated_at
|
|||
|
|
| table
|
|||
|
|
|
|||
|
|
sleep 10sec
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
|
|||
|
|
<h3 id="when-to-use-plugins-vs-http"><a class="header" href="#when-to-use-plugins-vs-http">When to Use Plugins vs HTTP</a></h3>
|
|||
|
|
<p><strong>✅ Use Plugins When:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>Performance is critical (high-frequency operations)</li>
|
|||
|
|
<li>Working in pipelines (Nushell data structures)</li>
|
|||
|
|
<li>Need offline capability (KMS, orchestrator local ops)</li>
|
|||
|
|
<li>Building automation scripts</li>
|
|||
|
|
<li>CI/CD pipelines</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Use HTTP When:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>Calling from external systems (not Nushell)</li>
|
|||
|
|
<li>Need consistent REST API interface</li>
|
|||
|
|
<li>Cross-language integration</li>
|
|||
|
|
<li>Web UI backend</li>
|
|||
|
|
</ul>
|
|||
|
|
<h3 id="performance-optimization"><a class="header" href="#performance-optimization">Performance Optimization</a></h3>
|
|||
|
|
<p><strong>1. Batch Operations</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># ❌ Slow: Individual HTTP calls in loop
|
|||
|
|
ls configs/*.yaml | each { |file|
|
|||
|
|
http post http://localhost:9998/encrypt { data: (open $file.name) }
|
|||
|
|
}
|
|||
|
|
# Total: ~5 seconds (50ms × 100)
|
|||
|
|
|
|||
|
|
# ✅ Fast: Plugin in pipeline
|
|||
|
|
ls configs/*.yaml | each { |file|
|
|||
|
|
kms encrypt (open $file.name)
|
|||
|
|
}
|
|||
|
|
# Total: ~0.5 seconds (5ms × 100)
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>2. Parallel Processing</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Process multiple operations in parallel
|
|||
|
|
ls configs/*.yaml
|
|||
|
|
| par-each { |file|
|
|||
|
|
kms encrypt (open $file.name) | save $"encrypted/($file.name).enc"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>3. Caching Session State</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Cache auth verification
|
|||
|
|
let $auth_cache = auth verify
|
|||
|
|
if $auth_cache.active {
|
|||
|
|
# Use cached result instead of repeated calls
|
|||
|
|
echo $"Authenticated as ($auth_cache.user)"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="error-handling"><a class="header" href="#error-handling">Error Handling</a></h3>
|
|||
|
|
<p><strong>Graceful Degradation:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Try plugin, fallback to HTTP if unavailable
|
|||
|
|
def kms_encrypt [data: string] {
|
|||
|
|
try {
|
|||
|
|
kms encrypt $data
|
|||
|
|
} catch {
|
|||
|
|
http post http://localhost:9998/encrypt { data: $data } | get encrypted
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Comprehensive Error Handling:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Handle all error cases
|
|||
|
|
def safe_deployment [] {
|
|||
|
|
# Check authentication
|
|||
|
|
let auth_status = try {
|
|||
|
|
auth verify
|
|||
|
|
} catch {
|
|||
|
|
echo "✗ Authentication failed, logging in..."
|
|||
|
|
auth login admin
|
|||
|
|
auth verify
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Check KMS health
|
|||
|
|
let kms_health = try {
|
|||
|
|
kms status
|
|||
|
|
} catch {
|
|||
|
|
error make { msg: "KMS unavailable, cannot proceed" }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Validate workflow
|
|||
|
|
let validation = try {
|
|||
|
|
orch validate workflow.k --strict
|
|||
|
|
} catch {
|
|||
|
|
error make { msg: "Workflow validation failed" }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Proceed if all checks pass
|
|||
|
|
if $auth_status.active and $kms_health.status == "healthy" and $validation.valid {
|
|||
|
|
echo "✓ All checks passed, deploying..."
|
|||
|
|
provisioning cluster create production
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="security-best-practices"><a class="header" href="#security-best-practices">Security Best Practices</a></h3>
|
|||
|
|
<p><strong>1. Never Log Decrypted Data</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># ❌ BAD: Logs plaintext password
|
|||
|
|
let password = kms decrypt $encrypted_password
|
|||
|
|
echo $"Password: ($password)" # Visible in logs!
|
|||
|
|
|
|||
|
|
# ✅ GOOD: Use directly without logging
|
|||
|
|
let password = kms decrypt $encrypted_password
|
|||
|
|
psql --dbname mydb --password $password # Not logged
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>2. Use Context (AAD) for Critical Data</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Encrypt with context
|
|||
|
|
let context = $"user=(whoami),env=production,date=(date now | format date "%Y-%m-%d")"
|
|||
|
|
kms encrypt $sensitive_data --context $context
|
|||
|
|
|
|||
|
|
# Decrypt requires same context
|
|||
|
|
kms decrypt $encrypted --context $context
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>3. Rotate Backup Codes</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># After using backup code, generate new set
|
|||
|
|
auth mfa verify --code ABCD-EFGH-IJKL
|
|||
|
|
# Warning: Backup code used
|
|||
|
|
auth mfa regenerate-backups
|
|||
|
|
# New backup codes generated
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>4. Limit Token Lifetime</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Check token expiration before long operations
|
|||
|
|
let session = auth verify
|
|||
|
|
let expires_in = (($session.expires_at | into datetime) - (date now))
|
|||
|
|
if $expires_in < 5min {
|
|||
|
|
echo "⚠️ Token expiring soon, re-authenticating..."
|
|||
|
|
auth login $session.user
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h2>
|
|||
|
|
<h3 id="common-issues-across-plugins"><a class="header" href="#common-issues-across-plugins">Common Issues Across Plugins</a></h3>
|
|||
|
|
<p><strong>“Plugin not found”</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check plugin registration
|
|||
|
|
plugin list | where name =~ "auth|kms|orch"
|
|||
|
|
|
|||
|
|
# Re-register if missing
|
|||
|
|
cd provisioning/core/plugins/nushell-plugins
|
|||
|
|
plugin add target/release/nu_plugin_auth
|
|||
|
|
plugin add target/release/nu_plugin_kms
|
|||
|
|
plugin add target/release/nu_plugin_orchestrator
|
|||
|
|
|
|||
|
|
# Restart Nushell
|
|||
|
|
exit
|
|||
|
|
nu
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“Plugin command failed”</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Enable debug mode
|
|||
|
|
$env.RUST_LOG = "debug"
|
|||
|
|
|
|||
|
|
# Run command again to see detailed errors
|
|||
|
|
kms encrypt "test"
|
|||
|
|
|
|||
|
|
# Check plugin version compatibility
|
|||
|
|
plugin list | where name =~ "kms" | select name version
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>“Permission denied”</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check plugin executable permissions
|
|||
|
|
ls -l provisioning/core/plugins/nushell-plugins/target/release/nu_plugin_*
|
|||
|
|
# Should show: -rwxr-xr-x
|
|||
|
|
|
|||
|
|
# Fix if needed
|
|||
|
|
chmod +x provisioning/core/plugins/nushell-plugins/target/release/nu_plugin_*
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="platform-specific-issues"><a class="header" href="#platform-specific-issues">Platform-Specific Issues</a></h3>
|
|||
|
|
<p><strong>macOS Issues:</strong></p>
|
|||
|
|
<pre><code class="language-bash"># "cannot be opened because the developer cannot be verified"
|
|||
|
|
xattr -d com.apple.quarantine target/release/nu_plugin_auth
|
|||
|
|
xattr -d com.apple.quarantine target/release/nu_plugin_kms
|
|||
|
|
xattr -d com.apple.quarantine target/release/nu_plugin_orchestrator
|
|||
|
|
|
|||
|
|
# Keychain access denied
|
|||
|
|
# System Preferences → Security & Privacy → Privacy → Full Disk Access
|
|||
|
|
# Add: /usr/local/bin/nu
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Linux Issues:</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Keyring service not running
|
|||
|
|
systemctl --user status gnome-keyring-daemon
|
|||
|
|
systemctl --user start gnome-keyring-daemon
|
|||
|
|
|
|||
|
|
# Missing dependencies
|
|||
|
|
sudo apt install libssl-dev pkg-config # Ubuntu/Debian
|
|||
|
|
sudo dnf install openssl-devel # Fedora
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Windows Issues:</strong></p>
|
|||
|
|
<pre><code class="language-powershell"># Credential Manager access denied
|
|||
|
|
# Control Panel → User Accounts → Credential Manager
|
|||
|
|
# Ensure Windows Credential Manager service is running
|
|||
|
|
|
|||
|
|
# Missing Visual C++ runtime
|
|||
|
|
# Download from: https://aka.ms/vs/17/release/vc_redist.x64.exe
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="debugging-techniques"><a class="header" href="#debugging-techniques">Debugging Techniques</a></h3>
|
|||
|
|
<p><strong>Enable Verbose Logging:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Set log level
|
|||
|
|
$env.RUST_LOG = "debug,nu_plugin_auth=trace"
|
|||
|
|
|
|||
|
|
# Run command
|
|||
|
|
auth login admin
|
|||
|
|
|
|||
|
|
# Check logs
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Test Plugin Directly:</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Test plugin communication (advanced)
|
|||
|
|
echo '{"Call": [0, {"name": "auth", "call": "login", "args": ["admin", "password"]}]}' \
|
|||
|
|
| target/release/nu_plugin_auth
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Check Plugin Health:</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Test each plugin
|
|||
|
|
auth --help # Should show auth commands
|
|||
|
|
kms --help # Should show kms commands
|
|||
|
|
orch --help # Should show orch commands
|
|||
|
|
|
|||
|
|
# Test functionality
|
|||
|
|
auth verify # Should return session status
|
|||
|
|
kms status # Should return backend status
|
|||
|
|
orch status # Should return orchestrator status
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="migration-guide"><a class="header" href="#migration-guide">Migration Guide</a></h2>
|
|||
|
|
<h3 id="migrating-from-http-to-plugin-based"><a class="header" href="#migrating-from-http-to-plugin-based">Migrating from HTTP to Plugin-Based</a></h3>
|
|||
|
|
<p><strong>Phase 1: Install Plugins (No Breaking Changes)</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Build and register plugins
|
|||
|
|
cd provisioning/core/plugins/nushell-plugins
|
|||
|
|
cargo build --release --all
|
|||
|
|
plugin add target/release/nu_plugin_auth
|
|||
|
|
plugin add target/release/nu_plugin_kms
|
|||
|
|
plugin add target/release/nu_plugin_orchestrator
|
|||
|
|
|
|||
|
|
# Verify HTTP still works
|
|||
|
|
http get http://localhost:9090/health
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Phase 2: Update Scripts Incrementally</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Before (HTTP)
|
|||
|
|
def encrypt_config [file: string] {
|
|||
|
|
let data = open $file
|
|||
|
|
let result = http post http://localhost:9998/encrypt { data: $data }
|
|||
|
|
$result.encrypted | save $"($file).enc"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# After (Plugin with fallback)
|
|||
|
|
def encrypt_config [file: string] {
|
|||
|
|
let data = open $file
|
|||
|
|
let encrypted = try {
|
|||
|
|
kms encrypt $data --backend rustyvault
|
|||
|
|
} catch {
|
|||
|
|
# Fallback to HTTP if plugin unavailable
|
|||
|
|
(http post http://localhost:9998/encrypt { data: $data }).encrypted
|
|||
|
|
}
|
|||
|
|
$encrypted | save $"($file).enc"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Phase 3: Test Migration</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Run side-by-side comparison
|
|||
|
|
def test_migration [] {
|
|||
|
|
let test_data = "test secret data"
|
|||
|
|
|
|||
|
|
# Plugin approach
|
|||
|
|
let start_plugin = date now
|
|||
|
|
let plugin_result = kms encrypt $test_data
|
|||
|
|
let plugin_time = ((date now) - $start_plugin)
|
|||
|
|
|
|||
|
|
# HTTP approach
|
|||
|
|
let start_http = date now
|
|||
|
|
let http_result = (http post http://localhost:9998/encrypt { data: $test_data }).encrypted
|
|||
|
|
let http_time = ((date now) - $start_http)
|
|||
|
|
|
|||
|
|
echo $"Plugin: ($plugin_time)ms"
|
|||
|
|
echo $"HTTP: ($http_time)ms"
|
|||
|
|
echo $"Speedup: (($http_time / $plugin_time))x"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Phase 4: Gradual Rollout</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Use feature flag for controlled rollout
|
|||
|
|
$env.USE_PLUGINS = true
|
|||
|
|
|
|||
|
|
def encrypt_with_flag [data: string] {
|
|||
|
|
if $env.USE_PLUGINS {
|
|||
|
|
kms encrypt $data
|
|||
|
|
} else {
|
|||
|
|
(http post http://localhost:9998/encrypt { data: $data }).encrypted
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Phase 5: Full Migration</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Replace all HTTP calls with plugin calls
|
|||
|
|
# Remove fallback logic once stable
|
|||
|
|
def encrypt_config [file: string] {
|
|||
|
|
let data = open $file
|
|||
|
|
kms encrypt $data --backend rustyvault | save $"($file).enc"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="rollback-strategy"><a class="header" href="#rollback-strategy">Rollback Strategy</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># If issues arise, quickly rollback
|
|||
|
|
def rollback_to_http [] {
|
|||
|
|
# Remove plugin registrations
|
|||
|
|
plugin rm nu_plugin_auth
|
|||
|
|
plugin rm nu_plugin_kms
|
|||
|
|
plugin rm nu_plugin_orchestrator
|
|||
|
|
|
|||
|
|
# Restart Nushell
|
|||
|
|
exec nu
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="advanced-configuration"><a class="header" href="#advanced-configuration">Advanced Configuration</a></h2>
|
|||
|
|
<h3 id="custom-plugin-paths"><a class="header" href="#custom-plugin-paths">Custom Plugin Paths</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># ~/.config/nushell/config.nu
|
|||
|
|
$env.PLUGIN_PATH = "/opt/provisioning/plugins"
|
|||
|
|
|
|||
|
|
# Register from custom location
|
|||
|
|
plugin add $"($env.PLUGIN_PATH)/nu_plugin_auth"
|
|||
|
|
plugin add $"($env.PLUGIN_PATH)/nu_plugin_kms"
|
|||
|
|
plugin add $"($env.PLUGIN_PATH)/nu_plugin_orchestrator"
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="environment-specific-configuration"><a class="header" href="#environment-specific-configuration">Environment-Specific Configuration</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># ~/.config/nushell/env.nu
|
|||
|
|
|
|||
|
|
# Development environment
|
|||
|
|
if ($env.ENV? == "dev") {
|
|||
|
|
$env.RUSTYVAULT_ADDR = "http://localhost:8200"
|
|||
|
|
$env.CONTROL_CENTER_URL = "http://localhost:3000"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Staging environment
|
|||
|
|
if ($env.ENV? == "staging") {
|
|||
|
|
$env.RUSTYVAULT_ADDR = "https://vault-staging.example.com"
|
|||
|
|
$env.CONTROL_CENTER_URL = "https://control-staging.example.com"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Production environment
|
|||
|
|
if ($env.ENV? == "prod") {
|
|||
|
|
$env.RUSTYVAULT_ADDR = "https://vault.example.com"
|
|||
|
|
$env.CONTROL_CENTER_URL = "https://control.example.com"
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="plugin-aliases"><a class="header" href="#plugin-aliases">Plugin Aliases</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># ~/.config/nushell/config.nu
|
|||
|
|
|
|||
|
|
# Auth shortcuts
|
|||
|
|
alias login = auth login
|
|||
|
|
alias logout = auth logout
|
|||
|
|
alias whoami = auth verify | get user
|
|||
|
|
|
|||
|
|
# KMS shortcuts
|
|||
|
|
alias encrypt = kms encrypt
|
|||
|
|
alias decrypt = kms decrypt
|
|||
|
|
|
|||
|
|
# Orchestrator shortcuts
|
|||
|
|
alias status = orch status
|
|||
|
|
alias tasks = orch tasks
|
|||
|
|
alias validate = orch validate
|
|||
|
|
</code></pre>
|
|||
|
|
<h3 id="custom-commands"><a class="header" href="#custom-commands">Custom Commands</a></h3>
|
|||
|
|
<pre><code class="language-nushell"># ~/.config/nushell/custom_commands.nu
|
|||
|
|
|
|||
|
|
# Encrypt all files in directory
|
|||
|
|
def encrypt-dir [dir: string] {
|
|||
|
|
ls $"($dir)/**/*" | where type == file | each { |file|
|
|||
|
|
kms encrypt (open $file.name) | save $"($file.name).enc"
|
|||
|
|
echo $"✓ Encrypted ($file.name)"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Decrypt all files in directory
|
|||
|
|
def decrypt-dir [dir: string] {
|
|||
|
|
ls $"($dir)/**/*.enc" | each { |file|
|
|||
|
|
kms decrypt (open $file.name)
|
|||
|
|
| save (echo $file.name | str replace '.enc' '')
|
|||
|
|
echo $"✓ Decrypted ($file.name)"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Monitor deployments
|
|||
|
|
def watch-deployments [] {
|
|||
|
|
while true {
|
|||
|
|
clear
|
|||
|
|
echo "=== Active Deployments ==="
|
|||
|
|
orch tasks --status running | table
|
|||
|
|
sleep 5sec
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="security-considerations"><a class="header" href="#security-considerations">Security Considerations</a></h2>
|
|||
|
|
<h3 id="threat-model"><a class="header" href="#threat-model">Threat Model</a></h3>
|
|||
|
|
<p><strong>What Plugins Protect Against:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ Network eavesdropping (no HTTP for KMS/orch)</li>
|
|||
|
|
<li>✅ Token theft from files (keyring storage)</li>
|
|||
|
|
<li>✅ Credential exposure in logs (prompt-based input)</li>
|
|||
|
|
<li>✅ Man-in-the-middle attacks (local file access)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>What Plugins Don’t Protect Against:</strong></p>
|
|||
|
|
<ul>
|
|||
|
|
<li>❌ Memory dumping (decrypted data in RAM)</li>
|
|||
|
|
<li>❌ Malicious plugins (trust registry only)</li>
|
|||
|
|
<li>❌ Compromised OS keyring</li>
|
|||
|
|
<li>❌ Physical access to machine</li>
|
|||
|
|
</ul>
|
|||
|
|
<h3 id="secure-deployment"><a class="header" href="#secure-deployment">Secure Deployment</a></h3>
|
|||
|
|
<p><strong>1. Verify Plugin Integrity</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Check plugin signatures (if available)
|
|||
|
|
sha256sum target/release/nu_plugin_auth
|
|||
|
|
# Compare with published checksums
|
|||
|
|
|
|||
|
|
# Build from trusted source
|
|||
|
|
git clone https://github.com/provisioning-platform/plugins
|
|||
|
|
cd plugins
|
|||
|
|
cargo build --release --all
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>2. Restrict Plugin Access</strong></p>
|
|||
|
|
<pre><code class="language-bash"># Set plugin permissions (only owner can execute)
|
|||
|
|
chmod 700 target/release/nu_plugin_*
|
|||
|
|
|
|||
|
|
# Store in protected directory
|
|||
|
|
sudo mkdir -p /opt/provisioning/plugins
|
|||
|
|
sudo chown $(whoami):$(whoami) /opt/provisioning/plugins
|
|||
|
|
sudo chmod 755 /opt/provisioning/plugins
|
|||
|
|
mv target/release/nu_plugin_* /opt/provisioning/plugins/
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>3. Audit Plugin Usage</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Log plugin calls (for compliance)
|
|||
|
|
def logged_encrypt [data: string] {
|
|||
|
|
let timestamp = date now
|
|||
|
|
let result = kms encrypt $data
|
|||
|
|
{ timestamp: $timestamp, action: "encrypt" } | save --append audit.log
|
|||
|
|
$result
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>4. Rotate Credentials Regularly</strong></p>
|
|||
|
|
<pre><code class="language-nushell"># Weekly credential rotation script
|
|||
|
|
def rotate_credentials [] {
|
|||
|
|
# Re-authenticate
|
|||
|
|
auth logout
|
|||
|
|
auth login admin
|
|||
|
|
|
|||
|
|
# Rotate KMS keys (if supported)
|
|||
|
|
kms rotate-key --key provisioning-main
|
|||
|
|
|
|||
|
|
# Update encrypted secrets
|
|||
|
|
ls secrets/*.enc | each { |file|
|
|||
|
|
let plain = kms decrypt (open $file.name)
|
|||
|
|
kms encrypt $plain | save $file.name
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="faq"><a class="header" href="#faq">FAQ</a></h2>
|
|||
|
|
<p><strong>Q: Can I use plugins without RustyVault/Age installed?</strong></p>
|
|||
|
|
<p>A: Yes, authentication and orchestrator plugins work independently. KMS plugin requires at least one backend configured (Age is easiest for local dev).</p>
|
|||
|
|
<p><strong>Q: Do plugins work in CI/CD pipelines?</strong></p>
|
|||
|
|
<p>A: Yes, plugins work great in CI/CD. For headless environments (no keyring), use environment variables for auth or file-based tokens.</p>
|
|||
|
|
<pre><code class="language-bash"># CI/CD example
|
|||
|
|
export CONTROL_CENTER_TOKEN="jwt-token-here"
|
|||
|
|
kms encrypt "data" --backend age
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Q: How do I update plugins?</strong></p>
|
|||
|
|
<p>A: Rebuild and re-register:</p>
|
|||
|
|
<pre><code class="language-bash">cd provisioning/core/plugins/nushell-plugins
|
|||
|
|
git pull
|
|||
|
|
cargo build --release --all
|
|||
|
|
plugin add --force target/release/nu_plugin_auth
|
|||
|
|
plugin add --force target/release/nu_plugin_kms
|
|||
|
|
plugin add --force target/release/nu_plugin_orchestrator
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Q: Can I use multiple KMS backends simultaneously?</strong></p>
|
|||
|
|
<p>A: Yes, specify <code>--backend</code> for each operation:</p>
|
|||
|
|
<pre><code class="language-nushell">kms encrypt "data1" --backend rustyvault
|
|||
|
|
kms encrypt "data2" --backend age
|
|||
|
|
kms encrypt "data3" --backend aws
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Q: What happens if a plugin crashes?</strong></p>
|
|||
|
|
<p>A: Nushell isolates plugin crashes. The command fails with an error, but Nushell continues running. Check logs with <code>$env.RUST_LOG = "debug"</code>.</p>
|
|||
|
|
<p><strong>Q: Are plugins compatible with older Nushell versions?</strong></p>
|
|||
|
|
<p>A: Plugins require Nushell 0.107.1+. For older versions, use HTTP API.</p>
|
|||
|
|
<p><strong>Q: How do I backup MFA enrollment?</strong></p>
|
|||
|
|
<p>A: Save backup codes securely (password manager, encrypted file). QR code can be re-scanned from the same secret.</p>
|
|||
|
|
<pre><code class="language-nushell"># Save backup codes
|
|||
|
|
auth mfa enroll totp | save mfa-backup-codes.txt
|
|||
|
|
kms encrypt (open mfa-backup-codes.txt) | save mfa-backup-codes.enc
|
|||
|
|
rm mfa-backup-codes.txt
|
|||
|
|
</code></pre>
|
|||
|
|
<p><strong>Q: Can plugins work offline?</strong></p>
|
|||
|
|
<p>A: Partially:</p>
|
|||
|
|
<ul>
|
|||
|
|
<li>✅ <code>kms</code> with Age backend (fully offline)</li>
|
|||
|
|
<li>✅ <code>orch</code> status/tasks (reads local files)</li>
|
|||
|
|
<li>❌ <code>auth</code> (requires control center)</li>
|
|||
|
|
<li>❌ <code>kms</code> with RustyVault/AWS/Vault (requires network)</li>
|
|||
|
|
</ul>
|
|||
|
|
<p><strong>Q: How do I troubleshoot plugin performance?</strong></p>
|
|||
|
|
<p>A: Use Nushell’s timing:</p>
|
|||
|
|
<pre><code class="language-nushell">timeit { kms encrypt "data" }
|
|||
|
|
# 5ms 123μs 456ns
|
|||
|
|
|
|||
|
|
timeit { http post http://localhost:9998/encrypt { data: "data" } }
|
|||
|
|
# 52ms 789μs 123ns
|
|||
|
|
</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="related-documentation"><a class="header" href="#related-documentation">Related Documentation</a></h2>
|
|||
|
|
<ul>
|
|||
|
|
<li><strong>Security System</strong>: <code>/Users/Akasha/project-provisioning/docs/architecture/ADR-009-security-system-complete.md</code></li>
|
|||
|
|
<li><strong>JWT Authentication</strong>: <code>/Users/Akasha/project-provisioning/docs/architecture/JWT_AUTH_IMPLEMENTATION.md</code></li>
|
|||
|
|
<li><strong>Config Encryption</strong>: <code>/Users/Akasha/project-provisioning/docs/user/CONFIG_ENCRYPTION_GUIDE.md</code></li>
|
|||
|
|
<li><strong>RustyVault Integration</strong>: <code>/Users/Akasha/project-provisioning/RUSTYVAULT_INTEGRATION_SUMMARY.md</code></li>
|
|||
|
|
<li><strong>MFA Implementation</strong>: <code>/Users/Akasha/project-provisioning/docs/architecture/MFA_IMPLEMENTATION_SUMMARY.md</code></li>
|
|||
|
|
<li><strong>Nushell Plugins Reference</strong>: <code>/Users/Akasha/project-provisioning/docs/user/NUSHELL_PLUGINS_GUIDE.md</code></li>
|
|||
|
|
</ul>
|
|||
|
|
<hr />
|
|||
|
|
<p><strong>Version</strong>: 1.0.0
|
|||
|
|
<strong>Maintained By</strong>: Platform Team
|
|||
|
|
<strong>Last Updated</strong>: 2025-10-09
|
|||
|
|
<strong>Feedback</strong>: Open an issue or contact platform-team@example.com</p>
|
|||
|
|
|
|||
|
|
</main>
|
|||
|
|
|
|||
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|||
|
|
<!-- Mobile navigation buttons -->
|
|||
|
|
<a rel="prev" href="../user/NUSHELL_PLUGINS_GUIDE.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
|
<i class="fa fa-angle-left"></i>
|
|||
|
|
</a>
|
|||
|
|
|
|||
|
|
<a rel="next prefetch" href="../architecture/ARCHITECTURE_OVERVIEW.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
|
<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="../user/NUSHELL_PLUGINS_GUIDE.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
|
<i class="fa fa-angle-left"></i>
|
|||
|
|
</a>
|
|||
|
|
|
|||
|
|
<a rel="next prefetch" href="../architecture/ARCHITECTURE_OVERVIEW.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
|
<i class="fa fa-angle-right"></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>
|