From 7ee4bbe5b6dea1d1a45f00514ba128aa96030b5a Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 14 Mar 2026 09:49:38 +0000 Subject: [PATCH] feat(claude-memory): add stack and update image to standalone repo - Add claude-memory stack (was previously untracked) - Update Docker image from viktorbarzin/claude-memory to viktorbarzin/claude-memory-mcp (standalone open-source repo) - CI/CD now lives in the standalone repo's .woodpecker.yml --- stacks/claude-memory/main.tf | 182 +++++++++++++++++++++ stacks/claude-memory/secrets/fullchain.pem | 48 ++++++ stacks/claude-memory/secrets/privkey.pem | 5 + stacks/claude-memory/terragrunt.hcl | 8 + stacks/claude-memory/tiers.tf | 10 ++ 5 files changed, 253 insertions(+) create mode 100644 stacks/claude-memory/main.tf create mode 100644 stacks/claude-memory/secrets/fullchain.pem create mode 100644 stacks/claude-memory/secrets/privkey.pem create mode 100644 stacks/claude-memory/terragrunt.hcl create mode 100644 stacks/claude-memory/tiers.tf diff --git a/stacks/claude-memory/main.tf b/stacks/claude-memory/main.tf new file mode 100644 index 00000000..e23ce4c8 --- /dev/null +++ b/stacks/claude-memory/main.tf @@ -0,0 +1,182 @@ +variable "tls_secret_name" { + type = string + sensitive = true +} +variable "postgresql_host" { type = string } +variable "dbaas_postgresql_root_password" { + type = string + sensitive = true +} +variable "claude_memory_db_password" { + type = string + sensitive = true +} +variable "claude_memory_api_key" { + type = string + sensitive = true +} + +resource "kubernetes_namespace" "claude-memory" { + metadata { + name = "claude-memory" + labels = { + tier = local.tiers.aux + } + } +} + +module "tls_secret" { + source = "../../modules/kubernetes/setup_tls_secret" + namespace = kubernetes_namespace.claude-memory.metadata[0].name + tls_secret_name = var.tls_secret_name +} + +# Database init job +resource "kubernetes_job" "db_init" { + metadata { + name = "claude-memory-db-init" + namespace = kubernetes_namespace.claude-memory.metadata[0].name + } + spec { + template { + metadata {} + spec { + container { + name = "db-init" + image = "postgres:16-alpine" + command = [ + "sh", "-c", + <<-EOT + set -e + PGPASSWORD='${var.dbaas_postgresql_root_password}' psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_roles WHERE rolname='claude_memory'" | grep -q 1 || \ + PGPASSWORD='${var.dbaas_postgresql_root_password}' psql -h ${var.postgresql_host} -U root -c "CREATE ROLE claude_memory WITH LOGIN PASSWORD '${var.claude_memory_db_password}'" + PGPASSWORD='${var.dbaas_postgresql_root_password}' psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_database WHERE datname='claude_memory'" | grep -q 1 || \ + PGPASSWORD='${var.dbaas_postgresql_root_password}' psql -h ${var.postgresql_host} -U root -c "CREATE DATABASE claude_memory OWNER claude_memory" + PGPASSWORD='${var.dbaas_postgresql_root_password}' psql -h ${var.postgresql_host} -U root -c "GRANT ALL PRIVILEGES ON DATABASE claude_memory TO claude_memory" + echo "Database init complete" + EOT + ] + } + restart_policy = "Never" + } + } + backoff_limit = 3 + } + wait_for_completion = true + timeouts { + create = "2m" + } +} + +resource "kubernetes_deployment" "claude-memory" { + depends_on = [kubernetes_job.db_init] + metadata { + name = "claude-memory" + namespace = kubernetes_namespace.claude-memory.metadata[0].name + labels = { + app = "claude-memory" + tier = local.tiers.aux + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "claude-memory" + } + } + template { + metadata { + labels = { + app = "claude-memory" + } + } + spec { + container { + name = "claude-memory" + image = "viktorbarzin/claude-memory-mcp:latest" + + port { + container_port = 8000 + } + + env { + name = "DATABASE_URL" + value = "postgresql://claude_memory:${var.claude_memory_db_password}@${var.postgresql_host}:5432/claude_memory" + } + env { + name = "API_KEY" + value = var.claude_memory_api_key + } + + liveness_probe { + http_get { + path = "/health" + port = 8000 + } + initial_delay_seconds = 5 + period_seconds = 30 + } + readiness_probe { + http_get { + path = "/health" + port = 8000 + } + initial_delay_seconds = 3 + period_seconds = 10 + } + + resources { + requests = { + memory = "32Mi" + cpu = "10m" + } + limits = { + memory = "128Mi" + } + } + } + } + } + } + lifecycle { + ignore_changes = [ + spec[0].template[0].spec[0].container[0].image + ] + } +} + +resource "kubernetes_service" "claude-memory" { + metadata { + name = "claude-memory" + namespace = kubernetes_namespace.claude-memory.metadata[0].name + labels = { + app = "claude-memory" + } + } + spec { + selector = { + app = "claude-memory" + } + port { + name = "http" + port = 80 + target_port = 8000 + } + } +} + +module "ingress" { + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.claude-memory.metadata[0].name + name = "claude-memory" + tls_secret_name = var.tls_secret_name + extra_annotations = { + "gethomepage.dev/enabled" = "true" + "gethomepage.dev/name" = "Claude Memory" + "gethomepage.dev/description" = "Shared persistent memory for Claude sessions" + "gethomepage.dev/icon" = "claude-ai.png" + "gethomepage.dev/group" = "Core Platform" + "gethomepage.dev/pod-selector" = "" + } +} diff --git a/stacks/claude-memory/secrets/fullchain.pem b/stacks/claude-memory/secrets/fullchain.pem new file mode 100644 index 00000000..9733f8b8 --- /dev/null +++ b/stacks/claude-memory/secrets/fullchain.pem @@ -0,0 +1,48 @@ +-----BEGIN CERTIFICATE----- +MIIDlTCCAxqgAwIBAgISBvDIlLwpDZs2hEwZWeUvq4gVMAoGCCqGSM49BAMDMDIx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF +NzAeFw0yNjAyMTQyMzA2NDZaFw0yNjA1MTUyMzA2NDVaMBoxGDAWBgNVBAMTD3Zp +a3RvcmJhcnppbi5tZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP8GrH0I0dUB +clgDenQcFQAqje+eg6ZS2YGe2vjbDsZFiBOqepISPrDSnBNq7CLNtMm9flr+ldw7 +ghs4N1j/ajajggImMIICIjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB +BQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxAtiVrnp8HhoPacayElIikSS +2RcwHwYDVR0jBBgwFoAUrkie3IcdRKBv2qLlYHQEeMKcAIAwMgYIKwYBBQUHAQEE +JjAkMCIGCCsGAQUFBzAChhZodHRwOi8vZTcuaS5sZW5jci5vcmcvMC0GA1UdEQQm +MCSCESoudmlrdG9yYmFyemluLm1lgg92aWt0b3JiYXJ6aW4ubWUwEwYDVR0gBAww +CjAIBgZngQwBAgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0cDovL2U3LmMubGVuY3Iu +b3JnLzIyLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB3AEmcm2neHXzs/Dbe +zYdkprhbrwqHgBnRVVL76esp3fjDAAABnF6dqlAAAAQDAEgwRgIhALqavwUJGHOp +9rIvPmeAbd14fR2kVjrmmwPKqVnwraD3AiEAiBH8UfmcdE2NULHHEQWqsXNzSay7 +weQAH6ysTwXt8sgAdQCWl2S/VViXrfdDh2g3CEJ36fA61fak8zZuRqQ/D8qpxgAA +AZxenaqPAAAEAwBGMEQCIAvcxs2nNTNSnz8+AGVlZRYCeY+ADSqFGXtzgx/rtAGF +AiAs7PTDnFtrNuY6UeprX9WXBCVWiJcefFCSIwgRvsMjzzAKBggqhkjOPQQDAwNp +ADBmAjEA1wgegFLHC/MZJt7hYaYfvdaECgoAIwgnHQXYgP9eaB5SVDpRGcJWVQLT +iLR2KEv0AjEA8ajBRm579Mv4WzYROi14Cy5cLaMwyZV5ZfRWLYhIdouLxzsXlgh5 +HmaIfzUQHaWe +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEVzCCAj+gAwIBAgIRAKp18eYrjwoiCWbTi7/UuqEwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw +WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCRTcwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARB6AST +CFh/vjcwDMCgQer+VtqEkz7JANurZxLP+U9TCeioL6sp5Z8VRvRbYk4P1INBmbef +QHJFHCxcSjKmwtvGBWpl/9ra8HW0QDsUaJW2qOJqceJ0ZVFT3hbUHifBM/2jgfgw +gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD +ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSuSJ7chx1EoG/aouVgdAR4 +wpwAgDAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB +AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g +BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu +Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAjx66fDdLk5ywFn3CzA1w1qfylHUD +aEf0QZpXcJseddJGSfbUUOvbNR9N/QQ16K1lXl4VFyhmGXDT5Kdfcr0RvIIVrNxF +h4lqHtRRCP6RBRstqbZ2zURgqakn/Xip0iaQL0IdfHBZr396FgknniRYFckKORPG +yM3QKnd66gtMst8I5nkRQlAg/Jb+Gc3egIvuGKWboE1G89NTsN9LTDD3PLj0dUMr +OIuqVjLB8pEC6yk9enrlrqjXQgkLEYhXzq7dLafv5Vkig6Gl0nuuqjqfp0Q1bi1o +yVNAlXe6aUXw92CcghC9bNsKEO1+M52YY5+ofIXlS/SEQbvVYYBLZ5yeiglV6t3S +M6H+vTG0aP9YHzLn/KVOHzGQfXDP7qM5tkf+7diZe7o2fw6O7IvN6fsQXEQQj8TJ +UXJxv2/uJhcuy/tSDgXwHM8Uk34WNbRT7zGTGkQRX0gsbjAea/jYAoWv0ZvQRwpq +Pe79D/i7Cep8qWnA+7AE/3B3S/3dEEYmc0lpe1366A/6GEgk3ktr9PEoQrLChs6I +tu3wnNLB2euC8IKGLQFpGtOO/2/hiAKjyajaBP25w1jF0Wl8Bbqne3uZ2q1GyPFJ +YRmT7/OXpmOH/FVLtwS+8ng1cAmpCujPwteJZNcDG0sF2n/sc0+SQf49fdyUK0ty ++VUwFj9tmWxyR/M= +-----END CERTIFICATE----- diff --git a/stacks/claude-memory/secrets/privkey.pem b/stacks/claude-memory/secrets/privkey.pem new file mode 100644 index 00000000..39d79eda --- /dev/null +++ b/stacks/claude-memory/secrets/privkey.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWwcmcU+mLlFDxrKh +Iy7RRghYkNr/MdLEwcePoe1vUWKhRANCAAT/Bqx9CNHVAXJYA3p0HBUAKo3vnoOm +UtmBntr42w7GRYgTqnqSEj6w0pwTauwizbTJvX5a/pXcO4IbODdY/2o2 +-----END PRIVATE KEY----- diff --git a/stacks/claude-memory/terragrunt.hcl b/stacks/claude-memory/terragrunt.hcl new file mode 100644 index 00000000..0d1c8e53 --- /dev/null +++ b/stacks/claude-memory/terragrunt.hcl @@ -0,0 +1,8 @@ +include "root" { + path = find_in_parent_folders() +} + +dependency "platform" { + config_path = "../platform" + skip_outputs = true +} diff --git a/stacks/claude-memory/tiers.tf b/stacks/claude-memory/tiers.tf new file mode 100644 index 00000000..eb0f8083 --- /dev/null +++ b/stacks/claude-memory/tiers.tf @@ -0,0 +1,10 @@ +# Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa +locals { + tiers = { + core = "0-core" + cluster = "1-cluster" + gpu = "2-gpu" + edge = "3-edge" + aux = "4-aux" + } +}