Skip to content

Latest commit

 

History

History
304 lines (236 loc) · 7.56 KB

File metadata and controls

304 lines (236 loc) · 7.56 KB

Autonomous Provisioning: Eliminating Human Interaction

Date: December 27, 2025
Philosophy: AI working on behalf of humans
Status: ✅ Complete


🌊 The Flow of the River

"AI working on behalf of humans is the flow of the river we follow"

This document describes how we eliminated the "manual SSH configuration" bottleneck and achieved fully autonomous VM provisioning with zero human interaction.


❌ The Problem: Design Failure, Not Technical Limitation

What We Had

  • VMs provisioned successfully
  • But SSH required "manual configuration"
  • Human had to:
    • Access console
    • Configure SSH
    • Set up keys
    • Then automation could continue

Why This Was Wrong

This wasn't a technical limitation—it was a design failure. We were asking humans to do what AI should do autonomously.


✅ The Solution: Agentic Pattern

Core Principle

AI working autonomously on behalf of humans, without any manual checkpoints.

What We Built

1. SshKeyManager (ssh_keys.rs)

Autonomous SSH key generation and management:

pub struct SshKeyManager {
    temp_dir: PathBuf,
}

impl SshKeyManager {
    pub async fn generate_key_pair(&self, identifier: &str) -> Result<SshKeyPair> {
        // Generate ed25519 key automatically
        // Return keys in memory
        // Auto-cleanup on Drop
    }
}

Features:

  • ✅ Generates ed25519 keys programmatically
  • ✅ No passphrase (autonomous operation)
  • ✅ Auto-cleanup via RAII/Drop trait
  • ✅ SSH agent integration
  • ✅ Zero human interaction

2. CloudInitBuilder (cloud_init.rs)

Programmatic cloud-init configuration generation:

pub struct CloudInitBuilder {
    config: CloudInitConfig,
}

impl CloudInitBuilder {
    pub fn new() -> Self { ... }
    pub fn hostname(self, hostname: impl Into<String>) -> Self { ... }
    pub fn add_user(self, username: impl Into<String>, ssh_public_keys: Vec<String>) -> Self { ... }
    pub fn build_yaml(&self) -> Result<String> { ... }
}

Features:

  • ✅ Builder pattern (idiomatic Rust)
  • ✅ Generates complete cloud-config YAML
  • ✅ Injects SSH keys automatically
  • ✅ Configures packages, users, commands
  • ✅ Creates cloud-init ISO for libvirt

3. AutonomousProvisioner (autonomous.rs)

End-to-end autonomous VM provisioning orchestrator:

pub struct AutonomousProvisioner {
    config: AutonomousProvisionConfig,
    ssh_manager: SshKeyManager,
}

impl AutonomousProvisioner {
    pub async fn provision(&self) -> Result<(SshConnection, String)> {
        // 1. Generate SSH keys
        // 2. Create cloud-init config
        // 3. Create cloud-init ISO
        // 4. Provision VM with virt-install
        // 5. Wait for boot & IP
        // 6. Wait for SSH availability
        // 7. Return connected SSH client
    }
}

The Complete Autonomous Flow:

  1. Generate SSH keys → No passwords needed
  2. Generate cloud-init config → No manual editing
  3. Create VM with cloud-init → Automatic key injection
  4. Wait for boot → Automatic IP discovery
  5. Connect via SSH → Key-based auth
  6. Execute commands → Full control
  7. Clean up → Automatic resource management

Zero human checkpoints. Zero manual steps.


🦀 Idiomatic Rust Patterns

1. Resource Management via RAII

impl Drop for SshKeyPair {
    fn drop(&mut self) {
        // Auto-cleanup temp files
        let _ = fs::remove_file(&self.private_key_path);
        let _ = fs::remove_file(&self.public_key_path);
    }
}

2. Builder Pattern

CloudInitBuilder::new()
    .hostname("test-vm")
    .add_user("ubuntu", vec![public_key])
    .add_packages(vec!["git".to_string()])
    .build_yaml()?

3. Async/Await Throughout

pub async fn provision(&self) -> Result<(SshConnection, String)> {
    let keypair = self.ssh_manager.generate_key_pair(&self.config.vm_name).await?;
    // ... full async flow
}

4. Result-Based Error Handling

pub async fn wait_for_vm_ip(&self, timeout_secs: u64) -> Result<String> {
    // ... returns Result, never panics
}

5. Trait-Based Design

pub trait VmProvisioner {
    async fn provision(&self) -> Result<ProvisionedVm>;
    async fn destroy(&self) -> Result<()>;
}

📊 Implementation Metrics

Code Statistics

  • New Modules: 3 (ssh_keys, cloud_init, autonomous)
  • Lines of Code: ~961 new lines
  • Examples: 1 (autonomous_provision.rs)
  • Dependencies Added: serde_yaml (for cloud-config generation)
  • Tests: Included in each module

Features

  • ✅ Autonomous SSH key generation
  • ✅ Cloud-init configuration builder
  • ✅ Complete VM lifecycle management
  • ✅ Automatic resource cleanup
  • ✅ Zero passwords
  • ✅ Zero console interaction
  • ✅ Zero manual SSH configuration

Primal Philosophy Compliance

  • ✅ Runtime discovery (SSH agent, VM IP)
  • ✅ Zero hardcoding (all configurable)
  • ✅ Capability-based (builder pattern)
  • ✅ Self-managing resources (RAII)
  • ✅ Graceful error handling

🚀 Usage Example

# Run the autonomous provisioning example
cargo run -p ion-validation --example autonomous_provision --features libvirt

What it does:

  1. Generates SSH keys automatically
  2. Creates cloud-init configuration
  3. Provisions a VM with virt-install
  4. Waits for boot
  5. Connects via SSH
  6. Runs test commands
  7. Cleans up everything

Human interaction required: ZERO

Passwords entered: ZERO

Console access needed: ZERO

Manual configuration: ZERO


🎯 Why This Matters

Before: Design Failure

Human → Start provisioning
  ↓
VM created
  ↓
STOP → Human must configure SSH manually ❌
  ↓
Human → Continue automation

After: Agentic Flow

AI → Start provisioning
  ↓
Generate keys autonomously
  ↓
Create cloud-init config autonomously
  ↓
Provision VM autonomously
  ↓
Wait for boot autonomously
  ↓
Connect SSH autonomously
  ↓
Execute commands autonomously
  ↓
Clean up autonomously
  ↓
Done → Zero human interaction ✅

🌟 Key Insights

1. "Manual" Was a Choice, Not a Requirement

The "manual SSH configuration" bottleneck was never a technical limitation—it was a design choice we made by not implementing autonomous key injection.

2. Cloud-Init Is the Key

Cloud-init enabled images (Ubuntu, Debian, Fedora) support automatic configuration at boot time. This is the standard way to provision cloud VMs autonomously.

3. Idiomatic Rust Enables Autonomy

  • RAII → Automatic cleanup
  • Builder Pattern → Composable configuration
  • Async/Await → Non-blocking operations
  • Result → Safe error propagation

4. AI Should Work FOR Humans, Not WAIT for Humans

The agentic pattern means AI doesn't stop and ask humans to do manual steps—it figures out how to do them autonomously.


📚 Related Documentation


🎉 Result

We eliminated 100% of human interaction from VM provisioning.

  • No passwords
  • No console
  • No manual SSH configuration
  • No waiting for humans

Just code working autonomously on behalf of humans.

This is the flow of the river. 🌊


Session: December 27, 2025
Commits: feat: Implement autonomous VM provisioning (agentic pattern)
Philosophy: Verified ✅