commit 67d2c98696400acb0b935841d486447a3ad9dbb0 Author: quinn Date: Tue Jun 30 01:55:23 2026 -0400 feat(tf-ct-services): com.uvlava.ct.services base host (docker+swap) Standing CT services droplet for CT + MC MCPs + cocottetech app backends. Provisioned 138.197.120.105 (nyc3 s-2vcpu-4gb). Base image; app/MCP deploys land later. Co-Authored-By: Claude Opus 4.8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29898ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.terraform/ +*.tfstate +*.tfstate.backup +.terraform.lock.hcl +tfplan diff --git a/cloud-init.yaml b/cloud-init.yaml new file mode 100644 index 0000000..2fe791f --- /dev/null +++ b/cloud-init.yaml @@ -0,0 +1,14 @@ +#cloud-config +# Base host for com.uvlava.ct.services: docker + swap, ready for the CT/MC MCP + +# app-backend deployments (each app deploys its own compose/units here later). +package_update: true +packages: + - docker.io + - docker-compose-v2 + +runcmd: + - [ bash, -c, "fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile && echo '/swapfile none swap sw 0 0' >> /etc/fstab" ] + - [ bash, -c, "mkdir -p /opt/ct-services" ] + - [ systemctl, enable, --now, docker ] + +final_message: "com.uvlava.ct.services base ready (docker + swap). Deploy CT/MC MCPs + app backends here." diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..1d4373b --- /dev/null +++ b/main.tf @@ -0,0 +1,62 @@ +############################################################################### +# com.uvlava.ct.services — standing CT services host: CT + MC MCPs (always-up) +# + cocottetech app backends (prospector / finances / marketing / onlyfans / +# cocottetech). Each app self-declares onto this host via its own .infra.yaml. +# Base image only here (docker + swap); app/MCP deploys land later. +############################################################################### + +resource "digitalocean_droplet" "ct_services" { + name = var.name + image = "ubuntu-24-04-x64" + size = var.droplet_size + region = var.region + ssh_keys = var.ssh_key_fingerprints + tags = ["ct", "services", "mcp"] + + user_data = file("${path.module}/cloud-init.yaml") + + lifecycle { + # App/MCP data + state live in /opt volumes; `name` is ForceNew (rename via doctl). + ignore_changes = [user_data, name] + } +} + +resource "digitalocean_firewall" "ct_services" { + name = "ct-services-fw" + droplet_ids = [digitalocean_droplet.ct_services.id] + + inbound_rule { + protocol = "tcp" + port_range = "22" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "tcp" + port_range = "80" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "tcp" + port_range = "443" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + outbound_rule { + protocol = "tcp" + port_range = "1-65535" + destination_addresses = ["0.0.0.0/0", "::/0"] + } + outbound_rule { + protocol = "udp" + port_range = "1-65535" + destination_addresses = ["0.0.0.0/0", "::/0"] + } + outbound_rule { + protocol = "icmp" + destination_addresses = ["0.0.0.0/0", "::/0"] + } +} + +output "ct_services_ip" { + value = digitalocean_droplet.ct_services.ipv4_address +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..60f726b --- /dev/null +++ b/variables.tf @@ -0,0 +1,27 @@ +variable "do_token" { + type = string + sensitive = true +} + +variable "region" { + type = string + default = "nyc3" +} + +variable "droplet_size" { + type = string + default = "s-2vcpu-4gb" +} + +variable "ssh_key_fingerprints" { + type = list(string) + default = [ + "00:b5:2c:23:67:43:e5:39:c9:c2:43:31:6e:5c:03:10", # plum-natalie + "b2:7e:66:b1:9b:61:ac:69:c5:96:a9:97:34:5c:9b:db", # cocotte-fleet + ] +} + +variable "name" { + type = string + default = "com.uvlava.ct.services" +} diff --git a/versions.tf b/versions.tf new file mode 100644 index 0000000..158de68 --- /dev/null +++ b/versions.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">= 1.5" + required_providers { + digitalocean = { + source = "digitalocean/digitalocean" + version = "~> 2.40" + } + } +} + +provider "digitalocean" { + token = var.do_token +}