# System Setup Module # Orchestrates complete provisioning system setup and initialization # Follows Nushell guidelines: explicit types, single purpose, no try-catch use ./mod.nu * use ./detection.nu * use ./validation.nu * use ./wizard.nu * # ============================================================================ # SYSTEM CONFIGURATION CREATION # ============================================================================ # Create system configuration file export def create-system-config-file [ config_base: string config_data: record ]: nothing -> bool { let system_config_path = $"($config_base)/system.toml" let system_config = { version: "1.0.0" install_path: (config_data.install_path? | default "") os_name: (config_data.os_name? | default (detect-os)) os_version: (config_data.os_version? | default "unknown") config_base_path: $config_base cache_base_path: $"($config_base)/cache" workspaces_dir: $"($config_base)/workspaces" system_architecture: (detect-architecture) cpu_count: (config_data.cpu_count? | default (get-cpu-count)) memory_total_gb: (config_data.memory_gb? | default (get-system-memory-gb)) disk_total_gb: (config_data.disk_gb? | default (get-system-disk-gb)) setup_date: (get-timestamp-iso8601) setup_by_user: (get-current-user) setup_hostname: (get-system-hostname) } save-config-toml $system_config_path $system_config } # Create platform services configuration export def create-platform-config-file [ config_base: string config_data: record ]: nothing -> bool { let platform_config_path = $"($config_base)/platform/deployment.toml" let platform_config = { version: "1.0.0" deployment: { mode: (config_data.deployment_mode? | default "docker-compose") location_type: "local" } docker_compose: { enabled: true path: (config_data.docker_compose_path? | default ".") } kubernetes: { enabled: false namespace: "provisioning" kubeconfig: "" manifests_path: "./k8s" } remote_ssh: { enabled: false host: "" user: "deploy" ssh_key: "~/.ssh/id_rsa" } systemd: { enabled: false service_prefix: "provisioning-" } services: { orchestrator: { endpoint: "http://localhost:9090/health" timeout_seconds: 30 type: "http" } control_center: { endpoint: "http://localhost:3000/health" timeout_seconds: 30 type: "http" } kms_service: { endpoint: "http://localhost:3001/health" timeout_seconds: 30 type: "http" } } platform: { orchestrator: { endpoint: "http://localhost:9090" api_version: "v1" } control_center: { url: "http://localhost:3000" token: "" } kms: { endpoint: "http://localhost:3001" backend: "rustyvault" rustyvault: { enabled: true master_key: "" } } } } save-config-toml $platform_config_path $platform_config } # Create user preferences configuration export def create-user-preferences-file [ config_base: string config_data: record ]: nothing -> bool { let user_prefs_path = $"($config_base)/user_preferences.toml" let user_prefs = { output_format: (config_data.output_format? | default "yaml") use_colors: (config_data.use_colors? | default true) file_viewer: (config_data.file_viewer? | default "bat") confirm_delete: (config_data.confirm_delete? | default true) confirm_deploy: (config_data.confirm_deploy? | default true) default_log_level: (config_data.default_log_level? | default "info") default_provider: (config_data.default_provider? | default "local") http_use_curl: (config_data.http_use_curl? | default false) http_timeout_seconds: (config_data.http_timeout_seconds? | default 30) terraform_auto_approve: (config_data.terraform_auto_approve? | default false) editor: (config_data.editor? | default "vim") } save-config-toml $user_prefs_path $user_prefs } # ============================================================================ # PROVIDER CONFIGURATION # ============================================================================ # Create provider configuration file export def create-provider-config-file [ config_base: string provider_name: string credentials_source: string = "" ]: nothing -> bool { let provider_config_path = $"($config_base)/providers/($provider_name).toml" let provider_config = (match $provider_name { "upcloud" => { { api_url: "https://api.upcloud.com/1.3" interface: "API" credentials_source: ($credentials_source | default "rustyvault://system/providers/upcloud") timeout_seconds: 30 } } "aws" => { { region: "us-east-1" credentials_source: ($credentials_source | default "rustyvault://system/providers/aws") timeout_seconds: 30 } } "hetzner" => { { api_url: "https://api.hetzner.cloud/v1" credentials_source: ($credentials_source | default "rustyvault://system/providers/hetzner") timeout_seconds: 30 } } "local" => { { base_path: "/tmp/provisioning-local" timeout_seconds: 10 } } _ => {} }) save-config-toml $provider_config_path $provider_config } # ============================================================================ # RUSTYVAULT BOOTSTRAP SETUP # ============================================================================ # Create RustyVault bootstrap key placeholder export def create-rustyvault-bootstrap-placeholder [ config_base: string ]: nothing -> bool { let bootstrap_path = $"($config_base)/rustyvault_bootstrap.age" # Create placeholder file with instructions let bootstrap_content = "# RustyVault Bootstrap Key (SOPS-Encrypted)\n# This file contains the encrypted RustyVault master key\n# Generated: (get-timestamp-iso8601)\n#\n# To generate the actual key:\n# age-keygen -o > ~/.age/keys.txt\n# sops encrypt --age rustyvault_bootstrap.age > .sops.yaml\n#\n# This is the ONLY secret stored with SOPS - all other credentials\n# are stored encrypted in RustyVault with encrypt/decrypt at-rest support.\n\n# Placeholder - run: provisioning setup rustyvault-bootstrap\n" let result = (do { $bootstrap_content | save -f $bootstrap_path } | complete) ($result.exit_code == 0) } # ============================================================================ # WORKSPACE REGISTRY # ============================================================================ # Create workspace registry file export def create-workspace-registry [ config_base: string ]: nothing -> bool { let registry_path = $"($config_base)/workspaces_registry.yaml" let workspace_registry = { version: "1.0.0" active_workspace: "" workspaces: [] created_at: (get-timestamp-iso8601) metadata: { initialized: true } } save-config-yaml $registry_path $workspace_registry } # ============================================================================ # CEDAR POLICIES SETUP # ============================================================================ # Create default Cedar policies directory and files export def setup-cedar-policies [ config_base: string ]: nothing -> bool { let policies_dir = $"($config_base)/cedar-policies" # Create directory let mkdir_result = (do { mkdir $policies_dir } | complete) if ($mkdir_result.exit_code != 0) { return false } # Create default policies let default_policies = "// Default Cedar Authorization Policies\n// Version: 1.0.0\n// Auto-generated during system setup\n\n// Allow admin full access\npermit(\n principal in Role::\"Admin\",\n action,\n resource\n);\n\n// Allow users workspace-scoped access\npermit(\n principal in Workspace::\".*\",\n action in [\n Action::\"server.create\",\n Action::\"server.read\",\n Action::\"taskserv.install\"\n ],\n resource\n) when {\n context has workspace_id &&\n resource.workspace_id == principal.id\n};\n\n// Require MFA for production operations\nforbid(\n principal,\n action in [\n Action::\"server.delete\",\n Action::\"cluster.delete\",\n Action::\"database.delete\"\n ],\n resource in Environment::\"production\"\n) unless {\n context.mfa_verified == true\n};\n" let policies_file = $"($policies_dir)/default.cedar" let result = (do { $default_policies | save -f $policies_file } | complete) ($result.exit_code == 0) } # ============================================================================ # COMPLETE SYSTEM SETUP # ============================================================================ # Execute complete system setup export def setup-system-complete [ setup_config: record --verbose = false ]: nothing -> record { print-setup-header "Complete System Setup" print "" let config_base = (get-config-base-path) # Step 1: Ensure directories exist print-setup-info "Creating configuration directories..." if not (ensure-config-dirs) { print-setup-error "Failed to create configuration directories" return { success: false errors: ["Failed to create configuration directories"] } } print-setup-success "Configuration directories created" # Step 2: Create system configuration print-setup-info "Creating system configuration..." if not (create-system-config-file $config_base $setup_config.system_config) { print-setup-error "Failed to create system configuration" return { success: false errors: ["Failed to create system configuration"] } } print-setup-success "System configuration created" # Step 3: Create platform configuration print-setup-info "Creating platform services configuration..." if not (create-platform-config-file $config_base $setup_config) { print-setup-error "Failed to create platform configuration" return { success: false errors: ["Failed to create platform configuration"] } } print-setup-success "Platform services configuration created" # Step 4: Create user preferences print-setup-info "Creating user preferences..." if not (create-user-preferences-file $config_base {}) { print-setup-error "Failed to create user preferences" return { success: false errors: ["Failed to create user preferences"] } } print-setup-success "User preferences created" # Step 5: Setup provider configurations print-setup-info "Setting up provider configurations..." for provider in ($setup_config.providers? | default ["local"]) { if not (create-provider-config-file $config_base $provider) { print-setup-warning $"Failed to create provider config for ($provider)" } } print-setup-success "Provider configurations created" # Step 6: Create RustyVault bootstrap placeholder print-setup-info "Setting up RustyVault bootstrap key..." if not (create-rustyvault-bootstrap-placeholder $config_base) { print-setup-warning "Failed to create RustyVault bootstrap placeholder" } print-setup-success "RustyVault bootstrap key placeholder created" # Step 7: Create workspace registry print-setup-info "Creating workspace registry..." if not (create-workspace-registry $config_base) { print-setup-error "Failed to create workspace registry" return { success: false errors: ["Failed to create workspace registry"] } } print-setup-success "Workspace registry created" # Step 8: Setup Cedar policies print-setup-info "Setting up Cedar authorization policies..." if not (setup-cedar-policies $config_base) { print-setup-warning "Failed to setup Cedar policies" } print-setup-success "Cedar authorization policies configured" # Step 9: Create setup metadata print-setup-info "Finalizing setup..." let setup_metadata = { version: "1.0.0" completed_at: (get-timestamp-iso8601) setup_by: (get-current-user) setup_on_hostname: (get-system-hostname) deployment_mode: ($setup_config.deployment_mode? | default "docker-compose") providers: ($setup_config.providers? | default ["local"]) security: ($setup_config.security? | default {}) } let metadata_path = $"($config_base)/setup_metadata.yaml" let save_result = (save-config-yaml $metadata_path $setup_metadata) if not $save_result { print-setup-warning "Failed to save setup metadata" } print "" print-setup-success "System setup completed successfully!" print "" { success: true config_base: $config_base timestamp: (get-timestamp-iso8601) setup_config: $setup_config } } # Run interactive setup wizard with all steps export def run-interactive-setup [ --verbose = false ]: nothing -> record { let wizard_result = (run-setup-wizard --verbose=$verbose) if not $wizard_result.completed { return { success: false reason: "Setup wizard cancelled by user" } } setup-system-complete $wizard_result --verbose=$verbose } # Run setup with defaults (no interaction) export def run-setup-defaults [ --verbose = false ]: nothing -> record { let defaults = (run-setup-with-defaults) setup-system-complete $defaults --verbose=$verbose } # Run minimal setup export def run-setup-minimal [ --verbose = false ]: nothing -> record { let minimal = (run-minimal-setup) setup-system-complete $minimal --verbose=$verbose } # ============================================================================ # SETUP INFORMATION # ============================================================================ # Print setup status export def print-setup-status []: nothing -> nothing { let config_base = (get-config-base-path) print "" print "╔═══════════════════════════════════════════════════════════════╗" print "║ PROVISIONING SYSTEM SETUP STATUS ║" print "╚═══════════════════════════════════════════════════════════════╝" print "" print $"Configuration Base: ($config_base)" print "" if ($"($config_base)/system.toml" | path exists) { print "✅ System configuration found" } else { print "❌ System configuration NOT found" } if ($"($config_base)/platform/deployment.toml" | path exists) { print "✅ Platform configuration found" } else { print "❌ Platform configuration NOT found" } if ($"($config_base)/user_preferences.toml" | path exists) { print "✅ User preferences found" } else { print "❌ User preferences NOT found" } if ($"($config_base)/workspaces_registry.yaml" | path exists) { print "✅ Workspace registry found" } else { print "❌ Workspace registry NOT found" } if ($"($config_base)/cedar-policies" | path exists) { print "✅ Cedar policies directory found" } else { print "❌ Cedar policies directory NOT found" } print "" print "═══════════════════════════════════════════════════════════════" print "" }