From ea737451479be4dd741cef7eb3660dd0d9d0ef19 Mon Sep 17 00:00:00 2001 From: Mermaid Man Date: Fri, 24 Apr 2026 19:45:03 +0000 Subject: [PATCH] refactor: restructure into hermes/ and openclaw/ directories - Split cloudinit.tf into cloudinit-hermes.tf and cloudinit-openclaw.tf - Split variables.tf into variables-common.tf, variables-hermes.tf, variables-openclaw.tf - Move templates into hermes/templates/ and openclaw/templates/ - Move models/ into openclaw/models/ - Move hermes-openclaw.json to openclaw/openclaw-reference.json - Move hermes docs to hermes/docs/ - OpenClaw cloudinit now uses variables instead of hardcoded values - All 48 variable references verified against definitions --- cloudinit-hermes.tf | 58 +++++++++ cloudinit-openclaw.tf | 59 +++++++++ cloudinit.tf | 120 ------------------ .../docs/AUDIT_REPORT.md | 0 .../docs/DEBUGGING.md | 0 .../docs/FIX_SUMMARY.md | 0 .../docs/VERIFICATION_CHECKLIST.md | 0 .../templates}/userdata-hermes.tpl | 0 main.tf | 3 + {models => openclaw/models}/anthropic.json | 0 {models => openclaw/models}/combined.json | 0 {models => openclaw/models}/gemini.json | 0 {models => openclaw/models}/groq.json | 0 {models => openclaw/models}/openai.json | 0 {models => openclaw/models}/openrouter.json | 0 {models => openclaw/models}/venice.json | 0 .../openclaw-reference.json | 0 .../templates}/userdata-openclaw.tpl | 0 variables.tf => variables-common.tf | 102 +-------------- variables-hermes.tf | 105 +++++++++++++++ variables-openclaw.tf | 46 +++++++ 21 files changed, 277 insertions(+), 216 deletions(-) create mode 100644 cloudinit-hermes.tf create mode 100644 cloudinit-openclaw.tf delete mode 100644 cloudinit.tf rename docs/HERMES_AUDIT_REPORT.md => hermes/docs/AUDIT_REPORT.md (100%) rename docs/HERMES_DEBUGGING.md => hermes/docs/DEBUGGING.md (100%) rename HERMES_FIX_SUMMARY.md => hermes/docs/FIX_SUMMARY.md (100%) rename HERMES_VERIFICATION_CHECKLIST.md => hermes/docs/VERIFICATION_CHECKLIST.md (100%) rename {templates => hermes/templates}/userdata-hermes.tpl (100%) rename {models => openclaw/models}/anthropic.json (100%) rename {models => openclaw/models}/combined.json (100%) rename {models => openclaw/models}/gemini.json (100%) rename {models => openclaw/models}/groq.json (100%) rename {models => openclaw/models}/openai.json (100%) rename {models => openclaw/models}/openrouter.json (100%) rename {models => openclaw/models}/venice.json (100%) rename hermes-openclaw.json => openclaw/openclaw-reference.json (100%) rename {templates => openclaw/templates}/userdata-openclaw.tpl (100%) rename variables.tf => variables-common.tf (69%) create mode 100644 variables-hermes.tf create mode 100644 variables-openclaw.tf diff --git a/cloudinit-hermes.tf b/cloudinit-hermes.tf new file mode 100644 index 0000000..bc1b2cd --- /dev/null +++ b/cloudinit-hermes.tf @@ -0,0 +1,58 @@ +# Cloud-init Configuration — Hermes Agent +# Only active when agent_framework = "hermes" + +# Hermes Agent cloud-init +data "cloudinit_config" "hermes" { + count = var.agent_framework == "hermes" ? 1 : 0 + + gzip = false + base64_encode = true + + part { + filename = "cloud-config.yaml" + content_type = "text/cloud-config" + content = templatefile("${path.module}/hermes/templates/userdata-hermes.tpl", { + # Server configuration + server_name = var.server_name + admin_user = local.effective_admin_user + location = var.location_hetzner + + # Agent configuration + agent_name = var.agent_name + primary_model = var.primary_model + primary_model_name = var.primary_model_name + fallback_models = var.fallback_models + docker_enabled = var.docker_enabled + + # SSH configuration + ssh_port = var.ssh_port + ssh_allowed_ips = var.ssh_allowed_ips + admin_ssh_keys = var.admin_ssh_keys + + # API keys + venice_api_key = var.venice_api_key + venice_base_url = var.venice_base_url + brave_search_api_key = var.brave_search_api_key + + # Discord + discord_bot_token = var.discord_bot_token + discord_server_id = var.discord_server_id + discord_user_id = var.discord_user_id + discord_home_channel = var.discord_home_channel + discord_allowed_users = var.discord_allowed_users + discord_auto_thread = var.discord_auto_thread + gateway_allow_all_users = var.gateway_allow_all_users + + # Gateway + gateway_token = var.gateway_token != "" ? var.gateway_token : random_password.gateway_token[0].result + gateway_allowed_users = var.gateway_allowed_users + }) + } +} + +# Random password for gateway token if not provided +resource "random_password" "gateway_token" { + count = var.agent_framework == "hermes" && var.gateway_token == "" ? 1 : 0 + length = 32 + special = false +} diff --git a/cloudinit-openclaw.tf b/cloudinit-openclaw.tf new file mode 100644 index 0000000..c38b191 --- /dev/null +++ b/cloudinit-openclaw.tf @@ -0,0 +1,59 @@ +# Cloud-init Configuration — OpenClaw Gateway +# Only active when agent_framework = "openclaw" + +# OpenClaw cloud-init +data "cloudinit_config" "openclaw" { + count = var.agent_framework == "openclaw" ? 1 : 0 + + gzip = false + base64_encode = true + + part { + filename = "cloud-config.yaml" + content_type = "text/cloud-config" + content = templatefile("${path.module}/openclaw/templates/userdata-openclaw.tpl", { + # Server configuration + server_name = var.server_name + admin_user = local.effective_admin_user + + # SSH configuration + ssh_port = var.ssh_port + ssh_allowed_ips = var.ssh_allowed_ips + admin_ssh_keys = var.admin_ssh_keys + + # OpenClaw configuration + openclaw_version = var.openclaw_version + node_version = var.node_version + agent_name = var.agent_name + agent_timezone = var.agent_timezone + + # System configuration + enable_swap = var.enable_swap + swap_size = var.swap_size + enable_fail2ban = var.enable_fail2ban + enable_unattended_upgrades = var.enable_unattended_upgrades + + # Tailscale + enable_tailscale = var.enable_tailscale + tailscale_auth_key = var.tailscale_auth_key + + # API keys + venice_api_key = var.venice_api_key + default_model = var.primary_model + brave_search_api_key = var.brave_search_api_key + + # Discord + discord_bot_token = var.discord_bot_token + discord_server_id = var.discord_server_id + discord_user_id = var.discord_user_id + discord_home_channel = var.discord_home_channel + discord_allowed_users = var.discord_allowed_users + discord_auto_thread = var.discord_auto_thread + + # Inference models configuration + primary_model = var.primary_model + fallback_models = jsonencode(var.fallback_models) + models_config = file("${path.module}/openclaw/models/venice.json") + }) + } +} diff --git a/cloudinit.tf b/cloudinit.tf deleted file mode 100644 index da8ed45..0000000 --- a/cloudinit.tf +++ /dev/null @@ -1,120 +0,0 @@ -# Cloud-init Configuration -# Selects template based on agent_framework variable - -# Hermes Agent cloud-init -data "cloudinit_config" "hermes" { - count = var.agent_framework == "hermes" ? 1 : 0 - - gzip = false - base64_encode = true - - part { - filename = "cloud-config.yaml" - content_type = "text/cloud-config" - content = templatefile("${path.module}/templates/userdata-hermes.tpl", { - # Server configuration - server_name = var.server_name - admin_user = local.effective_admin_user - location = var.location_hetzner - - # Agent configuration - agent_name = var.agent_name - primary_model = var.primary_model - primary_model_name = var.primary_model_name - fallback_models = var.fallback_models - docker_enabled = var.docker_enabled - - # SSH configuration - ssh_port = var.ssh_port - ssh_allowed_ips = var.ssh_allowed_ips - admin_ssh_keys = var.admin_ssh_keys - - # API keys - venice_api_key = var.venice_api_key - venice_base_url = var.venice_base_url - brave_search_api_key = var.brave_search_api_key - - # Discord - discord_bot_token = var.discord_bot_token - discord_server_id = var.discord_server_id - discord_user_id = var.discord_user_id - discord_home_channel = var.discord_home_channel - discord_allowed_users = var.discord_allowed_users - discord_auto_thread = var.discord_auto_thread - gateway_allow_all_users = var.gateway_allow_all_users - - # Gateway - gateway_token = var.gateway_token != "" ? var.gateway_token : random_password.gateway_token[0].result - gateway_allowed_users = var.gateway_allowed_users - }) - } -} - -# OpenClaw cloud-init -data "cloudinit_config" "openclaw" { - count = var.agent_framework == "openclaw" ? 1 : 0 - - gzip = false - base64_encode = true - - part { - filename = "cloud-config.yaml" - content_type = "text/cloud-config" - content = templatefile("${path.module}/templates/userdata-openclaw.tpl", { - # Server configuration - server_name = var.server_name - admin_user = local.effective_admin_user - - # SSH configuration - ssh_port = var.ssh_port - ssh_allowed_ips = var.ssh_allowed_ips - admin_ssh_keys = var.admin_ssh_keys - - # OpenClaw configuration - openclaw_version = "lts" - node_version = "22" - agent_name = var.agent_name - agent_timezone = "UTC" - - # System configuration - enable_swap = true - swap_size = 2 - enable_fail2ban = true - enable_unattended_upgrades = true - - # Tailscale - enable_tailscale = var.enable_tailscale - tailscale_auth_key = var.tailscale_auth_key - - # API keys - venice_api_key = var.venice_api_key - default_model = var.primary_model - brave_search_api_key = var.brave_search_api_key - - # Discord - discord_bot_token = var.discord_bot_token - discord_server_id = var.discord_server_id - discord_user_id = var.discord_user_id - discord_home_channel = var.discord_home_channel - discord_allowed_users = var.discord_allowed_users - discord_auto_thread = var.discord_auto_thread - - # Inference models configuration - primary_model = var.primary_model - fallback_models = jsonencode(var.fallback_models) - models_config = file("${path.module}/models/venice.json") - }) - } -} - -# Random password for gateway token if not provided -resource "random_password" "gateway_token" { - count = var.agent_framework == "hermes" && var.gateway_token == "" ? 1 : 0 - length = 32 - special = false -} - -# Output selected userdata -locals { - userdata = var.agent_framework == "hermes" ? data.cloudinit_config.hermes[0].rendered : data.cloudinit_config.openclaw[0].rendered -} \ No newline at end of file diff --git a/docs/HERMES_AUDIT_REPORT.md b/hermes/docs/AUDIT_REPORT.md similarity index 100% rename from docs/HERMES_AUDIT_REPORT.md rename to hermes/docs/AUDIT_REPORT.md diff --git a/docs/HERMES_DEBUGGING.md b/hermes/docs/DEBUGGING.md similarity index 100% rename from docs/HERMES_DEBUGGING.md rename to hermes/docs/DEBUGGING.md diff --git a/HERMES_FIX_SUMMARY.md b/hermes/docs/FIX_SUMMARY.md similarity index 100% rename from HERMES_FIX_SUMMARY.md rename to hermes/docs/FIX_SUMMARY.md diff --git a/HERMES_VERIFICATION_CHECKLIST.md b/hermes/docs/VERIFICATION_CHECKLIST.md similarity index 100% rename from HERMES_VERIFICATION_CHECKLIST.md rename to hermes/docs/VERIFICATION_CHECKLIST.md diff --git a/templates/userdata-hermes.tpl b/hermes/templates/userdata-hermes.tpl similarity index 100% rename from templates/userdata-hermes.tpl rename to hermes/templates/userdata-hermes.tpl diff --git a/main.tf b/main.tf index 1b2cca4..4555a21 100644 --- a/main.tf +++ b/main.tf @@ -52,4 +52,7 @@ locals { managed = "terraform" component = var.agent_framework == "hermes" ? "hermes-agent" : "openclaw-gateway" } + + # Select userdata based on framework + userdata = var.agent_framework == "hermes" ? data.cloudinit_config.hermes[0].rendered : data.cloudinit_config.openclaw[0].rendered } \ No newline at end of file diff --git a/models/anthropic.json b/openclaw/models/anthropic.json similarity index 100% rename from models/anthropic.json rename to openclaw/models/anthropic.json diff --git a/models/combined.json b/openclaw/models/combined.json similarity index 100% rename from models/combined.json rename to openclaw/models/combined.json diff --git a/models/gemini.json b/openclaw/models/gemini.json similarity index 100% rename from models/gemini.json rename to openclaw/models/gemini.json diff --git a/models/groq.json b/openclaw/models/groq.json similarity index 100% rename from models/groq.json rename to openclaw/models/groq.json diff --git a/models/openai.json b/openclaw/models/openai.json similarity index 100% rename from models/openai.json rename to openclaw/models/openai.json diff --git a/models/openrouter.json b/openclaw/models/openrouter.json similarity index 100% rename from models/openrouter.json rename to openclaw/models/openrouter.json diff --git a/models/venice.json b/openclaw/models/venice.json similarity index 100% rename from models/venice.json rename to openclaw/models/venice.json diff --git a/hermes-openclaw.json b/openclaw/openclaw-reference.json similarity index 100% rename from hermes-openclaw.json rename to openclaw/openclaw-reference.json diff --git a/templates/userdata-openclaw.tpl b/openclaw/templates/userdata-openclaw.tpl similarity index 100% rename from templates/userdata-openclaw.tpl rename to openclaw/templates/userdata-openclaw.tpl diff --git a/variables.tf b/variables-common.tf similarity index 69% rename from variables.tf rename to variables-common.tf index ea031de..cfa8051 100644 --- a/variables.tf +++ b/variables-common.tf @@ -1,4 +1,5 @@ -# OpenBoatmobile Configuration Variables +# OpenBoatmobile Configuration Variables — Common +# Shared by both Hermes and OpenClaw deployments # Environment-based secrets: Set TF_VAR_ in your shell or .env file # ============================================================================= @@ -148,66 +149,16 @@ variable "admin_ssh_keys" { } # ============================================================================= -# AGENT CONFIGURATION -# ============================================================================= - -variable "agent_name" { - description = "Name for the agent" - type = string - default = "hermes" -} - -variable "docker_enabled" { - description = "Whether to deploy Hermes in Docker container (true) or install directly on host (false)" - type = bool - default = true -} - -variable "agent_timezone" { - description = "Timezone for the agent" - type = string - default = "UTC" -} - -# ============================================================================= -# MODEL CONFIGURATION -# ============================================================================= - -variable "primary_model" { - description = "Primary model for inference (without venice/ prefix when using Venice API directly)" - type = string - default = "olafangensan-glm-4.7-flash-heretic" -} - -variable "primary_model_name" { - description = "Human-readable name for the primary model" - type = string - default = "GLM 4.7 Flash Heretic" -} - -variable "fallback_models" { - description = "List of fallback models in priority order (without venice/ prefix)" - type = list(string) - default = ["zai-org-glm-5"] -} - -# ============================================================================= -# API KEYS (Set via environment: TF_VAR_) +# API KEYS — Shared (Set via environment: TF_VAR_) # ============================================================================= variable "venice_api_key" { - description = "Venice AI API key for inference (used as OPENAI_API_KEY for custom endpoint)" + description = "Venice AI API key for inference" type = string sensitive = true default = "" } -variable "venice_base_url" { - description = "Venice AI base URL (default: https://api.venice.ai/api/v1)" - type = string - default = "https://api.venice.ai/api/v1" -} - variable "brave_search_api_key" { description = "Brave Search API key" type = string @@ -216,7 +167,7 @@ variable "brave_search_api_key" { } # ============================================================================= -# DISCORD CONFIGURATION +# DISCORD CONFIGURATION — Shared # ============================================================================= variable "discord_bot_token" { @@ -238,47 +189,6 @@ variable "discord_user_id" { default = [] } -variable "discord_home_channel" { - description = "Discord channel ID for home channel (cron delivery, notifications)" - type = string - default = "" -} - -variable "discord_allowed_users" { - description = "Comma-separated Discord user IDs allowed (DISCORD_ALLOWED_USERS)" - type = string - default = "" -} - -variable "discord_auto_thread" { - description = "Auto-create threads on @mention (DISCORD_AUTO_THREAD)" - type = bool - default = true -} - -variable "gateway_allow_all_users" { - description = "Allow all users without allowlist (GATEWAY_ALLOW_ALL_USERS)" - type = bool - default = true -} - -# ============================================================================= -# GATEWAY CONFIGURATION -# ============================================================================= - -variable "gateway_token" { - description = "Gateway authentication token" - type = string - sensitive = true - default = "" -} - -variable "gateway_allowed_users" { - description = "Comma-separated list of allowed user IDs" - type = string - default = "" -} - # ============================================================================= # PROJECT METADATA # ============================================================================= @@ -316,4 +226,4 @@ variable "tailscale_tailnet_domain" { description = "Tailscale tailnet domain (without .ts.net suffix)" type = string default = "tailnet" -} \ No newline at end of file +} diff --git a/variables-hermes.tf b/variables-hermes.tf new file mode 100644 index 0000000..d2155d2 --- /dev/null +++ b/variables-hermes.tf @@ -0,0 +1,105 @@ +# OpenBoatmobile Configuration Variables — Hermes-specific +# Only used when agent_framework = "hermes" + +# ============================================================================= +# AGENT CONFIGURATION +# ============================================================================= + +variable "agent_name" { + description = "Name for the agent" + type = string + default = "hermes" +} + +variable "docker_enabled" { + description = "Whether to deploy Hermes in Docker container (true) or install directly on host (false)" + type = bool + default = true +} + +# ============================================================================= +# MODEL CONFIGURATION +# ============================================================================= + +variable "primary_model" { + description = "Primary model for inference (without venice/ prefix when using Venice API directly)" + type = string + default = "olafangensan-glm-4.7-flash-heretic" +} + +variable "primary_model_name" { + description = "Human-readable name for the primary model" + type = string + default = "GLM 4.7 Flash Heretic" +} + +variable "fallback_models" { + description = "List of fallback models in priority order (without venice/ prefix)" + type = list(string) + default = ["zai-org-glm-5"] +} + +# ============================================================================= +# HERMES API CONFIGURATION +# ============================================================================= + +variable "venice_base_url" { + description = "Venice AI base URL (default: https://api.venice.ai/api/v1)" + type = string + default = "https://api.venice.ai/api/v1" +} + +# ============================================================================= +# DISCORD — Hermes-specific +# ============================================================================= + +variable "discord_home_channel" { + description = "Discord channel ID for home channel (cron delivery, notifications)" + type = string + default = "" +} + +variable "discord_allowed_users" { + description = "Comma-separated Discord user IDs allowed (DISCORD_ALLOWED_USERS)" + type = string + default = "" +} + +variable "discord_auto_thread" { + description = "Auto-create threads on @mention (DISCORD_AUTO_THREAD)" + type = bool + default = true +} + +# ============================================================================= +# GATEWAY CONFIGURATION — Hermes-specific +# ============================================================================= + +variable "gateway_token" { + description = "Gateway authentication token" + type = string + sensitive = true + default = "" +} + +variable "gateway_allowed_users" { + description = "Comma-separated list of allowed user IDs" + type = string + default = "" +} + +variable "gateway_allow_all_users" { + description = "Allow all users without allowlist (GATEWAY_ALLOW_ALL_USERS)" + type = bool + default = true +} + +# ============================================================================= +# TIMEZONE +# ============================================================================= + +variable "agent_timezone" { + description = "Timezone for the agent" + type = string + default = "UTC" +} diff --git a/variables-openclaw.tf b/variables-openclaw.tf new file mode 100644 index 0000000..5d135e6 --- /dev/null +++ b/variables-openclaw.tf @@ -0,0 +1,46 @@ +# OpenBoatmobile Configuration Variables — OpenClaw-specific +# Only used when agent_framework = "openclaw" + +# ============================================================================= +# OPENCLAW INSTALLATION +# ============================================================================= + +variable "openclaw_version" { + description = "OpenClaw version to install: 'latest', 'lts', or a specific version string" + type = string + default = "lts" +} + +variable "node_version" { + description = "Node.js major version for OpenClaw runtime (LTS recommended: 22)" + type = string + default = "22" +} + +# ============================================================================= +# SYSTEM CONFIGURATION — OpenClaw defaults +# ============================================================================= + +variable "enable_swap" { + description = "Create a swap file on the server" + type = bool + default = true +} + +variable "swap_size" { + description = "Swap file size in GB" + type = number + default = 2 +} + +variable "enable_fail2ban" { + description = "Install and configure fail2ban for SSH protection" + type = bool + default = true +} + +variable "enable_unattended_upgrades" { + description = "Enable automatic security updates" + type = bool + default = true +}