uvlava/terraform/do/README.md
Natalie a379326caa docs(infra): update README, outputs for new pypi/swift services on ct-forge DO droplet
- Document pypi.ct, swift.ct in ct_infra_hosts output.
- Update README table and follow-ups for the new publish registries (verdaccio, pypi via pypiserver, swift via forgejo) on the forge droplet.
- All under TF IaC (cloud-init compose, dns, firewall).
2026-06-28 17:46:07 -04:00

6.9 KiB

DO store tier — IaC + live state

DigitalOcean store/backend tier that replaces the dead homelan host black. Adult content is served only from 1984.is / vps-0; DO holds durable state and never has a public hostname for the content surface. Full rationale + homelan→cloud mapping: ../../../.claude/plans / the recovery plan and ../../../docs/EDGE_ISLAND_MODE.md.

Account / project

  • DO account: ctTransQuinnFTW@pm.me (active, verified). PAT at ~/.vault/do-pat-ct.token. (Separate mc account/PAT also exists: ~/.vault/do-pat-mc.token-style — do not mix.)
  • Project: ct:prod (ed8cdfb7-f6eb-4f92-a44e-2a03627d5baa). All rebuild resources are grouped here.
  • Region: nyc3 (operator NYC-local). GDPR residency caveat for EU-subject PII is open — see the plan.

Live now (ct-forge / cocotte-forge droplet — now under Terraform)

The cocotte-forge droplet (aliased lilith-forge) that hosts Forgejo (the new git origin) + Verdaccio (the private @lilith/* npm registry, required for publishing updated versions of things like @lilith/mac-sync-client with phone/call support) is now a managed digitalocean_droplet.forge resource.

DNS (forge.ct.uvlava.com, npm.ct.uvlava.com, apex) is driven from its IPv4 address.

If the droplet already exists from the out-of-band provisioning, import it (and optionally the matching cloud firewall):

terraform import digitalocean_droplet.forge <id-from-do-console-or-state>  # e.g. 580675125
# Optional: terraform import digitalocean_firewall.forge lilith-forge-fw

After import + apply, the DNS records (forge.ct, npm.ct, apex) will be driven from the resource IP, and the firewall resource will manage the rules. The existing cloud fw (if not imported) may need manual cleanup or rename to avoid conflicts.

(See the "Live now" table in the previous version of this README for the id 580675125 and current IP if you need to cross-reference before import.)

Thing Value
Droplet (TF resource) cocotte-forge (digitalocean_droplet.forge), s-1vcpu-2gb, nyc3
Public IP digitalocean_droplet.forge.ipv4_address (was 134.199.243.61)
Forgejo (git, new origin) http://:3000 (or forge.ct.uvlava.com once Caddy) · git-ssh :2222
Verdaccio (@lilith/* npm) http://:4873 (or https://npm.ct.uvlava.com once TLS)
PyPI (Python packages, new service) http://:8080 (or https://pypi.ct.uvlava.com once TLS) — pypiserver
Swift (Swift packages via Forgejo registry, new) https://forge.ct.uvlava.com/api/packages//swift (or https://swift.ct.uvlava.com once TLS)
Cloud firewall digitalocean_firewall.forge (lilith-forge-fw) — inbound 22/2222/3000/4873/8080/8081 from admin_ips + vps-0/plum
Forgejo admin user quinn — password at ~/.vault/forge-admin-quinn.password
Forgejo API token ~/.vault/forge-admin-quinn.api-token
Repo quinn/lilith-platform.live (private)

Git remote on plum: forgessh://git@134.199.243.61:2222/quinn/lilith-platform.live.git (push with GIT_SSH_COMMAND="ssh -i ~/.ssh/id_ed25519_1984").

Cloud-init that builds this box: cloud-init/forge.yaml (keep it pure-ASCII — em-dashes break cloud-init's early YAML parse). The droplet resource now uses it directly.

Follow-ups for the forge box

  • TLS (Caddy) in front of Forgejo/Verdaccio/PyPI (see the yaml comment); then move git to HTTPS or keep SSH. swift.ct proxies to Forgejo.
  • Join it to the wg1 mesh (update cloud-init or post-provision) and drop the public 3000/4873/8080 exposure (use the backend mesh + firewall).
  • Repoint repo origin + registry refs + @lilith .npmrc / PyPI config to the ct-forge services (Verdaccio for npm, pypiserver for PyPI, Forgejo for Swift). Update consumers and publish flows (from ct-forge CI runners on DO).
  • Bring the lilith-forge-fw under TF as digitalocean_firewall.forge (inbound rules for 22/2222/3000/4873/8080/8081 from admin IPs + vps-0 + plum).
  • (Optional) make the forge droplet join the same VPC as backend for private paths.
  • Add forge:setup-registries or post-up hook in cocottetech scripts if needed for service bootstrap (compose now includes them via TF cloud-init).

Terraform (store tier — written, NOT yet applied)

The .tf files describe the full store tier (Managed PG + Spaces + backend droplet + WG peer + GPU). They are intentionally un-applied until the GDPR region call and PG sizing are settled. Apply gates on a verified account (done) and registered SSH keys.

cd ~/Code/@projects/uvlava/terraform/do   # IaC moved out of the v2 tree into the uvlava infranet repo
export TF_VAR_do_token="$(cat ~/.vault/do-pat-ct.token)"
export TF_VAR_spaces_access_id="…"      # API → Spaces Keys
export TF_VAR_spaces_secret_key="…"
cp terraform.tfvars.example terraform.tfvars   # fill ssh_key_fingerprints, admin_ips
terraform init
terraform plan
File What
versions.tf provider pin + (commented) Spaces remote backend
variables.tf all inputs; GPU vars gated (account not GPU-allowlisted yet)
network.tf VPC, project, firewall (WG+SSH only), reserved IP
database.tf Managed PG (private VPC, trusted-sources = droplet only)
spaces.tf private media bucket + deny-public policy + CDN
droplet.tf backend droplet + optional GPU droplet
cloud-init/backend.yaml backend bootstrap (WG self-keygen, ufw, pgBouncer)
outputs.tf private PG URI, Spaces endpoint, WG address (sensitive)

GPU note: this account returns zero gpu-* sizes — DO gates GPU droplets behind an access request. gpu_enabled=false until granted (hybrid inference 12b/12c). Serverless inference (12a) is unaffected.

Forge/Verdaccio/PyPI/Swift (ct-forge) transition (2026-06-28): The cocotte-forge droplet (now digitalocean_droplet.forge in this TF) hosts Forgejo + Verdaccio + pypiserver (new PyPI service) + Swift registry support (via Forgejo packages) live at the bare IP. uvlava.com DNS (forge.ct.uvlava.com, npm.ct.uvlava.com, pypi.ct.uvlava.com, swift.ct.uvlava.com) + A records are in dns.tf (and the forge droplet + firewall resources added), but the domains are NOT LIVE YET (registrar joker.com NS delegation for uvlava.com to DO nameservers is pending). Old forge.black.lan / npm.black.lan are dead. Use the IP for git pushes, publishing to the new services (Verdaccio 4873 for npm @lilith/*, pypiserver 8080 for PyPI, Forgejo API for Swift), until delegation + Caddy/LE TLS is live on the ct domains (see cloud-init/forge.yaml which now includes pypiserver and updated Caddy). Update scripts/.npmrc/pypirc etc once live. The push scripts and platform infra handle the current IP. New services on the forge droplet for publishing packages (no more black).

Secrets

None live in this tree. All under ~/.vault/ (0600). .gitignore blocks *.tfstate, *.tfvars, .terraform/.