# ThreeFold Grid Deployment Automation Specification **Document Purpose**: Technical specification for automated deployment pipeline from TFC payment to ThreeFold Grid service activation. **Last Updated**: 2025-08-04 **Status**: Implementation Ready --- ## Overview This document outlines the automated deployment pipeline that converts TFC credits to TFT tokens and deploys services on the ThreeFold Grid, providing seamless Web2-to-Web3 bridge functionality. --- ## Deployment Pipeline Architecture ```mermaid graph TD A[User Purchase Complete] --> B[TFC Deducted] B --> C[Deployment Queued] C --> D[Commission Calculated] D --> E[USD → TFT Conversion] E --> F[Grid Node Selection] F --> G[Resource Allocation] G --> H[Service Deployment] H --> I[Health Check] I --> J{Deployment Success?} J -->|Yes| K[Service Active] J -->|No| L[Rollback & Refund] K --> M[User Notification] L --> N[Error Notification] ``` --- ## Core Components ### 1. Deployment Queue Service ```rust // src/services/deployment_queue.rs use tokio::sync::mpsc; use std::collections::VecDeque; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DeploymentJob { pub id: String, pub user_id: String, pub service_spec: ServiceSpec, pub tfc_amount: Decimal, pub commission_amount: Decimal, pub grid_payment_amount: Decimal, pub priority: DeploymentPriority, pub created_at: DateTime, pub max_retries: u32, pub retry_count: u32, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum DeploymentPriority { Low, Normal, High, Critical, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServiceSpec { pub service_type: ServiceType, pub cpu_cores: u32, pub memory_gb: u32, pub storage_gb: u32, pub network_config: NetworkConfig, pub environment_vars: HashMap, pub docker_image: Option, pub kubernetes_manifest: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ServiceType { VM { os: String, version: String }, Container { image: String, ports: Vec }, KubernetesCluster { node_count: u32 }, Storage { storage_type: String }, Application { app_id: String }, } pub struct DeploymentQueueService { queue: Arc>>, workers: Vec, tx: mpsc::UnboundedSender, rx: Arc>>, } impl DeploymentQueueService { pub fn new(worker_count: usize) -> Self { let (tx, rx) = mpsc::unbounded_channel(); let queue = Arc::new(Mutex::new(VecDeque::new())); let mut workers = Vec::new(); for i in 0..worker_count { workers.push(DeploymentWorker::new(i, rx.clone())); } Self { queue, workers, tx, rx: Arc::new(Mutex::new(rx)), } } pub async fn queue_deployment(&self, job: DeploymentJob) -> Result<(), DeploymentError> { // Add to persistent queue self.queue.lock().await.push_back(job.clone()); // Send to worker pool self.tx.send(job).map_err(|_| DeploymentError::QueueFull)?; Ok(()) } pub async fn start_workers(&self) { for worker in &self.workers { worker.start().await; } } } ``` ### 2. ThreeFold Grid Integration ```rust // src/services/threefold_grid.rs use reqwest::Client; use serde_json::json; pub struct ThreeFoldGridService { client: Client, grid_proxy_url: String, tft_wallet: TFTWallet, } impl ThreeFoldGridService { pub async fn deploy_vm( &self, spec: &ServiceSpec, tft_amount: Decimal, ) -> Result { // 1. Find suitable nodes let nodes = self.find_suitable_nodes(spec).await?; let selected_node = self.select_best_node(&nodes, spec)?; // 2. Create deployment contract let contract = self.create_deployment_contract(spec, &selected_node, tft_amount).await?; // 3. Deploy on grid let deployment = self.execute_deployment(contract).await?; // 4. Wait for deployment to be ready self.wait_for_deployment_ready(&deployment.id, Duration::from_secs(300)).await?; // 5. Get connection details let connection_info = self.get_deployment_info(&deployment.id).await?; Ok(GridDeployment { id: deployment.id, node_id: selected_node.id, contract_id: contract.id, connection_info, status: DeploymentStatus::Active, created_at: Utc::now(), }) } async fn find_suitable_nodes(&self, spec: &ServiceSpec) -> Result, GridError> { let query = json!({ "cpu": spec.cpu_cores, "memory": spec.memory_gb * 1024 * 1024 * 1024, // Convert GB to bytes "storage": spec.storage_gb * 1024 * 1024 * 1024, "status": "up", "available": true }); let response = self.client .post(&format!("{}/nodes/search", self.grid_proxy_url)) .json(&query) .send() .await?; let nodes: Vec = response.json().await?; Ok(nodes) } fn select_best_node(&self, nodes: &[GridNode], spec: &ServiceSpec) -> Result { // Node selection algorithm: // 1. Filter by resource availability // 2. Prefer nodes with better uptime // 3. Consider geographic proximity // 4. Balance load across nodes let suitable_nodes: Vec<_> = nodes.iter() .filter(|node| { node.available_cpu >= spec.cpu_cores && node.available_memory >= spec.memory_gb * 1024 * 1024 * 1024 && node.available_storage >= spec.storage_gb * 1024 * 1024 * 1024 && node.uptime_percentage > 95.0 }) .collect(); if suitable_nodes.is_empty() { return Err(GridError::NoSuitableNodes); } // Select node with best score (uptime + available resources) let best_node = suitable_nodes.iter() .max_by(|a, b| { let score_a = a.uptime_percentage + (a.available_cpu as f64 * 10.0); let score_b = b.uptime_percentage + (b.available_cpu as f64 * 10.0); score_a.partial_cmp(&score_b).unwrap() }) .unwrap(); Ok((*best_node).clone()) } async fn create_deployment_contract( &self, spec: &ServiceSpec, node: &GridNode, tft_amount: Decimal, ) -> Result { match &spec.service_type { ServiceType::VM { os, version } => { self.create_vm_contract(spec, node, os, version, tft_amount).await } ServiceType::Container { image, ports } => { self.create_container_contract(spec, node, image, ports, tft_amount).await } ServiceType::KubernetesCluster { node_count } => { self.create_k8s_contract(spec, node, *node_count, tft_amount).await } ServiceType::Storage { storage_type } => { self.create_storage_contract(spec, node, storage_type, tft_amount).await } ServiceType::Application { app_id } => { self.create_app_contract(spec, node, app_id, tft_amount).await } } } async fn create_vm_contract( &self, spec: &ServiceSpec, node: &GridNode, os: &str, version: &str, tft_amount: Decimal, ) -> Result { let vm_config = json!({ "type": "vm", "node_id": node.id, "cpu": spec.cpu_cores, "memory": spec.memory_gb, "storage": spec.storage_gb, "os": os, "version": version, "network": spec.network_config, "environment": spec.environment_vars, "payment": { "amount": tft_amount, "currency": "TFT" } }); let response = self.client .post(&format!("{}/deployments/vm", self.grid_proxy_url)) .json(&vm_config) .send() .await?; let contract: DeploymentContract = response.json().await?; Ok(contract) } } ``` ### 3. TFT Conversion Service ```rust // src/services/tft_conversion.rs use rust_decimal::Decimal; pub struct TFTConversionService { grid_client: ThreeFoldGridService, price_oracle: TFTPriceOracle, } impl TFTConversionService { pub async fn convert_usd_to_tft(&self, usd_amount: Decimal) -> Result { // Get current TFT/USD rate let tft_rate = self.price_oracle.get_tft_usd_rate().await?; let tft_amount = usd_amount / tft_rate; // Add small buffer for price fluctuations (2%) let tft_with_buffer = tft_amount * Decimal::from_str("1.02")?; Ok(TFTConversion { usd_amount, tft_rate, tft_amount: tft_with_buffer, conversion_timestamp: Utc::now(), }) } pub async fn execute_conversion(&self, conversion: &TFTConversion) -> Result { // Execute the actual TFT purchase/conversion // This would integrate with TFT DEX or direct wallet operations let transaction = self.grid_client.tft_wallet.convert_usd_to_tft( conversion.usd_amount, conversion.tft_amount, ).await?; Ok(transaction) } } #[derive(Debug, Clone)] pub struct TFTConversion { pub usd_amount: Decimal, pub tft_rate: Decimal, pub tft_amount: Decimal, pub conversion_timestamp: DateTime, } pub struct TFTPriceOracle { client: Client, } impl TFTPriceOracle { pub async fn get_tft_usd_rate(&self) -> Result { // Get TFT price from multiple sources and average let sources = vec![ self.get_rate_from_dex().await, self.get_rate_from_coingecko().await, self.get_rate_from_grid_stats().await, ]; let valid_rates: Vec = sources.into_iter() .filter_map(|r| r.ok()) .collect(); if valid_rates.is_empty() { return Err(PriceError::NoValidSources); } // Calculate average rate let sum: Decimal = valid_rates.iter().sum(); let average = sum / Decimal::from(valid_rates.len()); Ok(average) } } ``` ### 4. Deployment Worker ```rust // src/services/deployment_worker.rs pub struct DeploymentWorker { id: usize, grid_service: Arc, tft_service: Arc, notification_service: Arc, } impl DeploymentWorker { pub async fn process_deployment(&self, job: DeploymentJob) -> Result<(), DeploymentError> { log::info!("Worker {} processing deployment {}", self.id, job.id); // Update status to "processing" self.update_deployment_status(&job.id, DeploymentStatus::Processing).await?; // 1. Convert USD to TFT let conversion = self.tft_service.convert_usd_to_tft(job.grid_payment_amount).await?; let tft_transaction = self.tft_service.execute_conversion(&conversion).await?; // 2. Deploy on ThreeFold Grid let grid_deployment = self.grid_service.deploy_vm(&job.service_spec, conversion.tft_amount).await?; // 3. Update deployment record self.update_deployment_with_grid_info(&job.id, &grid_deployment, &tft_transaction).await?; // 4. Verify deployment health self.verify_deployment_health(&grid_deployment).await?; // 5. Update status to "active" self.update_deployment_status(&job.id, DeploymentStatus::Active).await?; // 6. Notify user self.notification_service.send_deployment_success(&job.user_id, &job.id).await?; log::info!("Worker {} completed deployment {}", self.id, job.id); Ok(()) } async fn handle_deployment_failure(&self, job: &DeploymentJob, error: &DeploymentError) { log::error!("Deployment {} failed: {:?}", job.id, error); // Update status to failed self.update_deployment_status(&job.id, DeploymentStatus::Failed).await.ok(); // Refund TFC credits to user if let Err(refund_error) = self.refund_tfc_credits(job).await { log::error!("Failed to refund TFC for deployment {}: {:?}", job.id, refund_error); } // Notify user of failure self.notification_service.send_deployment_failure(&job.user_id, &job.id, error).await.ok(); } async fn refund_tfc_credits(&self, job: &DeploymentJob) -> Result<(), RefundError> { // Refund the full TFC amount back to user self.tfc_service.add_tfc_credits( &job.user_id, job.tfc_amount, &format!("Refund for failed deployment {}", job.id), ).await?; Ok(()) } } ``` --- ## Service Management Dashboard ### 1. Real-time Deployment Status ```html
Deployment Status
{{deployment.status}}
Payment Processed
Queued for Deployment
Converting TFC → TFT
Deploying on Grid
Service Active
{{#if deployment.active}}
Connection Details
{{deployment.ip_address}}
ssh root@{{deployment.ip_address}}
{{#if deployment.web_url}}
{{deployment.web_url}}
{{/if}}
{{/if}}
{{deployment.tfc_amount}} TFC
{{deployment.tft_amount}} TFT
{{deployment.node_id}}
``` ### 2. Service Management Controls ```javascript // Service Management Functions class ServiceManager { constructor(deploymentId) { this.deploymentId = deploymentId; this.statusPolling = null; } startStatusPolling() { this.statusPolling = setInterval(async () => { await this.updateDeploymentStatus(); }, 5000); // Poll every 5 seconds } async updateDeploymentStatus() { try { const response = await fetch(`/api/deployments/${this.deploymentId}/status`); const deployment = await response.json(); this.updateStatusDisplay(deployment); // Stop polling if deployment is complete or failed if (deployment.status === 'Active' || deployment.status === 'Failed') { this.stopStatusPolling(); } } catch (error) { console.error('Failed to update deployment status:', error); } } updateStatusDisplay(deployment) { // Update progress timeline document.querySelectorAll('.timeline-step').forEach((step, index) => { const stepStates = ['payment_completed', 'queued', 'converting', 'deploying', 'active']; if (deployment[stepStates[index]]) { step.classList.add('completed'); } }); // Update status badge const statusBadge = document.querySelector('.status-badge'); statusBadge.className = `status-badge status-${deployment.status.toLowerCase()}`; statusBadge.textContent = deployment.status; // Update connection details if active if (deployment.status === 'Active' && deployment.connection_info) { this.showConnectionDetails(deployment.connection_info); } } async restartService() { try { const response = await fetch(`/api/deployments/${this.deploymentId}/restart`, { method: 'POST' }); if (response.ok) { showSuccessToast('Service restart initiated'); this.startStatusPolling(); } else { showErrorToast('Failed to restart service'); } } catch (error) { showErrorToast('Failed to restart service'); } } async stopService() { if (confirm('Are you sure you want to stop this service? This action cannot be undone.')) { try { const response = await fetch(`/api/deployments/${this.deploymentId}/stop`, { method: 'POST' }); if (response.ok) { showSuccessToast('Service stopped successfully'); window.location.href = '/dashboard/deployments'; } else { showErrorToast('Failed to stop service'); } } catch (error) { showErrorToast('Failed to stop service'); } } } } ``` --- ## Configuration & Environment ```toml # config/deployment.toml [deployment_queue] worker_count = 4 max_queue_size = 1000 retry_attempts = 3 retry_delay_seconds = 30 [threefold_grid] grid_proxy_url = "https://gridproxy.grid.tf" substrate_url = "wss://tfchain.grid.tf/ws" relay_url = "wss://relay.grid.tf" [tft_conversion] price_sources = ["dex", "coingecko", "grid_stats"] conversion_buffer_percent = 2.0 max_slippage_percent = 5.0 [notifications] email_enabled = true webhook_enabled = true slack_enabled = false ``` --- This deployment automation specification provides the complete technical foundation for seamless TFC-to-Grid deployment automation, ensuring reliable and scalable service provisioning on the ThreeFold Grid.