Create Cluster Secret
curl --request POST \
  --url https://api.tensorone.ai/v2/clusters/{cluster_id}/secrets \
  --header 'Authorization: <api-key>' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "<string>",
  "value": "<string>",
  "description": "<string>"
}'
{
  "name": "<string>",
  "createdAt": "2023-11-07T05:31:56Z"
}

Overview

Cluster Secrets provide secure storage and management of sensitive information like API keys, database credentials, and certificates. Secrets are encrypted at rest and in transit, and can be securely injected into cluster environments without exposing sensitive data in configuration files.

Endpoints

List Secrets

GET https://api.tensorone.ai/v1/clusters/{cluster_id}/secrets

Get Secret Details

GET https://api.tensorone.ai/v1/clusters/{cluster_id}/secrets/{secret_name}

Create Secret

POST https://api.tensorone.ai/v1/clusters/{cluster_id}/secrets

Update Secret

PUT https://api.tensorone.ai/v1/clusters/{cluster_id}/secrets/{secret_name}

Delete Secret

DELETE https://api.tensorone.ai/v1/clusters/{cluster_id}/secrets/{secret_name}

List Secrets

Query Parameters

ParameterTypeRequiredDescription
typestringNoFilter by secret type: api_key, database, certificate, file, generic
categorystringNoFilter by category: ml_services, databases, cloud_providers, custom
include_valuesbooleanNoInclude decrypted values (requires elevated permissions)
searchstringNoSearch secrets by name or description

Request Examples

# List all secrets for a cluster
curl -X GET "https://api.tensorone.ai/v1/clusters/cluster_abc123/secrets" \
  -H "Authorization: Bearer YOUR_API_KEY"

# List API key secrets only
curl -X GET "https://api.tensorone.ai/v1/clusters/cluster_abc123/secrets?type=api_key" \
  -H "Authorization: Bearer YOUR_API_KEY"

Create Secret

Request Body

ParameterTypeRequiredDescription
namestringYesSecret name (must be unique within cluster)
descriptionstringNoSecret description
typestringYesSecret type
categorystringNoSecret category for organization
valuestringYesSecret value (will be encrypted)
environment_variablestringNoEnvironment variable name to expose secret as
file_pathstringNoFile path to mount secret as (for file-type secrets)
permissionsobjectNoAccess permissions for the secret
expiration_datestringNoOptional expiration date (ISO 8601)
rotation_policyobjectNoAutomatic rotation configuration
tagsarrayNoTags for organization

Request Examples

# Create API key secret
curl -X POST "https://api.tensorone.ai/v1/clusters/cluster_abc123/secrets" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "wandb_api_key",
    "description": "Weights & Biases API key for experiment tracking",
    "type": "api_key",
    "category": "ml_services",
    "value": "your-actual-wandb-api-key-here",
    "environment_variable": "WANDB_API_KEY",
    "tags": ["wandb", "ml-tracking"]
  }'

# Create database credentials
curl -X POST "https://api.tensorone.ai/v1/clusters/cluster_abc123/secrets" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "postgres_credentials",
    "description": "PostgreSQL database credentials",
    "type": "database",
    "category": "databases",
    "value": "{\"username\": \"dbuser\", \"password\": \"secure_password\", \"host\": \"db.example.com\", \"port\": 5432, \"database\": \"ml_data\"}",
    "environment_variable": "DATABASE_URL",
    "expiration_date": "2024-12-31T23:59:59Z"
  }'

# Create SSL certificate file
curl -X POST "https://api.tensorone.ai/v1/clusters/cluster_abc123/secrets" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ssl_certificate",
    "description": "SSL certificate for HTTPS endpoints",
    "type": "certificate",
    "category": "security",
    "value": "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
    "file_path": "/etc/ssl/certs/app.crt",
    "permissions": {
      "read_only": true,
      "file_mode": "0644"
    }
  }'

Get Secret Details

# Get secret metadata (no values)
curl -X GET "https://api.tensorone.ai/v1/clusters/cluster_abc123/secrets/wandb_api_key" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Get secret with decrypted value (requires special permissions)
curl -X GET "https://api.tensorone.ai/v1/clusters/cluster_abc123/secrets/wandb_api_key?include_value=true" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response Schema

{
  "success": true,
  "data": {
    "secrets": [
      {
        "name": "wandb_api_key",
        "description": "Weights & Biases API key for experiment tracking",
        "type": "api_key",
        "category": "ml_services",
        "environment_variable": "WANDB_API_KEY",
        "file_path": null,
        "encrypted": true,
        "expiration_date": null,
        "rotation_policy": {
          "enabled": false
        },
        "permissions": {
          "read_only": true,
          "allowed_users": ["ml_engineer"]
        },
        "usage_statistics": {
          "last_accessed": "2024-01-15T14:30:00Z",
          "access_count": 47
        },
        "tags": ["wandb", "ml-tracking"],
        "created_at": "2024-01-10T09:00:00Z",
        "updated_at": "2024-01-10T09:00:00Z"
      },
      {
        "name": "postgres_credentials",
        "description": "PostgreSQL database credentials",
        "type": "database",
        "category": "databases",
        "environment_variable": "DATABASE_URL",
        "file_path": null,
        "encrypted": true,
        "expiration_date": "2024-12-31T23:59:59Z",
        "rotation_policy": {
          "enabled": true,
          "rotation_interval_days": 90,
          "next_rotation": "2024-04-10T09:00:00Z",
          "notification_days_before": 7
        },
        "permissions": {
          "read_only": false,
          "allowed_users": ["database_admin", "ml_engineer"]
        },
        "usage_statistics": {
          "last_accessed": "2024-01-15T16:45:00Z",
          "access_count": 234
        },
        "tags": ["postgresql", "database"],
        "created_at": "2024-01-10T10:30:00Z",
        "updated_at": "2024-01-12T15:20:00Z"
      }
    ],
    "total_count": 8,
    "secret_types": {
      "api_key": 4,
      "database": 2,
      "certificate": 1,
      "generic": 1
    }
  }
}

Use Cases

ML Training Setup

Set up comprehensive secrets for ML training workflows.
def setup_training_secrets(cluster_id, training_config):
    """Set up all necessary secrets for ML training"""
    
    secrets_setup = {
        "ml_services": [],
        "data_sources": [], 
        "cloud_storage": [],
        "monitoring": []
    }
    
    # ML service API keys
    if training_config.get("use_wandb"):
        wandb_secret = {
            "name": "wandb_api_key",
            "description": "W&B API key for experiment tracking",
            "type": "api_key",
            "category": "ml_services",
            "value": training_config["wandb_key"],
            "environment_variable": "WANDB_API_KEY",
            "tags": ["wandb", "experiment-tracking"]
        }
        result = create_cluster_secret(cluster_id, wandb_secret)
        secrets_setup["ml_services"].append(result)
    
    if training_config.get("use_huggingface"):
        hf_secret = {
            "name": "huggingface_token",
            "description": "HF token for model downloads",
            "type": "api_key",
            "category": "ml_services",
            "value": training_config["hf_token"],
            "environment_variable": "HUGGINGFACE_HUB_TOKEN",
            "tags": ["huggingface", "models"]
        }
        result = create_cluster_secret(cluster_id, hf_secret)
        secrets_setup["ml_services"].append(result)
    
    # Database connections
    if training_config.get("database_config"):
        db_config = training_config["database_config"]
        db_secret = {
            "name": "training_database",
            "description": "Training data database connection",
            "type": "database",
            "category": "databases",
            "value": json.dumps(db_config),
            "environment_variable": "TRAINING_DB_URL",
            "expiration_date": training_config.get("db_expiry"),
            "tags": ["database", "training-data"]
        }
        result = create_cluster_secret(cluster_id, db_secret)
        secrets_setup["data_sources"].append(result)
    
    # Cloud storage credentials
    if training_config.get("s3_config"):
        s3_config = training_config["s3_config"]
        s3_secret = {
            "name": "s3_credentials",
            "description": "S3 credentials for dataset access",
            "type": "cloud_provider",
            "category": "cloud_providers",
            "value": json.dumps(s3_config),
            "environment_variable": "S3_CREDENTIALS",
            "tags": ["aws", "s3", "datasets"]
        }
        result = create_cluster_secret(cluster_id, s3_secret)
        secrets_setup["cloud_storage"].append(result)
    
    # Monitoring and alerting
    if training_config.get("slack_webhook"):
        slack_secret = {
            "name": "slack_webhook",
            "description": "Slack webhook for training notifications",
            "type": "webhook",
            "category": "monitoring",
            "value": training_config["slack_webhook"],
            "environment_variable": "SLACK_WEBHOOK_URL",
            "tags": ["slack", "notifications"]
        }
        result = create_cluster_secret(cluster_id, slack_secret)
        secrets_setup["monitoring"].append(result)
    
    return secrets_setup

# Example training configuration
training_config = {
    "use_wandb": True,
    "wandb_key": "your-wandb-key",
    "use_huggingface": True,
    "hf_token": "your-hf-token",
    "database_config": {
        "host": "training-db.company.com",
        "port": 5432,
        "database": "ml_training",
        "username": "trainer",
        "password": "secure_password"
    },
    "s3_config": {
        "access_key_id": "AKIAI...",
        "secret_access_key": "wJalr...",
        "bucket": "ml-training-data",
        "region": "us-west-2"
    },
    "slack_webhook": "https://hooks.slack.com/services/..."
}

secrets = setup_training_secrets("cluster_training_001", training_config)

Production Deployment Secrets

Manage secrets for production model deployments.
async function setupProductionSecrets(clusterId, deploymentConfig) {
  console.log('Setting up production secrets...');
  
  const secrets = [];
  
  // API authentication secrets
  if (deploymentConfig.apiKeys) {
    for (const [service, apiKey] of Object.entries(deploymentConfig.apiKeys)) {
      const secret = {
        name: `${service}_api_key`,
        description: `API key for ${service} service`,
        type: 'api_key',
        category: 'api_authentication',
        value: apiKey,
        environment_variable: `${service.toUpperCase()}_API_KEY`,
        permissions: {
          read_only: true,
          allowed_roles: ['production_service']
        },
        tags: [service, 'production', 'api']
      };
      
      const result = await createSecret(clusterId, secret);
      secrets.push(result);
    }
  }
  
  // Database credentials with rotation
  if (deploymentConfig.database) {
    const dbSecret = {
      name: 'production_database',
      description: 'Production database credentials',
      type: 'database',
      category: 'databases',
      value: JSON.stringify(deploymentConfig.database),
      environment_variable: 'DATABASE_URL',
      rotation_policy: {
        enabled: true,
        rotation_interval_days: 60,
        notification_days_before: 7
      },
      permissions: {
        read_only: true,
        allowed_roles: ['database_client']
      },
      tags: ['database', 'production', 'rotating']
    };
    
    const result = await createSecret(clusterId, dbSecret);
    secrets.push(result);
  }
  
  // SSL/TLS certificates
  if (deploymentConfig.certificates) {
    for (const [name, certConfig] of Object.entries(deploymentConfig.certificates)) {
      const certSecret = {
        name: `ssl_cert_${name}`,
        description: `SSL certificate for ${name}`,
        type: 'certificate',
        category: 'security',
        value: certConfig.certificate,
        file_path: certConfig.path,
        permissions: {
          read_only: true,
          file_mode: '0644'
        },
        expiration_date: certConfig.expiry,
        tags: ['ssl', 'certificate', 'security']
      };
      
      const result = await createSecret(clusterId, certSecret);
      secrets.push(result);
      
      // Also create private key if provided
      if (certConfig.privateKey) {
        const keySecret = {
          name: `ssl_key_${name}`,
          description: `SSL private key for ${name}`,
          type: 'certificate',
          category: 'security',
          value: certConfig.privateKey,
          file_path: certConfig.keyPath,
          permissions: {
            read_only: true,
            file_mode: '0600'
          },
          tags: ['ssl', 'private-key', 'security']
        };
        
        const keyResult = await createSecret(clusterId, keySecret);
        secrets.push(keyResult);
      }
    }
  }
  
  // Monitoring and alerting credentials
  if (deploymentConfig.monitoring) {
    for (const [service, config] of Object.entries(deploymentConfig.monitoring)) {
      const monitoringSecret = {
        name: `${service}_monitoring`,
        description: `${service} monitoring credentials`,
        type: 'monitoring',
        category: 'monitoring',
        value: JSON.stringify(config),
        environment_variable: `${service.toUpperCase()}_CONFIG`,
        tags: [service, 'monitoring', 'production']
      };
      
      const result = await createSecret(clusterId, monitoringSecret);
      secrets.push(result);
    }
  }
  
  console.log(`Created ${secrets.length} production secrets`);
  return secrets;
}

// Example production deployment configuration
const productionConfig = {
  apiKeys: {
    openai: 'sk-...your-openai-key',
    stripe: 'sk_live_...your-stripe-key',
    sendgrid: 'SG....your-sendgrid-key'
  },
  database: {
    type: 'postgresql',
    host: 'prod-db.company.com',
    port: 5432,
    database: 'production',
    username: 'prod_user',
    password: 'ultra_secure_password',
    ssl: true,
    pool_size: 20
  },
  certificates: {
    api: {
      certificate: '-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----',
      privateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----',
      path: '/etc/ssl/certs/api.crt',
      keyPath: '/etc/ssl/private/api.key',
      expiry: '2024-12-31T23:59:59Z'
    }
  },
  monitoring: {
    datadog: {
      api_key: 'dd_api_key_here',
      app_key: 'dd_app_key_here',
      site: 'datadoghq.com'
    },
    prometheus: {
      endpoint: 'https://prometheus.company.com',
      username: 'prometheus_user',
      password: 'prometheus_password'
    }
  }
};

await setupProductionSecrets('cluster_prod_001', productionConfig);

Secret Rotation Management

Implement automatic secret rotation workflows.
def manage_secret_rotation(cluster_id):
    """Manage automatic secret rotation"""
    
    # Get all secrets with rotation policies
    response = requests.get(
        f"https://api.tensorone.ai/v1/clusters/{cluster_id}/secrets",
        headers={"Authorization": f"Bearer {API_KEY}"}
    )
    
    if not response.json()["success"]:
        return {"error": "Failed to get secrets"}
    
    secrets = response.json()["data"]["secrets"]
    rotation_needed = []
    
    for secret in secrets:
        rotation_policy = secret.get("rotation_policy", {})
        
        if rotation_policy.get("enabled"):
            next_rotation = rotation_policy.get("next_rotation")
            notification_days = rotation_policy.get("notification_days_before", 7)
            
            if next_rotation:
                from datetime import datetime, timedelta
                rotation_date = datetime.fromisoformat(next_rotation.replace('Z', '+00:00'))
                notification_date = rotation_date - timedelta(days=notification_days)
                
                if datetime.now() >= notification_date:
                    rotation_needed.append({
                        "secret_name": secret["name"],
                        "next_rotation": next_rotation,
                        "days_until_rotation": (rotation_date - datetime.now()).days,
                        "type": secret["type"],
                        "category": secret["category"]
                    })
    
    # Process rotation notifications and automatic rotations
    rotation_results = []
    
    for secret_info in rotation_needed:
        days_left = secret_info["days_until_rotation"]
        
        if days_left <= 0:
            # Automatic rotation needed
            result = perform_secret_rotation(cluster_id, secret_info["secret_name"])
            rotation_results.append({
                "secret_name": secret_info["secret_name"],
                "action": "rotated",
                "success": result.get("success", False),
                "details": result
            })
        elif days_left <= 7:
            # Send notification
            result = send_rotation_notification(cluster_id, secret_info)
            rotation_results.append({
                "secret_name": secret_info["secret_name"],
                "action": "notification_sent",
                "days_until_rotation": days_left,
                "success": result.get("success", False)
            })
    
    return {
        "cluster_id": cluster_id,
        "secrets_checked": len(secrets),
        "rotation_needed": len(rotation_needed),
        "actions_taken": len(rotation_results),
        "results": rotation_results
    }

def perform_secret_rotation(cluster_id, secret_name):
    """Perform automatic secret rotation"""
    
    # Get current secret details
    current_secret = get_secret_details(cluster_id, secret_name, include_value=True)
    
    if not current_secret["success"]:
        return {"success": False, "error": "Failed to get current secret"}
    
    secret_data = current_secret["data"]
    secret_type = secret_data["type"]
    
    # Generate new secret value based on type
    new_value = None
    
    if secret_type == "database":
        # For database secrets, generate new password
        import secrets
        import string
        alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
        new_password = ''.join(secrets.choice(alphabet) for _ in range(32))
        
        # Parse existing database config
        import json
        db_config = json.loads(secret_data["value"])
        db_config["password"] = new_password
        new_value = json.dumps(db_config)
        
        # TODO: Update actual database password
        
    elif secret_type == "api_key":
        # For API keys, would need service-specific rotation logic
        return {"success": False, "error": f"Automatic rotation not supported for {secret_type}"}
    
    if new_value:
        # Update secret with new value
        update_payload = {
            "value": new_value,
            "rotation_policy": {
                **secret_data["rotation_policy"],
                "last_rotation": datetime.now().isoformat(),
                "next_rotation": (datetime.now() + timedelta(days=secret_data["rotation_policy"]["rotation_interval_days"])).isoformat()
            }
        }
        
        response = requests.put(
            f"https://api.tensorone.ai/v1/clusters/{cluster_id}/secrets/{secret_name}",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json=update_payload
        )
        
        return response.json()
    
    return {"success": False, "error": "Could not generate new secret value"}

Error Handling

{
  "success": false,
  "error": {
    "code": "SECRET_NOT_FOUND",
    "message": "Secret 'invalid_secret' not found in cluster",
    "details": {
      "cluster_id": "cluster_abc123",
      "secret_name": "invalid_secret",
      "suggestion": "Check secret name or list available secrets"
    }
  }
}

Security Considerations

  • Encryption: All secrets are encrypted at rest using AES-256-GCM
  • Access Control: Implement strict role-based access control for secrets
  • Audit Logging: All secret access is logged for security auditing
  • Rotation: Implement regular secret rotation for sensitive credentials
  • Least Privilege: Grant minimal necessary permissions for secret access
  • Transit Security: Secrets are encrypted in transit using TLS 1.3

Best Practices

  1. Secret Naming: Use descriptive, consistent naming conventions
  2. Regular Rotation: Implement automatic rotation for critical secrets
  3. Access Monitoring: Monitor secret access patterns for anomalies
  4. Environment Isolation: Keep production and development secrets separate
  5. Backup Strategy: Maintain secure backups of critical secrets
  6. Documentation: Document secret purposes and rotation procedures

Authorizations

Authorization
string
header
required

API key authentication. Use 'Bearer YOUR_API_KEY' format.

Path Parameters

cluster_id
string
required

Body

application/json

Response

201 - application/json

Secret created successfully

The response is of type object.