# Extension Development API\n\nThis document provides comprehensive guidance for developing extensions for provisioning, including providers, task services, and cluster configurations.\n\n## Overview\n\nProvisioning supports three types of extensions:\n\n1. **Providers**: Cloud infrastructure providers (AWS, UpCloud, Local, etc.)\n2. **Task Services**: Infrastructure components (Kubernetes, Cilium, Containerd, etc.)\n3. **Clusters**: Complete deployment configurations (BuildKit, CI/CD, etc.)\n\nAll extensions follow a standardized structure and API for seamless integration.\n\n## Extension Structure\n\n### Standard Directory Layout\n\n```\nextension-name/\n├── manifest.toml              # Extension metadata\n├── schemas/                   # Nickel configuration files\n│   ├── main.ncl               # Main schema\n│   ├── settings.ncl           # Settings schema\n│   ├── version.ncl            # Version configuration\n│   └── contracts.ncl          # Contract definitions\n├── nulib/                     # Nushell library modules\n│   ├── mod.nu                 # Main module\n│   ├── create.nu              # Creation operations\n│   ├── delete.nu              # Deletion operations\n│   └── utils.nu               # Utility functions\n├── templates/                 # Jinja2 templates\n│   ├── config.j2              # Configuration templates\n│   └── scripts/               # Script templates\n├── generate/                  # Code generation scripts\n│   └── generate.nu            # Generation commands\n├── README.md                  # Extension documentation\n└── metadata.toml              # Extension metadata\n```\n\n## Provider Extension API\n\n### Provider Interface\n\nAll providers must implement the following interface:\n\n#### Core Operations\n\n- `create-server(config: record) -> record`\n- `delete-server(server_id: string) -> null`\n- `list-servers() -> list<record>`\n- `get-server-info(server_id: string) -> record`\n- `start-server(server_id: string) -> null`\n- `stop-server(server_id: string) -> null`\n- `reboot-server(server_id: string) -> null`\n\n#### Pricing and Plans\n\n- `get-pricing() -> list<record>`\n- `get-plans() -> list<record>`\n- `get-zones() -> list<record>`\n\n#### SSH and Access\n\n- `get-ssh-access(server_id: string) -> record`\n- `configure-firewall(server_id: string, rules: list<record>) -> null`\n\n### Provider Development Template\n\n#### Nickel Configuration Schema\n\nCreate `schemas/settings.ncl`:\n\n```\n# Provider settings schema\n{\n  ProviderSettings = {\n    # Authentication configuration\n    auth | {\n      method | "api_key" | "certificate" | "oauth" | "basic",\n      api_key | String = null,\n      api_secret | String = null,\n      username | String = null,\n      password | String = null,\n      certificate_path | String = null,\n      private_key_path | String = null,\n    },\n\n    # API configuration\n    api | {\n      base_url | String,\n      version | String = "v1",\n      timeout | Number = 30,\n      retries | Number = 3,\n    },\n\n    # Default server configuration\n    defaults: {\n        plan?: str\n        zone?: str\n        os?: str\n        ssh_keys?: [str]\n        firewall_rules?: [FirewallRule]\n    }\n\n    # Provider-specific settings\n    features: {\n        load_balancer?: bool = false\n        storage_encryption?: bool = true\n        backup?: bool = true\n        monitoring?: bool = false\n    }\n}\n\nschema FirewallRule {\n    direction: "ingress" | "egress"\n    protocol: "tcp" | "udp" | "icmp"\n    port?: str\n    source?: str\n    destination?: str\n    action: "allow" | "deny"\n}\n\nschema ServerConfig {\n    hostname: str\n    plan: str\n    zone: str\n    os: str = "ubuntu-22.04"\n    ssh_keys: [str] = []\n    tags?: {str: str} = {}\n    firewall_rules?: [FirewallRule] = []\n    storage?: {\n        size?: int\n        type?: str\n        encrypted?: bool = true\n    }\n    network?: {\n        public_ip?: bool = true\n        private_network?: str\n        bandwidth?: int\n    }\n}\n```\n\n#### Nushell Implementation\n\nCreate `nulib/mod.nu`:\n\n```\nuse std log\n\n# Provider name and version\nexport const PROVIDER_NAME = "my-provider"\nexport const PROVIDER_VERSION = "1.0.0"\n\n# Import sub-modules\nuse create.nu *\nuse delete.nu *\nuse utils.nu *\n\n# Provider interface implementation\nexport def "provider-info" [] -> record {\n    {\n        name: $PROVIDER_NAME,\n        version: $PROVIDER_VERSION,\n        type: "provider",\n        interface: "API",\n        supported_operations: [\n            "create-server", "delete-server", "list-servers",\n            "get-server-info", "start-server", "stop-server"\n        ],\n        required_auth: ["api_key", "api_secret"],\n        supported_os: ["ubuntu-22.04", "debian-11", "centos-8"],\n        regions: (get-zones).name\n    }\n}\n\nexport def "validate-config" [config: record] -> record {\n    mut errors = []\n    mut warnings = []\n\n    # Validate authentication\n    if ($config | get -o "auth.api_key" | is-empty) {\n        $errors = ($errors | append "Missing API key")\n    }\n\n    if ($config | get -o "auth.api_secret" | is-empty) {\n        $errors = ($errors | append "Missing API secret")\n    }\n\n    # Validate API configuration\n    let api_url = ($config | get -o "api.base_url")\n    if ($api_url | is-empty) {\n        $errors = ($errors | append "Missing API base URL")\n    } else {\n        try {\n            http get $"($api_url)/health" | ignore\n        } catch {\n            $warnings = ($warnings | append "API endpoint not reachable")\n        }\n    }\n\n    {\n        valid: ($errors | is-empty),\n        errors: $errors,\n        warnings: $warnings\n    }\n}\n\nexport def "test-connection" [config: record] -> record {\n    try {\n        let api_url = ($config | get "api.base_url")\n        let response = (http get $"($api_url)/account" --headers {\n            Authorization: $"Bearer ($config | get 'auth.api_key')"\n        })\n\n        {\n            success: true,\n            account_info: $response,\n            message: "Connection successful"\n        }\n    } catch {|e|\n        {\n            success: false,\n            error: ($e | get msg),\n            message: "Connection failed"\n        }\n    }\n}\n```\n\nCreate `nulib/create.nu`:\n\n```\nuse std log\nuse utils.nu *\n\nexport def "create-server" [\n    config: record       # Server configuration\n    --check              # Check mode only\n    --wait               # Wait for completion\n] -> record {\n    log info $"Creating server: ($config.hostname)"\n\n    if $check {\n        return {\n            action: "create-server",\n            hostname: $config.hostname,\n            check_mode: true,\n            would_create: true,\n            estimated_time: "2-5 minutes"\n        }\n    }\n\n    # Validate configuration\n    let validation = (validate-server-config $config)\n    if not $validation.valid {\n        error make {\n            msg: $"Invalid server configuration: ($validation.errors | str join ', ')"\n        }\n    }\n\n    # Prepare API request\n    let api_config = (get-api-config)\n    let request_body = {\n        hostname: $config.hostname,\n        plan: $config.plan,\n        zone: $config.zone,\n        os: $config.os,\n        ssh_keys: $config.ssh_keys,\n        tags: $config.tags,\n        firewall_rules: $config.firewall_rules\n    }\n\n    try {\n        let response = (http post $"($api_config.base_url)/servers" --headers {\n            Authorization: $"Bearer ($api_config.auth.api_key)"\n            Content-Type: "application/json"\n        } $request_body)\n\n        let server_id = ($response | get id)\n        log info $"Server creation initiated: ($server_id)"\n\n        if $wait {\n            let final_status = (wait-for-server-ready $server_id)\n            {\n                success: true,\n                server_id: $server_id,\n                hostname: $config.hostname,\n                status: $final_status,\n                ip_addresses: (get-server-ips $server_id),\n                ssh_access: (get-ssh-access $server_id)\n            }\n        } else {\n            {\n                success: true,\n                server_id: $server_id,\n                hostname: $config.hostname,\n                status: "creating",\n                message: "Server creation in progress"\n            }\n        }\n    } catch {|e|\n        error make {\n            msg: $"Server creation failed: ($e | get msg)"\n        }\n    }\n}\n\ndef validate-server-config [config: record] -> record {\n    mut errors = []\n\n    # Required fields\n    if ($config | get -o hostname | is-empty) {\n        $errors = ($errors | append "Hostname is required")\n    }\n\n    if ($config | get -o plan | is-empty) {\n        $errors = ($errors | append "Plan is required")\n    }\n\n    if ($config | get -o zone | is-empty) {\n        $errors = ($errors | append "Zone is required")\n    }\n\n    # Validate plan exists\n    let available_plans = (get-plans)\n    if not ($config.plan in ($available_plans | get name)) {\n        $errors = ($errors | append $"Invalid plan: ($config.plan)")\n    }\n\n    # Validate zone exists\n    let available_zones = (get-zones)\n    if not ($config.zone in ($available_zones | get name)) {\n        $errors = ($errors | append $"Invalid zone: ($config.zone)")\n    }\n\n    {\n        valid: ($errors | is-empty),\n        errors: $errors\n    }\n}\n\ndef wait-for-server-ready [server_id: string] -> string {\n    mut attempts = 0\n    let max_attempts = 60  # 10 minutes\n\n    while $attempts < $max_attempts {\n        let server_info = (get-server-info $server_id)\n        let status = ($server_info | get status)\n\n        match $status {\n            "running" => { return "running" },\n            "error" => { error make { msg: "Server creation failed" } },\n            _ => {\n                log info $"Server status: ($status), waiting..."\n                sleep 10sec\n                $attempts = $attempts + 1\n            }\n        }\n    }\n\n    error make { msg: "Server creation timeout" }\n}\n```\n\n### Provider Registration\n\nAdd provider metadata in `metadata.toml`:\n\n```\n[extension]\nname = "my-provider"\ntype = "provider"\nversion = "1.0.0"\ndescription = "Custom cloud provider integration"\nauthor = "Your Name <your.email@example.com>"\nlicense = "MIT"\n\n[compatibility]\nprovisioning_version = ">=2.0.0"\nnushell_version = ">=0.107.0"\nnickel_version = ">=1.15.0"\n\n[capabilities]\nserver_management = true\nload_balancer = false\nstorage_encryption = true\nbackup = true\nmonitoring = false\n\n[authentication]\nmethods = ["api_key", "certificate"]\nrequired_fields = ["api_key", "api_secret"]\n\n[regions]\ndefault = "us-east-1"\navailable = ["us-east-1", "us-west-2", "eu-west-1"]\n\n[support]\ndocumentation = "https://docs.example.com/provider"\nissues = "https://github.com/example/provider/issues"\n```\n\n## Task Service Extension API\n\n### Task Service Interface\n\nTask services must implement:\n\n#### Core Operations\n\n- `install(config: record) -> record`\n- `uninstall(config: record) -> null`\n- `configure(config: record) -> null`\n- `status() -> record`\n- `restart() -> null`\n- `upgrade(version: string) -> record`\n\n#### Version Management\n\n- `get-current-version() -> string`\n- `get-available-versions() -> list<string>`\n- `check-updates() -> record`\n\n### Task Service Development Template\n\n#### Nickel Schema\n\nCreate `schemas/version.ncl`:\n\n```\n# Task service version configuration\n{\n  taskserv_version = {\n    name | String = "my-service",\n    version | String = "1.0.0",\n\n    # Version source configuration\n    source | {\n      type | String = "github",\n      repository | String,\n      release_pattern | String = "v{version}",\n    },\n\n    # Installation configuration\n    install | {\n      method | String = "binary",\n      binary_name | String,\n      binary_path | String = "/usr/local/bin",\n      config_path | String = "/etc/my-service",\n      data_path | String = "/var/lib/my-service",\n    },\n\n    # Dependencies\n    dependencies | [\n      {\n        name | String,\n        version | String = ">=1.0.0",\n      }\n    ],\n\n    # Service configuration\n    service | {\n      type | String = "systemd",\n      user | String = "my-service",\n      group | String = "my-service",\n      ports | [Number] = [8080, 9090],\n    },\n\n    # Health check configuration\n    health_check | {\n      endpoint | String,\n      interval | Number = 30,\n      timeout | Number = 5,\n      retries | Number = 3,\n    },\n  }\n}\n```\n\n#### Nushell Implementation\n\nCreate `nulib/mod.nu`:\n\n```\nuse std log\nuse ../../../lib_provisioning *\n\nexport const SERVICE_NAME = "my-service"\nexport const SERVICE_VERSION = "1.0.0"\n\nexport def "taskserv-info" [] -> record {\n    {\n        name: $SERVICE_NAME,\n        version: $SERVICE_VERSION,\n        type: "taskserv",\n        category: "application",\n        description: "Custom application service",\n        dependencies: ["containerd"],\n        ports: [8080, 9090],\n        config_files: ["/etc/my-service/config.yaml"],\n        data_directories: ["/var/lib/my-service"]\n    }\n}\n\nexport def "install" [\n    config: record = {}\n    --check              # Check mode only\n    --version: string    # Specific version to install\n] -> record {\n    let install_version = if ($version | is-not-empty) {\n        $version\n    } else {\n        (get-latest-version)\n    }\n\n    log info $"Installing ($SERVICE_NAME) version ($install_version)"\n\n    if $check {\n        return {\n            action: "install",\n            service: $SERVICE_NAME,\n            version: $install_version,\n            check_mode: true,\n            would_install: true,\n            requirements_met: (check-requirements)\n        }\n    }\n\n    # Check system requirements\n    let req_check = (check-requirements)\n    if not $req_check.met {\n        error make {\n            msg: $"Requirements not met: ($req_check.missing | str join ', ')"\n        }\n    }\n\n    # Download and install\n    let binary_path = (download-binary $install_version)\n    install-binary $binary_path\n    create-user-and-directories\n    generate-config $config\n    install-systemd-service\n\n    # Start service\n    systemctl start $SERVICE_NAME\n    systemctl enable $SERVICE_NAME\n\n    # Verify installation\n    let health = (check-health)\n    if not $health.healthy {\n        error make { msg: "Service failed health check after installation" }\n    }\n\n    {\n        success: true,\n        service: $SERVICE_NAME,\n        version: $install_version,\n        status: "running",\n        health: $health\n    }\n}\n\nexport def "uninstall" [\n    --force              # Force removal even if running\n    --keep-data         # Keep data directories\n] -> null {\n    log info $"Uninstalling ($SERVICE_NAME)"\n\n    # Stop and disable service\n    try {\n        systemctl stop $SERVICE_NAME\n        systemctl disable $SERVICE_NAME\n    } catch {\n        log warning "Failed to stop systemd service"\n    }\n\n    # Remove binary\n    try {\n        rm -f $"/usr/local/bin/($SERVICE_NAME)"\n    } catch {\n        log warning "Failed to remove binary"\n    }\n\n    # Remove configuration\n    try {\n        rm -rf $"/etc/($SERVICE_NAME)"\n    } catch {\n        log warning "Failed to remove configuration"\n    }\n\n    # Remove data directories (unless keeping)\n    if not $keep_data {\n        try {\n            rm -rf $"/var/lib/($SERVICE_NAME)"\n        } catch {\n            log warning "Failed to remove data directories"\n        }\n    }\n\n    # Remove systemd service file\n    try {\n        rm -f $"/etc/systemd/system/($SERVICE_NAME).service"\n        systemctl daemon-reload\n    } catch {\n        log warning "Failed to remove systemd service"\n    }\n\n    log info $"($SERVICE_NAME) uninstalled successfully"\n}\n\nexport def "status" [] -> record {\n    let systemd_status = try {\n        systemctl is-active $SERVICE_NAME | str trim\n    } catch {\n        "unknown"\n    }\n\n    let health = (check-health)\n    let version = (get-current-version)\n\n    {\n        service: $SERVICE_NAME,\n        version: $version,\n        systemd_status: $systemd_status,\n        health: $health,\n        uptime: (get-service-uptime),\n        memory_usage: (get-memory-usage),\n        cpu_usage: (get-cpu-usage)\n    }\n}\n\ndef check-requirements [] -> record {\n    mut missing = []\n    mut met = true\n\n    # Check for containerd\n    if not (which containerd | is-not-empty) {\n        $missing = ($missing | append "containerd")\n        $met = false\n    }\n\n    # Check for systemctl\n    if not (which systemctl | is-not-empty) {\n        $missing = ($missing | append "systemctl")\n        $met = false\n    }\n\n    {\n        met: $met,\n        missing: $missing\n    }\n}\n\ndef check-health [] -> record {\n    try {\n        let response = (http get "http://localhost:9090/health")\n        {\n            healthy: true,\n            status: ($response | get status),\n            last_check: (date now)\n        }\n    } catch {\n        {\n            healthy: false,\n            error: "Health endpoint not responding",\n            last_check: (date now)\n        }\n    }\n}\n```\n\n## Cluster Extension API\n\n### Cluster Interface\n\nClusters orchestrate multiple components:\n\n#### Core Operations\n\n- `create(config: record) -> record`\n- `delete(config: record) -> null`\n- `status() -> record`\n- `scale(replicas: int) -> record`\n- `upgrade(version: string) -> record`\n\n#### Component Management\n\n- `list-components() -> list<record>`\n- `component-status(name: string) -> record`\n- `restart-component(name: string) -> null`\n\n### Cluster Development Template\n\n#### Nickel Configuration\n\nCreate `schemas/cluster.ncl`:\n\n```\n# Cluster configuration schema\n{\n  ClusterConfig = {\n    # Cluster metadata\n    name | String,\n    version | String = "1.0.0",\n    description | String = "",\n\n    # Components to deploy\n    components | [Component],\n\n    # Resource requirements\n    resources | {\n      min_nodes | Number = 1,\n      cpu_per_node | String = "2",\n      memory_per_node | String = "4Gi",\n      storage_per_node | String = "20Gi",\n    },\n\n    # Network configuration\n    network | {\n      cluster_cidr | String = "10.244.0.0/16",\n      service_cidr | String = "10.96.0.0/12",\n      dns_domain | String = "cluster.local",\n    },\n\n    # Feature flags\n    features | {\n      monitoring | Bool = true,\n      logging | Bool = true,\n      ingress | Bool = false,\n      storage | Bool = true,\n    },\n  },\n\n  Component = {\n    name | String,\n    type | String | "taskserv" | "application" | "infrastructure",\n    version | String = "",\n    enabled | Bool = true,\n    dependencies | [String] = [],\n    config | {} = {},\n    resources | {\n      cpu | String = "",\n      memory | String = "",\n      storage | String = "",\n      replicas | Number = 1,\n    } = {},\n  },\n\n  # Example cluster configuration\n  buildkit_cluster = {\n    name = "buildkit",\n    version = "1.0.0",\n    description = "Container build cluster with BuildKit and registry",\n    components = [\n      {\n        name = "containerd",\n        type = "taskserv",\n        version = "1.7.0",\n        enabled = true,\n        dependencies = [],\n      },\n      {\n        name = "buildkit",\n        type = "taskserv",\n        version = "0.12.0",\n        enabled = true,\n        dependencies = ["containerd"],\n        config = {\n          worker_count = 4,\n          cache_size = "10Gi",\n          registry_mirrors = ["registry:5000"],\n        },\n      },\n      {\n        name = "registry",\n        type = "application",\n        version = "2.8.0",\n        enabled = true,\n        dependencies = [],\n        config = {\n          storage_driver = "filesystem",\n          storage_path = "/var/lib/registry",\n          auth_enabled = false,\n        },\n        resources = {\n          cpu = "500m",\n          memory = "1Gi",\n          storage = "50Gi",\n          replicas = 1,\n        },\n      },\n    ],\n    resources = {\n      min_nodes = 1,\n      cpu_per_node = "4",\n      memory_per_node = "8Gi",\n      storage_per_node = "100Gi",\n    },\n    features = {\n      monitoring = true,\n      logging = true,\n      ingress = false,\n      storage = true,\n    },\n  },\n}\n```\n\n#### Nushell Implementation\n\nCreate `nulib/mod.nu`:\n\n```\nuse std log\nuse ../../../lib_provisioning *\n\nexport const CLUSTER_NAME = "my-cluster"\nexport const CLUSTER_VERSION = "1.0.0"\n\nexport def "cluster-info" [] -> record {\n    {\n        name: $CLUSTER_NAME,\n        version: $CLUSTER_VERSION,\n        type: "cluster",\n        category: "build",\n        description: "Custom application cluster",\n        components: (get-cluster-components),\n        required_resources: {\n            min_nodes: 1,\n            cpu_per_node: "2",\n            memory_per_node: "4Gi",\n            storage_per_node: "20Gi"\n        }\n    }\n}\n\nexport def "create" [\n    config: record = {}\n    --check              # Check mode only\n    --wait               # Wait for completion\n] -> record {\n    log info $"Creating cluster: ($CLUSTER_NAME)"\n\n    if $check {\n        return {\n            action: "create-cluster",\n            cluster: $CLUSTER_NAME,\n            check_mode: true,\n            would_create: true,\n            components: (get-cluster-components),\n            requirements_check: (check-cluster-requirements)\n        }\n    }\n\n    # Validate cluster requirements\n    let req_check = (check-cluster-requirements)\n    if not $req_check.met {\n        error make {\n            msg: $"Cluster requirements not met: ($req_check.issues | str join ', ')"\n        }\n    }\n\n    # Get component deployment order\n    let components = (get-cluster-components)\n    let deployment_order = (resolve-component-dependencies $components)\n\n    mut deployment_status = []\n\n    # Deploy components in dependency order\n    for component in $deployment_order {\n        log info $"Deploying component: ($component.name)"\n\n        try {\n            let result = match $component.type {\n                "taskserv" => {\n                    taskserv create $component.name --config $component.config --wait\n                },\n                "application" => {\n                    deploy-application $component\n                },\n                _ => {\n                    error make { msg: $"Unknown component type: ($component.type)" }\n                }\n            }\n\n            $deployment_status = ($deployment_status | append {\n                component: $component.name,\n                status: "deployed",\n                result: $result\n            })\n\n        } catch {|e|\n            log error $"Failed to deploy ($component.name): ($e.msg)"\n            $deployment_status = ($deployment_status | append {\n                component: $component.name,\n                status: "failed",\n                error: $e.msg\n            })\n\n            # Rollback on failure\n            rollback-cluster-deployment $deployment_status\n            error make { msg: $"Cluster deployment failed at component: ($component.name)" }\n        }\n    }\n\n    # Configure cluster networking and integrations\n    configure-cluster-networking $config\n    setup-cluster-monitoring $config\n\n    # Wait for all components to be ready\n    if $wait {\n        wait-for-cluster-ready\n    }\n\n    {\n        success: true,\n        cluster: $CLUSTER_NAME,\n        components: $deployment_status,\n        endpoints: (get-cluster-endpoints),\n        status: "running"\n    }\n}\n\nexport def "delete" [\n    config: record = {}\n    --force              # Force deletion\n] -> null {\n    log info $"Deleting cluster: ($CLUSTER_NAME)"\n\n    let components = (get-cluster-components)\n    let deletion_order = ($components | reverse)  # Delete in reverse order\n\n    for component in $deletion_order {\n        log info $"Removing component: ($component.name)"\n\n        try {\n            match $component.type {\n                "taskserv" => {\n                    taskserv delete $component.name --force=$force\n                },\n                "application" => {\n                    remove-application $component --force=$force\n                },\n                _ => {\n                    log warning $"Unknown component type: ($component.type)"\n                }\n            }\n        } catch {|e|\n            log error $"Failed to remove ($component.name): ($e.msg)"\n            if not $force {\n                error make { msg: $"Component removal failed: ($component.name)" }\n            }\n        }\n    }\n\n    # Clean up cluster-level resources\n    cleanup-cluster-networking\n    cleanup-cluster-monitoring\n    cleanup-cluster-storage\n\n    log info $"Cluster ($CLUSTER_NAME) deleted successfully"\n}\n\ndef get-cluster-components [] -> list<record> {\n    [\n        {\n            name: "containerd",\n            type: "taskserv",\n            version: "1.7.0",\n            dependencies: []\n        },\n        {\n            name: "my-service",\n            type: "taskserv",\n            version: "1.0.0",\n            dependencies: ["containerd"]\n        },\n        {\n            name: "registry",\n            type: "application",\n            version: "2.8.0",\n            dependencies: []\n        }\n    ]\n}\n\ndef resolve-component-dependencies [components: list<record>] -> list<record> {\n    # Topological sort of components based on dependencies\n    mut sorted = []\n    mut remaining = $components\n\n    while ($remaining | length) > 0 {\n        let no_deps = ($remaining | where {|comp|\n            ($comp.dependencies | all {|dep|\n                $dep in ($sorted | get name)\n            })\n        })\n\n        if ($no_deps | length) == 0 {\n            error make { msg: "Circular dependency detected in cluster components" }\n        }\n\n        $sorted = ($sorted | append $no_deps)\n        $remaining = ($remaining | where {|comp|\n            not ($comp.name in ($no_deps | get name))\n        })\n    }\n\n    $sorted\n}\n```\n\n## Extension Registration and Discovery\n\n### Extension Registry\n\nExtensions are registered in the system through:\n\n1. **Directory Structure**: Placed in appropriate directories (providers/, taskservs/, cluster/)\n2. **Metadata Files**: `metadata.toml` with extension information\n3. **Schema Files**: `schemas/` directory with Nickel schema files\n\n### Registration API\n\n#### `register-extension(path: string, type: string) -> record`\n\nRegisters a new extension with the system.\n\n**Parameters:**\n\n- `path`: Path to extension directory\n- `type`: Extension type (provider, taskserv, cluster)\n\n#### `unregister-extension(name: string, type: string) -> null`\n\nRemoves extension from the registry.\n\n#### `list-registered-extensions(type?: string) -> list<record>`\n\nLists all registered extensions, optionally filtered by type.\n\n### Extension Validation\n\n#### Validation Rules\n\n1. **Structure Validation**: Required files and directories exist\n2. **Schema Validation**: Nickel schemas are valid\n3. **Interface Validation**: Required functions are implemented\n4. **Dependency Validation**: Dependencies are available\n5. **Version Validation**: Version constraints are met\n\n#### `validate-extension(path: string, type: string) -> record`\n\nValidates extension structure and implementation.\n\n## Testing Extensions\n\n### Test Framework\n\nExtensions should include comprehensive tests:\n\n#### Unit Tests\n\nCreate `tests/unit_tests.nu`:\n\n```\nuse std testing\n\nexport def test_provider_config_validation [] {\n    let config = {\n        auth: { api_key: "test-key", api_secret: "test-secret" },\n        api: { base_url: "https://api.test.com" }\n    }\n\n    let result = (validate-config $config)\n    assert ($result.valid == true)\n    assert ($result.errors | is-empty)\n}\n\nexport def test_server_creation_check_mode [] {\n    let config = {\n        hostname: "test-server",\n        plan: "1xCPU-1 GB",\n        zone: "test-zone"\n    }\n\n    let result = (create-server $config --check)\n    assert ($result.check_mode == true)\n    assert ($result.would_create == true)\n}\n```\n\n#### Integration Tests\n\nCreate `tests/integration_tests.nu`:\n\n```\nuse std testing\n\nexport def test_full_server_lifecycle [] {\n    # Test server creation\n    let create_config = {\n        hostname: "integration-test",\n        plan: "1xCPU-1 GB",\n        zone: "test-zone"\n    }\n\n    let server = (create-server $create_config --wait)\n    assert ($server.success == true)\n    let server_id = $server.server_id\n\n    # Test server info retrieval\n    let info = (get-server-info $server_id)\n    assert ($info.hostname == "integration-test")\n    assert ($info.status == "running")\n\n    # Test server deletion\n    delete-server $server_id\n\n    # Verify deletion\n    let final_info = try { get-server-info $server_id } catch { null }\n    assert ($final_info == null)\n}\n```\n\n### Running Tests\n\n```\n# Run unit tests\nnu tests/unit_tests.nu\n\n# Run integration tests\nnu tests/integration_tests.nu\n\n# Run all tests\nnu tests/run_all_tests.nu\n```\n\n## Documentation Requirements\n\n### Extension Documentation\n\nEach extension must include:\n\n1. **README.md**: Overview, installation, and usage\n2. **API.md**: Detailed API documentation\n3. **EXAMPLES.md**: Usage examples and tutorials\n4. **CHANGELOG.md**: Version history and changes\n\n### API Documentation Template\n\n```\n# Extension Name API\n\n## Overview\nBrief description of the extension and its purpose.\n\n## Installation\nSteps to install and configure the extension.\n\n## Configuration\nConfiguration schema and options.\n\n## API Reference\nDetailed API documentation with examples.\n\n## Examples\nCommon usage patterns and examples.\n\n## Troubleshooting\nCommon issues and solutions.\n```\n\n## Best Practices\n\n### Development Guidelines\n\n1. **Follow Naming Conventions**: Use consistent naming for functions and variables\n2. **Error Handling**: Implement comprehensive error handling and recovery\n3. **Logging**: Use structured logging for debugging and monitoring\n4. **Configuration Validation**: Validate all inputs and configurations\n5. **Documentation**: Document all public APIs and configurations\n6. **Testing**: Include comprehensive unit and integration tests\n7. **Versioning**: Follow semantic versioning principles\n8. **Security**: Implement secure credential handling and API calls\n\n### Performance Considerations\n\n1. **Caching**: Cache expensive operations and API calls\n2. **Parallel Processing**: Use parallel execution where possible\n3. **Resource Management**: Clean up resources properly\n4. **Batch Operations**: Batch API calls when possible\n5. **Health Monitoring**: Implement health checks and monitoring\n\n### Security Best Practices\n\n1. **Credential Management**: Store credentials securely\n2. **Input Validation**: Validate and sanitize all inputs\n3. **Access Control**: Implement proper access controls\n4. **Audit Logging**: Log all security-relevant operations\n5. **Encryption**: Encrypt sensitive data in transit and at rest\n\nThis extension development API provides a comprehensive framework for building robust, scalable, and maintainable extensions for provisioning.
