# ClawManager single-node install profile.
# Official validation target: HostPath on one labeled storage node.
# Before installing, label exactly one node:
#   kubectl label node <node> clawmanager.io/storage-node=true --overwrite
# Replace paths or StorageClass names only if you understand the single-node constraints.
apiVersion: v1
kind: Namespace
metadata:
  name: clawmanager-system
---
apiVersion: v1
kind: Secret
metadata:
  name: clawmanager-secrets
  namespace: clawmanager-system
type: Opaque
stringData:
  mysql-root-password: root123
  mysql-password: clawreef123
  jwt-secret: change-me-in-production
  runtime-agent-control-token: change-me-runtime-control-token
  runtime-agent-report-token: change-me-runtime-report-token
  openclaw-gateway-token: change-me-openclaw-gateway-token
  minio-root-user: minioadmin
  minio-root-password: minioadmin@123
  minio-access-key: minioadmin
  minio-secret-key: minioadmin@123
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: clawmanager-mysql-init
  namespace: clawmanager-system
data:
  001_init_schema.sql: |
    CREATE DATABASE IF NOT EXISTS clawmanager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    USE clawmanager;

    CREATE TABLE IF NOT EXISTS users (
      id INT AUTO_INCREMENT PRIMARY KEY,
      username VARCHAR(255) UNIQUE NOT NULL,
      email VARCHAR(320) UNIQUE NOT NULL,
      password_hash VARCHAR(255) NOT NULL,
      role ENUM('admin', 'user') DEFAULT 'user',
      is_active BOOLEAN DEFAULT TRUE,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      last_login TIMESTAMP,
      INDEX idx_username (username),
      INDEX idx_role (role)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS instances (
      id INT AUTO_INCREMENT PRIMARY KEY,
      user_id INT NOT NULL,
      name VARCHAR(255) NOT NULL,
      description TEXT,
      type ENUM('openclaw', 'ubuntu', 'debian', 'centos', 'custom', 'webtop', 'hermes') DEFAULT 'ubuntu',
      runtime_type ENUM('desktop', 'shell') NOT NULL DEFAULT 'desktop',
      status ENUM('creating', 'running', 'stopped', 'error', 'deleting') DEFAULT 'creating',
      cpu_cores DECIMAL(10,2) NOT NULL,
      memory_gb INT NOT NULL,
      disk_gb INT NOT NULL,
      gpu_enabled BOOLEAN DEFAULT FALSE,
      gpu_type VARCHAR(100),
      gpu_count INT DEFAULT 0,
      os_type VARCHAR(50) NOT NULL,
      os_version VARCHAR(50) NOT NULL,
      image_registry VARCHAR(255),
      image_tag VARCHAR(100),
      storage_class VARCHAR(50) DEFAULT 'standard',
      mount_path VARCHAR(255) DEFAULT '/data',
      pod_name VARCHAR(255),
      pod_namespace VARCHAR(255),
      pod_ip VARCHAR(45),
      access_url VARCHAR(500),
      access_token VARCHAR(255),
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      started_at TIMESTAMP,
      stopped_at TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      INDEX idx_user_id (user_id),
      INDEX idx_status (status),
      INDEX idx_type (type)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS persistent_volumes (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      pvc_name VARCHAR(255) UNIQUE NOT NULL,
      pvc_namespace VARCHAR(255) NOT NULL,
      storage_size_gb INT NOT NULL,
      storage_class VARCHAR(50),
      mount_path VARCHAR(255),
      status ENUM('pending', 'bound', 'released', 'failed') DEFAULT 'pending',
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      INDEX idx_instance_id (instance_id),
      UNIQUE KEY uk_pvc_name_namespace (pvc_name, pvc_namespace)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS backups (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      backup_name VARCHAR(255) NOT NULL,
      backup_size_gb INT,
      backup_path VARCHAR(500),
      status ENUM('creating', 'completed', 'failed', 'deleted') DEFAULT 'creating',
      backup_type ENUM('manual', 'scheduled') DEFAULT 'manual',
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      completed_at TIMESTAMP,
      expires_at TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      INDEX idx_instance_id (instance_id),
      INDEX idx_created_at (created_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS backup_schedules (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      schedule_name VARCHAR(255),
      cron_expression VARCHAR(100) NOT NULL,
      retention_days INT DEFAULT 30,
      is_active BOOLEAN DEFAULT TRUE,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      INDEX idx_instance_id (instance_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS user_quotas (
      id INT AUTO_INCREMENT PRIMARY KEY,
      user_id INT NOT NULL UNIQUE,
      max_instances INT DEFAULT 10,
      max_cpu_cores DECIMAL(10,2) DEFAULT 40,
      max_memory_gb INT DEFAULT 100,
      max_storage_gb INT DEFAULT 500,
      max_gpu_count INT DEFAULT 2,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      INDEX idx_user_id (user_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS instance_usage (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      cpu_usage_percent DECIMAL(5,2),
      memory_usage_gb DECIMAL(10,2),
      disk_usage_gb DECIMAL(10,2),
      gpu_usage_percent DECIMAL(5,2),
      uptime_seconds INT,
      recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      INDEX idx_instance_recorded (instance_id, recorded_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS audit_logs (
      id INT AUTO_INCREMENT PRIMARY KEY,
      user_id INT,
      action VARCHAR(100) NOT NULL,
      resource_type VARCHAR(50) NOT NULL,
      resource_id INT,
      details JSON,
      ip_address VARCHAR(45),
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
      INDEX idx_user_id (user_id),
      INDEX idx_action (action),
      INDEX idx_created_at (created_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    INSERT INTO users (username, email, password_hash, role, is_active)
    SELECT 'admin', 'admin@clawmanager.local', '$2a$10$pbenze514mwv3pvQySQBVOsF5J4DBXL2kVo1hLa8JFhQu5x3AKvBi', 'admin', TRUE
    WHERE NOT EXISTS (SELECT 1 FROM users WHERE username = 'admin');

    INSERT INTO user_quotas (user_id, max_instances, max_cpu_cores, max_memory_gb, max_storage_gb, max_gpu_count)
    SELECT id, 100, 200, 1000, 5000, 10 FROM users
    WHERE username = 'admin'
      AND NOT EXISTS (SELECT 1 FROM user_quotas WHERE user_id = users.id);
  002_add_webtop_instance_type.sql: |
    USE clawmanager;
    ALTER TABLE instances
    MODIFY COLUMN type ENUM('openclaw', 'ubuntu', 'debian', 'centos', 'custom', 'webtop', 'hermes') DEFAULT 'ubuntu';
  003_add_system_image_settings.sql: |
    USE clawmanager;
    CREATE TABLE IF NOT EXISTS system_image_settings (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_type VARCHAR(50) NOT NULL,
      runtime_type ENUM('desktop', 'shell') NOT NULL DEFAULT 'desktop',
      display_name VARCHAR(255) NOT NULL,
      image VARCHAR(500) NOT NULL,
      is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      INDEX idx_instance_type (instance_type)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
  004_fix_seeded_admin_password.sql: |
    USE clawmanager;
    UPDATE users
    SET password_hash = '$2a$10$pbenze514mwv3pvQySQBVOsF5J4DBXL2kVo1hLa8JFhQu5x3AKvBi'
    WHERE username = 'admin'
      AND password_hash = '$2a$10$N9qo8uLOickgx2ZMRZoMy.MqrzL9wGC3qD3Q.ZHqQH6t3q7l1L5uG';
  005_update_openclaw_default_image.sql: |
    USE clawmanager;
    UPDATE system_image_settings
    SET image = 'ghcr.io/yuan-lab-llm/agentsruntime/openclaw:latest'
    WHERE instance_type = 'openclaw'
      AND image IN (
        'ericpearlee/openclaw:v2026.3.24',
        'ghcr.io/yuan-lab-llm/clawmanager-openclaw-image/openclaw:latest'
      );
  006_add_openclaw_config_center.sql: |
    USE clawmanager;
    SET @openclaw_snapshot_column_exists = (
      SELECT COUNT(*)
      FROM information_schema.COLUMNS
      WHERE TABLE_SCHEMA = DATABASE()
        AND TABLE_NAME = 'instances'
        AND COLUMN_NAME = 'openclaw_config_snapshot_id'
    );
    SET @openclaw_snapshot_column_sql = IF(
      @openclaw_snapshot_column_exists = 0,
      'ALTER TABLE instances ADD COLUMN openclaw_config_snapshot_id INT NULL AFTER access_token',
      'SELECT 1'
    );
    PREPARE openclaw_snapshot_column_stmt FROM @openclaw_snapshot_column_sql;
    EXECUTE openclaw_snapshot_column_stmt;
    DEALLOCATE PREPARE openclaw_snapshot_column_stmt;

    CREATE TABLE IF NOT EXISTS openclaw_config_resources (
      id INT AUTO_INCREMENT PRIMARY KEY,
      user_id INT NOT NULL,
      resource_type VARCHAR(50) NOT NULL,
      resource_key VARCHAR(100) NOT NULL,
      name VARCHAR(255) NOT NULL,
      description TEXT NULL,
      enabled BOOLEAN NOT NULL DEFAULT TRUE,
      version INT NOT NULL DEFAULT 1,
      tags_json LONGTEXT NOT NULL,
      content_json LONGTEXT NOT NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      UNIQUE KEY uk_openclaw_resource_key (user_id, resource_type, resource_key),
      INDEX idx_openclaw_resource_user_type (user_id, resource_type),
      INDEX idx_openclaw_resource_user_enabled (user_id, enabled)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS openclaw_config_bundles (
      id INT AUTO_INCREMENT PRIMARY KEY,
      user_id INT NOT NULL,
      name VARCHAR(255) NOT NULL,
      description TEXT NULL,
      enabled BOOLEAN NOT NULL DEFAULT TRUE,
      version INT NOT NULL DEFAULT 1,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      INDEX idx_openclaw_bundle_user (user_id),
      INDEX idx_openclaw_bundle_user_enabled (user_id, enabled)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS openclaw_config_bundle_items (
      id INT AUTO_INCREMENT PRIMARY KEY,
      bundle_id INT NOT NULL,
      resource_id INT NOT NULL,
      sort_order INT NOT NULL DEFAULT 0,
      required BOOLEAN NOT NULL DEFAULT TRUE,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (bundle_id) REFERENCES openclaw_config_bundles(id) ON DELETE CASCADE,
      FOREIGN KEY (resource_id) REFERENCES openclaw_config_resources(id) ON DELETE CASCADE,
      UNIQUE KEY uk_openclaw_bundle_resource (bundle_id, resource_id),
      INDEX idx_openclaw_bundle_item_bundle (bundle_id, sort_order)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS openclaw_injection_snapshots (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NULL,
      user_id INT NOT NULL,
      mode VARCHAR(20) NOT NULL,
      bundle_id INT NULL,
      selected_resource_ids_json LONGTEXT NOT NULL,
      resolved_resources_json LONGTEXT NOT NULL,
      rendered_manifest_json LONGTEXT NOT NULL,
      rendered_env_json LONGTEXT NOT NULL,
      secret_name VARCHAR(255) NULL,
      status VARCHAR(30) NOT NULL DEFAULT 'pending',
      error_message TEXT NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      activated_at TIMESTAMP NULL,
      INDEX idx_openclaw_snapshot_user_created (user_id, created_at),
      INDEX idx_openclaw_snapshot_instance (instance_id),
      INDEX idx_openclaw_snapshot_bundle (bundle_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
  007_add_instance_agent_control_plane.sql: |
    USE clawmanager;
    SET @instance_agent_bootstrap_token_column_exists = (
      SELECT COUNT(*)
      FROM information_schema.COLUMNS
      WHERE TABLE_SCHEMA = DATABASE()
        AND TABLE_NAME = 'instances'
        AND COLUMN_NAME = 'agent_bootstrap_token'
    );
    SET @instance_agent_bootstrap_token_column_sql = IF(
      @instance_agent_bootstrap_token_column_exists = 0,
      'ALTER TABLE instances ADD COLUMN agent_bootstrap_token VARCHAR(255) NULL AFTER access_token',
      'SELECT 1'
    );
    PREPARE instance_agent_bootstrap_token_column_stmt FROM @instance_agent_bootstrap_token_column_sql;
    EXECUTE instance_agent_bootstrap_token_column_stmt;
    DEALLOCATE PREPARE instance_agent_bootstrap_token_column_stmt;

    CREATE TABLE IF NOT EXISTS instance_agents (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      agent_id VARCHAR(255) NOT NULL,
      agent_version VARCHAR(50) NOT NULL,
      protocol_version VARCHAR(50) NOT NULL,
      status VARCHAR(30) NOT NULL DEFAULT 'online',
      capabilities_json LONGTEXT NOT NULL,
      host_info_json LONGTEXT NULL,
      session_token VARCHAR(255) NULL,
      session_expires_at TIMESTAMP NULL,
      last_heartbeat_at TIMESTAMP NULL,
      last_reported_at TIMESTAMP NULL,
      last_seen_ip VARCHAR(45) NULL,
      registered_at TIMESTAMP NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      UNIQUE KEY uk_instance_agents_instance (instance_id),
      UNIQUE KEY uk_instance_agents_session_token (session_token),
      INDEX idx_instance_agents_agent_id (agent_id),
      INDEX idx_instance_agents_status (status),
      INDEX idx_instance_agents_last_heartbeat (last_heartbeat_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS instance_runtime_status (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      infra_status VARCHAR(30) NOT NULL DEFAULT 'creating',
      agent_status VARCHAR(30) NOT NULL DEFAULT 'offline',
      openclaw_status VARCHAR(30) NOT NULL DEFAULT 'unknown',
      openclaw_pid INT NULL,
      openclaw_version VARCHAR(100) NULL,
      current_config_revision_id INT NULL,
      desired_config_revision_id INT NULL,
      summary_json LONGTEXT NULL,
      system_info_json LONGTEXT NULL,
      health_json LONGTEXT NULL,
      last_reported_at TIMESTAMP NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      UNIQUE KEY uk_instance_runtime_status_instance (instance_id),
      INDEX idx_instance_runtime_status_agent_status (agent_status),
      INDEX idx_instance_runtime_status_openclaw_status (openclaw_status)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS instance_desired_state (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      desired_power_state VARCHAR(30) NOT NULL DEFAULT 'running',
      desired_config_revision_id INT NULL,
      desired_runtime_action VARCHAR(50) NULL,
      updated_by INT NULL,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL,
      UNIQUE KEY uk_instance_desired_state_instance (instance_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS instance_commands (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      agent_id VARCHAR(255) NULL,
      command_type VARCHAR(50) NOT NULL,
      payload_json LONGTEXT NULL,
      status VARCHAR(30) NOT NULL DEFAULT 'pending',
      idempotency_key VARCHAR(255) NOT NULL,
      issued_by INT NULL,
      issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      dispatched_at TIMESTAMP NULL,
      started_at TIMESTAMP NULL,
      finished_at TIMESTAMP NULL,
      timeout_seconds INT NOT NULL DEFAULT 300,
      result_json LONGTEXT NULL,
      error_message TEXT NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      FOREIGN KEY (issued_by) REFERENCES users(id) ON DELETE SET NULL,
      UNIQUE KEY uk_instance_commands_idempotency (instance_id, idempotency_key),
      INDEX idx_instance_commands_instance_status (instance_id, status),
      INDEX idx_instance_commands_agent_status (agent_id, status),
      INDEX idx_instance_commands_issued_at (issued_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS instance_config_revisions (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      source_snapshot_id INT NULL,
      source_bundle_id INT NULL,
      revision_no INT NOT NULL,
      content_json LONGTEXT NOT NULL,
      checksum VARCHAR(255) NOT NULL,
      status VARCHAR(30) NOT NULL DEFAULT 'published',
      published_by INT NULL,
      published_at TIMESTAMP NULL,
      activated_at TIMESTAMP NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      FOREIGN KEY (published_by) REFERENCES users(id) ON DELETE SET NULL,
      UNIQUE KEY uk_instance_config_revision_unique (instance_id, revision_no),
      INDEX idx_instance_config_revision_instance (instance_id, revision_no),
      INDEX idx_instance_config_revision_status (status)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
  008_add_skill_management.sql: |
    USE clawmanager;
    CREATE TABLE IF NOT EXISTS skill_blobs (
      id INT AUTO_INCREMENT PRIMARY KEY,
      content_hash VARCHAR(128) NOT NULL,
      archive_hash VARCHAR(128) NOT NULL,
      object_key VARCHAR(512) NOT NULL,
      file_name VARCHAR(255) NOT NULL,
      media_type VARCHAR(100) NOT NULL DEFAULT 'application/gzip',
      size_bytes BIGINT NOT NULL DEFAULT 0,
      scan_status VARCHAR(30) NOT NULL DEFAULT 'pending',
      risk_level VARCHAR(30) NOT NULL DEFAULT 'unknown',
      last_scanned_at TIMESTAMP NULL,
      last_scan_result_id INT NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      UNIQUE KEY uk_skill_blobs_content_hash (content_hash),
      INDEX idx_skill_blobs_scan_status (scan_status),
      INDEX idx_skill_blobs_risk_level (risk_level)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS skills (
      id INT AUTO_INCREMENT PRIMARY KEY,
      user_id INT NOT NULL,
      skill_key VARCHAR(120) NOT NULL,
      name VARCHAR(255) NOT NULL,
      description TEXT NULL,
      current_version_id INT NULL,
      source_type VARCHAR(30) NOT NULL DEFAULT 'uploaded',
      status VARCHAR(30) NOT NULL DEFAULT 'active',
      risk_level VARCHAR(30) NOT NULL DEFAULT 'unknown',
      last_scanned_at TIMESTAMP NULL,
      last_scan_result_id INT NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      UNIQUE KEY uk_skills_user_key (user_id, skill_key),
      INDEX idx_skills_user_status (user_id, status),
      INDEX idx_skills_risk_level (risk_level)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS skill_versions (
      id INT AUTO_INCREMENT PRIMARY KEY,
      skill_id INT NOT NULL,
      blob_id INT NOT NULL,
      version_no INT NOT NULL,
      manifest_json LONGTEXT NULL,
      source_type VARCHAR(30) NOT NULL DEFAULT 'uploaded',
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE,
      FOREIGN KEY (blob_id) REFERENCES skill_blobs(id) ON DELETE RESTRICT,
      UNIQUE KEY uk_skill_versions_skill_version (skill_id, version_no),
      UNIQUE KEY uk_skill_versions_skill_blob (skill_id, blob_id),
      INDEX idx_skill_versions_skill_id (skill_id, version_no)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS instance_skills (
      id INT AUTO_INCREMENT PRIMARY KEY,
      instance_id INT NOT NULL,
      skill_id INT NOT NULL,
      skill_version_id INT NULL,
      source_type VARCHAR(40) NOT NULL DEFAULT 'discovered_in_instance',
      install_path VARCHAR(1024) NULL,
      observed_hash VARCHAR(128) NULL,
      status VARCHAR(30) NOT NULL DEFAULT 'active',
      last_seen_at TIMESTAMP NULL,
      removed_at TIMESTAMP NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE,
      FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE,
      FOREIGN KEY (skill_version_id) REFERENCES skill_versions(id) ON DELETE SET NULL,
      UNIQUE KEY uk_instance_skills_instance_skill (instance_id, skill_id),
      INDEX idx_instance_skills_instance (instance_id, status),
      INDEX idx_instance_skills_skill (skill_id, status)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    CREATE TABLE IF NOT EXISTS skill_scan_results (
      id INT AUTO_INCREMENT PRIMARY KEY,
      blob_id INT NOT NULL,
      engine VARCHAR(60) NOT NULL,
      risk_level VARCHAR(30) NOT NULL DEFAULT 'unknown',
      status VARCHAR(30) NOT NULL DEFAULT 'completed',
      summary TEXT NULL,
      findings_json LONGTEXT NULL,
      scanned_at TIMESTAMP NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      FOREIGN KEY (blob_id) REFERENCES skill_blobs(id) ON DELETE CASCADE,
      INDEX idx_skill_scan_results_blob (blob_id, scanned_at),
      INDEX idx_skill_scan_results_risk (risk_level, scanned_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
  009_cpu_cores_decimal.sql: |
    USE clawmanager;
    ALTER TABLE instances MODIFY COLUMN cpu_cores DECIMAL(10,2) NOT NULL;
    ALTER TABLE user_quotas MODIFY COLUMN max_cpu_cores DECIMAL(10,2) DEFAULT 40;
  011_add_hermes_instance_type.sql: |
    USE clawmanager;
    ALTER TABLE instances
    MODIFY COLUMN type ENUM('openclaw', 'ubuntu', 'debian', 'centos', 'custom', 'webtop', 'hermes') DEFAULT 'ubuntu';
  012_update_agents_runtime_default_images.sql: |
    USE clawmanager;
    UPDATE system_image_settings
    SET image = 'ghcr.io/yuan-lab-llm/agentsruntime/openclaw:latest'
    WHERE instance_type = 'openclaw'
      AND image IN (
        'ericpearlee/openclaw:v2026.3.24',
        'ghcr.io/yuan-lab-llm/clawmanager-openclaw-image/openclaw:latest'
      );

    UPDATE system_image_settings
    SET image = 'ghcr.io/yuan-lab-llm/agentsruntime/hermes:latest'
    WHERE instance_type = 'hermes'
      AND image IN (
        'registry.example.com/hermes-webtop:latest',
        'lscr.io/linuxserver/webtop:ubuntu-xfce'
      );
  013_normalize_agents_runtime_image_names.sql: |
    USE clawmanager;
    UPDATE system_image_settings
    SET image = 'ghcr.io/yuan-lab-llm/agentsruntime/openclaw:latest'
    WHERE image = 'ghcr.io/Yuan-lab-LLM/AgentsRuntime/openclaw:latest';

    UPDATE system_image_settings
    SET image = 'ghcr.io/yuan-lab-llm/agentsruntime/hermes:latest'
    WHERE image = 'ghcr.io/Yuan-lab-LLM/AgentsRuntime/hermes:latest';
  019_add_instance_runtime_type.sql: |
    USE clawmanager;
    SET @instance_runtime_type_column_exists = (
      SELECT COUNT(*)
      FROM information_schema.COLUMNS
      WHERE TABLE_SCHEMA = DATABASE()
        AND TABLE_NAME = 'instances'
        AND COLUMN_NAME = 'runtime_type'
    );
    SET @instance_runtime_type_column_sql = IF(
      @instance_runtime_type_column_exists = 0,
      'ALTER TABLE instances ADD COLUMN runtime_type ENUM(''desktop'', ''shell'') NOT NULL DEFAULT ''desktop'' AFTER type',
      'SELECT 1'
    );
    PREPARE instance_runtime_type_column_stmt FROM @instance_runtime_type_column_sql;
    EXECUTE instance_runtime_type_column_stmt;
    DEALLOCATE PREPARE instance_runtime_type_column_stmt;
  020_add_system_image_runtime_type.sql: |
    USE clawmanager;
    SET @system_image_runtime_type_column_exists = (
      SELECT COUNT(*)
      FROM information_schema.COLUMNS
      WHERE TABLE_SCHEMA = DATABASE()
        AND TABLE_NAME = 'system_image_settings'
        AND COLUMN_NAME = 'runtime_type'
    );
    SET @system_image_runtime_type_column_sql = IF(
      @system_image_runtime_type_column_exists = 0,
      'ALTER TABLE system_image_settings ADD COLUMN runtime_type ENUM(''desktop'', ''shell'') NOT NULL DEFAULT ''desktop'' AFTER instance_type',
      'SELECT 1'
    );
    PREPARE system_image_runtime_type_column_stmt FROM @system_image_runtime_type_column_sql;
    EXECUTE system_image_runtime_type_column_stmt;
    DEALLOCATE PREPARE system_image_runtime_type_column_stmt;
  021_add_openclaw_shell_runtime_image.sql: |
    USE clawmanager;
    SET @system_image_is_enabled_column_exists = (
      SELECT COUNT(*)
      FROM information_schema.COLUMNS
      WHERE TABLE_SCHEMA = DATABASE()
        AND TABLE_NAME = 'system_image_settings'
        AND COLUMN_NAME = 'is_enabled'
    );
    SET @system_image_is_enabled_column_sql = IF(
      @system_image_is_enabled_column_exists = 0,
      'ALTER TABLE system_image_settings ADD COLUMN is_enabled BOOLEAN NOT NULL DEFAULT TRUE',
      'SELECT 1'
    );
    PREPARE system_image_is_enabled_column_stmt FROM @system_image_is_enabled_column_sql;
    EXECUTE system_image_is_enabled_column_stmt;
    DEALLOCATE PREPARE system_image_is_enabled_column_stmt;

    SET @system_image_unique_instance_type_index_name = (
      SELECT INDEX_NAME
      FROM information_schema.STATISTICS
      WHERE TABLE_SCHEMA = DATABASE()
        AND TABLE_NAME = 'system_image_settings'
        AND COLUMN_NAME = 'instance_type'
        AND NON_UNIQUE = 0
        AND INDEX_NAME <> 'PRIMARY'
      LIMIT 1
    );
    SET @system_image_unique_instance_type_index_sql = IF(
      @system_image_unique_instance_type_index_name IS NOT NULL,
      CONCAT(
        'ALTER TABLE system_image_settings DROP INDEX `',
        REPLACE(@system_image_unique_instance_type_index_name, '`', '``'),
        '`'
      ),
      'SELECT 1'
    );
    PREPARE system_image_unique_instance_type_index_stmt FROM @system_image_unique_instance_type_index_sql;
    EXECUTE system_image_unique_instance_type_index_stmt;
    DEALLOCATE PREPARE system_image_unique_instance_type_index_stmt;

    INSERT INTO system_image_settings (instance_type, runtime_type, display_name, image, is_enabled)
    SELECT
      'openclaw',
      'desktop',
      'OpenClaw Desktop',
      'ghcr.io/yuan-lab-llm/agentsruntime/openclaw:latest',
      TRUE
    WHERE NOT EXISTS (
      SELECT 1
      FROM system_image_settings
      WHERE instance_type = 'openclaw'
        AND runtime_type = 'desktop'
    )
    AND NOT EXISTS (
      SELECT 1
      FROM system_image_settings
      WHERE instance_type = 'openclaw'
        AND is_enabled = FALSE
    );

    INSERT INTO system_image_settings (instance_type, runtime_type, display_name, image, is_enabled)
    SELECT
      'openclaw',
      'shell',
      'OpenClaw Lite',
      'ghcr.io/yuan-lab-llm/agentsruntime/openclaw-lite:latest',
      TRUE
    WHERE NOT EXISTS (
      SELECT 1
      FROM system_image_settings
      WHERE instance_type = 'openclaw'
        AND runtime_type = 'shell'
    )
    AND NOT EXISTS (
      SELECT 1
      FROM system_image_settings
      WHERE instance_type = 'openclaw'
        AND is_enabled = FALSE
    );
  022_add_openclaw_config_bundle_skills.sql: |
    USE clawmanager;
    CREATE TABLE IF NOT EXISTS openclaw_config_bundle_skills (
      id INT AUTO_INCREMENT PRIMARY KEY,
      bundle_id INT NOT NULL,
      skill_id INT NOT NULL,
      sort_order INT NOT NULL DEFAULT 0,
      required BOOLEAN NOT NULL DEFAULT TRUE,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (bundle_id) REFERENCES openclaw_config_bundles(id) ON DELETE CASCADE,
      FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE,
      UNIQUE KEY uk_openclaw_bundle_skill (bundle_id, skill_id),
      INDEX idx_openclaw_bundle_skill_bundle (bundle_id, sort_order)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
  023_normalize_runtime_config_mounts.sql: |
    USE clawmanager;
    UPDATE instances
    SET mount_path = '/config',
        updated_at = CURRENT_TIMESTAMP
    WHERE type IN ('openclaw', 'ubuntu', 'webtop', 'hermes')
      AND mount_path IN ('/data', '/home/user/data', '/config/.hermes');
  031_add_hermes_lite_runtime_image.sql: |
    USE clawmanager;
    UPDATE system_image_settings
    SET
      image = 'ghcr.io/yuan-lab-llm/agentsruntime/hermes-lite:latest',
      display_name = 'Hermes Lite',
      is_enabled = TRUE
    WHERE instance_type = 'hermes'
      AND runtime_type = 'shell'
      AND image IN (
        'ghcr.io/yuan-lab-llm/agentsruntime/hermes-shell:latest',
        'registry.example.com/your-custom-shell-image:latest'
      );

    INSERT INTO system_image_settings (instance_type, runtime_type, display_name, image, is_enabled)
    SELECT
      'hermes',
      'shell',
      'Hermes Lite',
      'ghcr.io/yuan-lab-llm/agentsruntime/hermes-lite:latest',
      TRUE
    WHERE NOT EXISTS (
      SELECT 1
      FROM system_image_settings
      WHERE instance_type = 'hermes'
        AND runtime_type = 'shell'
    )
    AND NOT EXISTS (
      SELECT 1
      FROM system_image_settings
      WHERE instance_type = 'hermes'
        AND is_enabled = FALSE
    );
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: clawmanager-mysql-pv
  labels:
    app: clawmanager
    managed-by: clawmanager
    clawmanager.io/storage-profile: single-node
    clawmanager.io/storage-node: "true"
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: clawmanager.io/storage-node
              operator: In
              values:
                - "true"
  hostPath:
    path: /data/clawmanager/system/mysql
    type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data
  namespace: clawmanager-system
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: manual
  volumeName: clawmanager-mysql-pv
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: clawmanager-minio-pv
  labels:
    app: clawmanager
    managed-by: clawmanager
    clawmanager.io/storage-profile: single-node
    clawmanager.io/storage-node: "true"
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: clawmanager.io/storage-node
              operator: In
              values:
                - "true"
  hostPath:
    path: /data/clawmanager/system/minio
    type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: minio-data
  namespace: clawmanager-system
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: manual
  volumeName: clawmanager-minio-pv
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: clawmanager-redis-pv
  labels:
    app: clawmanager
    managed-by: clawmanager
    clawmanager.io/storage-profile: single-node
    clawmanager.io/storage-node: "true"
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: clawmanager.io/storage-node
              operator: In
              values:
                - "true"
  hostPath:
    path: /data/clawmanager/system/redis
    type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-data
  namespace: clawmanager-system
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: manual
  volumeName: clawmanager-redis-pv
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: clawmanager-workspaces-pv
  labels:
    app: clawmanager
    managed-by: clawmanager
    clawmanager.io/storage-profile: single-node
    clawmanager.io/storage-node: "true"
spec:
  capacity:
    storage: 50Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: clawmanager.io/storage-node
              operator: In
              values:
                - "true"
  hostPath:
    path: /data/clawmanager/system/workspaces
    type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: clawmanager-workspaces
  namespace: clawmanager-system
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 50Gi
  storageClassName: manual
  volumeName: clawmanager-workspaces-pv
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: clawmanager-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          #image: mysql:8.4.8
          image: mysql:8.4.8
          imagePullPolicy: IfNotPresent
          args:
            - "--innodb-use-native-aio=0"
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: mysql-root-password
            - name: MYSQL_DATABASE
              value: clawmanager
            - name: MYSQL_USER
              value: clawmanager
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: mysql-password
          volumeMounts:
            - name: mysql-data
              mountPath: /var/lib/mysql
            - name: mysql-init
              mountPath: /docker-entrypoint-initdb.d
          readinessProbe:
            exec:
              command: ["sh", "-c", "mysqladmin ping -h 127.0.0.1 -uroot -p$MYSQL_ROOT_PASSWORD"]
            initialDelaySeconds: 20
            periodSeconds: 10
      volumes:
        - name: mysql-data
          persistentVolumeClaim:
            claimName: mysql-data
        - name: mysql-init
          configMap:
            name: clawmanager-mysql-init
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: clawmanager-system
spec:
  selector:
    app: mysql
  ports:
    - name: mysql
      port: 3306
      targetPort: 3306
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: minio
  namespace: clawmanager-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: minio
  template:
    metadata:
      labels:
        app: minio
    spec:
      containers:
        - name: minio
          image: minio/minio:latest
          imagePullPolicy: IfNotPresent
          args:
            - server
            - /data
            - --console-address
            - ":9001"
          env:
            - name: MINIO_ROOT_USER
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: minio-root-user
            - name: MINIO_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: minio-root-password
          ports:
            - name: api
              containerPort: 9000
            - name: console
              containerPort: 9001
          volumeMounts:
            - name: minio-data
              mountPath: /data
          readinessProbe:
            tcpSocket:
              port: api
            initialDelaySeconds: 10
            periodSeconds: 10
      volumes:
        - name: minio-data
          persistentVolumeClaim:
            claimName: minio-data
---
apiVersion: v1
kind: Service
metadata:
  name: minio
  namespace: clawmanager-system
spec:
  selector:
    app: minio
  ports:
    - name: api
      port: 9000
      targetPort: api
    - name: console
      port: 9001
      targetPort: console
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: clawmanager-team-redis
  namespace: clawmanager-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: clawmanager-team-redis
  template:
    metadata:
      labels:
        app: clawmanager-team-redis
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          imagePullPolicy: IfNotPresent
          args:
            - redis-server
            - --appendonly
            - "yes"
          ports:
            - name: redis
              containerPort: 6379
          readinessProbe:
            tcpSocket:
              port: redis
            initialDelaySeconds: 5
            periodSeconds: 10
          volumeMounts:
            - name: redis-data
              mountPath: /data
      volumes:
        - name: redis-data
          persistentVolumeClaim:
            claimName: redis-data
---
apiVersion: v1
kind: Service
metadata:
  name: clawmanager-redis
  namespace: clawmanager-system
spec:
  selector:
    app: clawmanager-team-redis
  ports:
    - name: redis
      port: 6379
      targetPort: redis
---
apiVersion: v1
kind: Service
metadata:
  name: clawmanager-team-redis
  namespace: clawmanager-system
spec:
  selector:
    app: clawmanager-team-redis
  ports:
    - name: redis
      port: 6379
      targetPort: redis
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: clawmanager-app
  namespace: clawmanager-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: clawmanager-runtime-manager
rules:
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs:
      - get
      - create
      - update
  - apiGroups:
      - apps
    resources:
      - deployments
      - deployments/scale
    verbs:
      - get
      - list
      - watch
      - create
      - update
      - patch
  - apiGroups:
      - ""
    resources:
      - pods
    verbs:
      - get
      - list
      - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: clawmanager-runtime-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: clawmanager-runtime-manager
subjects:
  - kind: ServiceAccount
    name: clawmanager-app
    namespace: clawmanager-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: clawmanager-app-cluster-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: clawmanager-app
    namespace: clawmanager-system
---
---
# Explicit lease permissions for control-plane leader election. The
# cluster-admin binding above already covers this; this Role documents the
# requirement and keeps leader election working if RBAC is tightened later.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: clawmanager-app-leaderelection
  namespace: clawmanager-system
rules:
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    verbs: ["get", "list", "watch", "create", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: clawmanager-app-leaderelection
  namespace: clawmanager-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: clawmanager-app-leaderelection
subjects:
  - kind: ServiceAccount
    name: clawmanager-app
    namespace: clawmanager-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: skill-scanner
  namespace: clawmanager-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: skill-scanner
  template:
    metadata:
      labels:
        app: skill-scanner
    spec:
      containers:
        - name: skill-scanner
          image: ghcr.io/yuan-lab-llm/skill-scanner:latest
          imagePullPolicy: IfNotPresent
          command:
            - /opt/skill-scanner-venv/bin/skill-scanner-api
            - --host
            - 0.0.0.0
            - --port
            - "8000"
          env:
            - name: SKILL_SCANNER_LLM_API_KEY
              value: ""
            - name: SKILL_SCANNER_LLM_MODEL
              value: ""
            - name: SKILL_SCANNER_LLM_BASE_URL
              value: ""
            - name: SKILL_SCANNER_META_LLM_API_KEY
              value: ""
            - name: SKILL_SCANNER_META_LLM_MODEL
              value: ""
            - name: SKILL_SCANNER_META_LLM_BASE_URL
              value: ""
          ports:
            - name: http
              containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
  name: skill-scanner
  namespace: clawmanager-system
spec:
  selector:
    app: skill-scanner
  ports:
    - name: http
      port: 8000
      targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: clawmanager-app
  namespace: clawmanager-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: clawmanager-app
  template:
    metadata:
      labels:
        app: clawmanager-app
    spec:
      serviceAccountName: clawmanager-app
      containers:
        - name: clawmanager-app
          image: ghcr.io/yuan-lab-llm/clawmanager:latest
          imagePullPolicy: IfNotPresent
          ports:
            - name: https
              containerPort: 8443
            - name: proxy
              containerPort: 9001
          env:
            - name: SERVER_ADDRESS
              value: ":9001"
            - name: SERVER_MODE
              value: "release"
            - name: DB_HOST
              value: "mysql"
            - name: DB_PORT
              value: "3306"
            - name: DB_USER
              value: "clawmanager"
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: mysql-password
            - name: DB_NAME
              value: "clawmanager"
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: jwt-secret
            - name: OBJECT_STORAGE_ENDPOINT
              value: "minio.clawmanager-system.svc.cluster.local:9000"
            - name: OBJECT_STORAGE_REGION
              value: ""
            - name: OBJECT_STORAGE_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: minio-access-key
            - name: OBJECT_STORAGE_SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: minio-secret-key
            - name: OBJECT_STORAGE_BUCKET
              value: "clawmanager-skills"
            - name: OBJECT_STORAGE_USE_SSL
              value: "false"
            - name: OBJECT_STORAGE_BASE_PATH
              value: "skills"
            - name: OBJECT_STORAGE_FORCE_PATH_STYLE
              value: "true"
            - name: K8S_MODE
              value: "incluster"
            - name: K8S_NAMESPACE
              value: "clawmanager"
            - name: K8S_STORAGE_CLASS
              value: "manual"
            - name: CLAWMANAGER_STORAGE_PROFILE
              value: "single-node"
            - name: K8S_HOSTPATH_FALLBACK_ENABLED
              value: "true"
            - name: K8S_PVC_BIND_TIMEOUT
              value: "2m"
            - name: K8S_CONTROL_PLANE_STORAGE_CLASS
              value: "manual"
            - name: K8S_INSTANCE_STORAGE_CLASS
              value: "manual"
            - name: K8S_WORKSPACE_STORAGE_CLASS
              value: "manual"
            - name: K8S_WORKSPACE_ACCESS_MODE
              value: "ReadWriteMany"
            - name: K8S_PV_HOST_PATH_PREFIX
              value: "/data/clawreef"
            - name: CLAWMANAGER_WORKSPACE_ARCHIVE_MAX_MIB
              value: "500"
            - name: PLATFORM_REDIS_URL
              value: "redis://clawmanager-redis.clawmanager-system.svc.cluster.local:6379/0"
            - name: TEAM_REDIS_URL
              value: "redis://clawmanager-redis.clawmanager-system.svc.cluster.local:6379/0"
            - name: CLAWMANAGER_DESKTOP_DIRECT_PROXY
              value: "true"
            # Leader election for control-plane singleton background loops.
            # Required before scaling replicas > 1 so SyncService / Team event
            # consumers / stale-task monitor run on exactly one replica.
            - name: CLAWMANAGER_LEADER_ELECTION
              value: "true"
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: CLAWMANAGER_TEAM_REDIS_URL
              value: "redis://clawmanager-redis.clawmanager-system.svc.cluster.local:6379/0"
            - name: CLAWMANAGER_TEAM_MANAGER_BASE_URL
              value: "http://clawmanager-gateway.clawmanager-system.svc.cluster.local:9001"
            - name: RUNTIME_NAMESPACE
              value: "clawmanager-system"
            - name: RUNTIME_WORKSPACE_ROOT
              value: "/workspaces"
            - name: RUNTIME_WORKSPACE_PVC_CLAIM
              value: "clawmanager-workspaces"
            - name: RUNTIME_MAX_GATEWAYS_PER_POD
              value: "100"
            - name: RUNTIME_GATEWAY_PORT_START
              value: "20000"
            - name: RUNTIME_GATEWAY_PORT_END
              value: "20299"
            - name: RUNTIME_SCHEDULER_ENABLED
              value: "true"
            - name: RUNTIME_HEARTBEAT_TIMEOUT
              value: "10s"
            - name: RUNTIME_AGENT_CONTROL_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-control-token
            - name: RUNTIME_AGENT_REPORT_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-report-token
            - name: OPENCLAW_GATEWAY_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: openclaw-gateway-token
            - name: SKILL_SCANNER_ENABLED
              value: "true"
            - name: SKILL_SCANNER_BASE_URL
              value: "http://skill-scanner.clawmanager-system.svc.cluster.local:8000"
            - name: SKILL_SCANNER_TIMEOUT_SECONDS
              value: "120"
            - name: SKILL_SCANNER_NAMESPACE
              value: "clawmanager-system"
            - name: SKILL_SCANNER_DEPLOYMENT
              value: "skill-scanner"
          volumeMounts:
            - name: tls-cert
              mountPath: /var/run/clawreef-tls
              readOnly: true
            - name: workspaces
              mountPath: /workspaces
          readinessProbe:
            httpGet:
              scheme: HTTPS
              path: /healthz
              port: https
            initialDelaySeconds: 10
            periodSeconds: 10
          livenessProbe:
            httpGet:
              scheme: HTTPS
              path: /healthz
              port: https
            initialDelaySeconds: 30
            periodSeconds: 20
      volumes:
        - name: tls-cert
          secret:
            secretName: clawmanager-tls
            optional: true
        - name: workspaces
          persistentVolumeClaim:
            claimName: clawmanager-workspaces
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: openclaw-runtime
  namespace: clawmanager-system
  labels:
    app: openclaw-runtime
    clawmanager.io/runtime-type: openclaw
spec:
  replicas: 1
  selector:
    matchLabels:
      app: openclaw-runtime
      clawmanager.io/runtime-type: openclaw
  template:
    metadata:
      labels:
        app: openclaw-runtime
        clawmanager.io/runtime-type: openclaw
    spec:
      containers:
        - name: runtime
          image: ghcr.io/yuan-lab-llm/agentsruntime/openclaw-lite:latest
          imagePullPolicy: Always
          env:
            - name: CLAWMANAGER_RUNTIME_TYPE
              value: openclaw
            - name: CLAWMANAGER_BACKEND_URL
              value: "http://clawmanager-gateway.clawmanager-system.svc.cluster.local:9001"
            - name: CLAWMANAGER_RUNTIME_DEPLOYMENT_NAME
              value: openclaw-runtime
            - name: CLAWMANAGER_RUNTIME_IMAGE_REF
              value: ghcr.io/yuan-lab-llm/agentsruntime/openclaw-lite:latest
            - name: RUNTIME_WORKSPACE_ROOT
              value: /workspaces
            - name: RUNTIME_GATEWAY_PORT_START
              value: "20000"
            - name: RUNTIME_GATEWAY_PORT_END
              value: "20299"
            - name: RUNTIME_AGENT_LISTEN_ADDR
              value: "0.0.0.0:19090"
            - name: RUNTIME_AGENT_PUBLIC_PORT
              value: "19090"
            - name: RUNTIME_GATEWAY_COMMAND
              value: "/usr/local/bin/openclaw gateway run --allow-unconfigured --auth token --bind lan --force"
            - name: OPENCLAW_GATEWAY_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: openclaw-gateway-token
            - name: RUNTIME_AGENT_CONTROL_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-control-token
            - name: RUNTIME_AGENT_REPORT_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-report-token
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
          ports:
            - name: agent
              containerPort: 19090
          volumeMounts:
            - name: workspaces
              mountPath: /workspaces
      volumes:
        - name: workspaces
          persistentVolumeClaim:
            claimName: clawmanager-workspaces
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hermes-runtime
  namespace: clawmanager-system
  labels:
    app: hermes-runtime
    clawmanager.io/runtime-type: hermes
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hermes-runtime
      clawmanager.io/runtime-type: hermes
  template:
    metadata:
      labels:
        app: hermes-runtime
        clawmanager.io/runtime-type: hermes
    spec:
      containers:
        - name: runtime
          image: ghcr.io/yuan-lab-llm/agentsruntime/hermes-lite:latest
          imagePullPolicy: IfNotPresent
          env:
            - name: CLAWMANAGER_RUNTIME_TYPE
              value: hermes
            - name: CLAWMANAGER_BACKEND_URL
              value: "http://clawmanager-gateway.clawmanager-system.svc.cluster.local:9001"
            - name: CLAWMANAGER_RUNTIME_DEPLOYMENT_NAME
              value: hermes-runtime
            - name: CLAWMANAGER_RUNTIME_IMAGE_REF
              value: ghcr.io/yuan-lab-llm/agentsruntime/hermes-lite:latest
            - name: CLAWMANAGER_AGENT_PORT
              value: "19090"
            - name: CLAWMANAGER_GATEWAY_PORT_START
              value: "20000"
            - name: CLAWMANAGER_GATEWAY_PORT_END
              value: "20299"
            - name: HERMES_TUI_DIR
              value: /usr/local/lib/hermes-agent/ui-tui
            - name: CLAWMANAGER_AGENT_CONTROL_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-control-token
            - name: CLAWMANAGER_AGENT_REPORT_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-report-token
            - name: RUNTIME_WORKSPACE_ROOT
              value: /workspaces
            - name: RUNTIME_AGENT_LISTEN_ADDR
              value: "0.0.0.0:19090"
            - name: RUNTIME_AGENT_PUBLIC_PORT
              value: "19090"
            - name: RUNTIME_GATEWAY_PORT_START
              value: "20000"
            - name: RUNTIME_GATEWAY_PORT_END
              value: "20299"
            - name: RUNTIME_AGENT_CONTROL_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-control-token
            - name: RUNTIME_AGENT_REPORT_TOKEN
              valueFrom:
                secretKeyRef:
                  name: clawmanager-secrets
                  key: runtime-agent-report-token
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
          ports:
            - name: agent
              containerPort: 19090
          volumeMounts:
            - name: workspaces
              mountPath: /workspaces
      volumes:
        - name: workspaces
          persistentVolumeClaim:
            claimName: clawmanager-workspaces
---
apiVersion: v1
kind: Service
metadata:
  name: clawmanager-frontend
  namespace: clawmanager-system
spec:
  type: NodePort
  selector:
    app: clawmanager-app
  ports:
    - name: https
      port: 443
      targetPort: https
      nodePort: 30443
---
apiVersion: v1
kind: Service
metadata:
  name: clawmanager-gateway
  namespace: clawmanager-system
spec:
  type: ClusterIP
  selector:
    app: clawmanager-app
  ports:
    - name: api
      port: 9001
      targetPort: 9001
---
apiVersion: v1
kind: Service
metadata:
  name: clawmanager-egress-proxy
  namespace: clawmanager-system
spec:
  type: ClusterIP
  selector:
    app: clawmanager-app
  ports:
    - name: proxy
      port: 3128
      targetPort: 9001
