diff --git a/scripts/cloud-bringup.sh b/scripts/cloud-bringup.sh index 14dd2c7f..ae6421d2 100644 --- a/scripts/cloud-bringup.sh +++ b/scripts/cloud-bringup.sh @@ -13,17 +13,17 @@ # Reads all secrets from ~/.vault/ — nothing sensitive is hardcoded here. set -uo pipefail -REPO="$HOME/Code/@projects/@magic-civilization" +REPO="$HOME/Code/@mc/@applications/magicciv" cd "$REPO" || exit 1 # --- auth (from vault) --- export DIGITALOCEAN_TOKEN; DIGITALOCEAN_TOKEN="$(cat ~/.vault/do_pat_mc)" export TF_VAR_do_token="$DIGITALOCEAN_TOKEN" -# shellcheck disable=SC1090 -. ~/.vault/mc_forge_creds # FORGE_IP ADMIN_USER ADMIN_PASS ... -GITR="http://${ADMIN_USER}:${ADMIN_PASS}@${FORGE_IP}:3000/mcadmin/magicciv.git" -export TF_VAR_git_remote="$GITR" # workers pull latest from the forge -export PKR_VAR_git_remote="$GITR" # packer reads the creds from env, not argv +# Forge clone URL (hostname + services token) — single source of truth. +# shellcheck disable=SC1091 +. "$REPO/scripts/lib/forge-remote.sh" || { echo "!!! forge-remote.sh failed (no token?)"; exit 1; } +export TF_VAR_git_remote="$MC_FORGE_GIT_REMOTE" # workers pull latest from the forge +export PKR_VAR_git_remote="$MC_FORGE_GIT_REMOTE" # packer reads the creds from env, not argv PKR_VAR_fleet_pubkey="$(cat ~/.ssh/id_mc_fleet.pub)"; export PKR_VAR_fleet_pubkey # baked into worker authorized_keys # fleet reuses the pre-registered DO key 'mc-fleet' (var ssh_key_name default); just load its private half ssh-add ~/.ssh/id_mc_fleet 2>/dev/null || true # so the dispatch ssh (mc@worker) authenticates diff --git a/scripts/lib/forge-remote.sh b/scripts/lib/forge-remote.sh new file mode 100644 index 00000000..be1c2379 --- /dev/null +++ b/scripts/lib/forge-remote.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Single source of truth for the MC forge git remote used to clone this repo onto +# cloud build/worker boxes. SOURCE it (it `return`s); it exports MC_FORGE_GIT_REMOTE. +# +# Uses the stable HOSTNAME (forge.mc.uvlava.com), never a hardcoded IP — the forge +# is no longer its own droplet, it rides a shared services box and can be moved +# between hosts; the DNS name is the contract, an IP is not. (Old dead endpoint +# was 159.203.170.249:3000/mcadmin — gone.) +# +# Auth = the services forge token (read-only clone is all a worker needs). The +# token is injected into the URL in-process only; callers pass MC_FORGE_GIT_REMOTE +# via PKR_VAR_*/TF_VAR_* ENV (never on argv), per cloud-dx-do.md's creds rule. +# +# Overridable for testing: MC_FORGE_HOST, MC_FORGE_ORG, MC_FORGE_TOKEN_FILE. + +: "${MC_FORGE_HOST:=forge.mc.uvlava.com}" +: "${MC_FORGE_ORG:=applications}" +: "${MC_FORGE_TOKEN_FILE:=$HOME/.vault/services-forge-token}" + +if [ ! -r "$MC_FORGE_TOKEN_FILE" ]; then + echo "forge-remote: no forge token at $MC_FORGE_TOKEN_FILE" >&2 + return 1 2>/dev/null || exit 1 +fi + +_mc_forge_token="$(cat "$MC_FORGE_TOKEN_FILE")" +if [ -z "$_mc_forge_token" ]; then + echo "forge-remote: forge token file is empty: $MC_FORGE_TOKEN_FILE" >&2 + unset _mc_forge_token + return 1 2>/dev/null || exit 1 +fi + +# Gitea accepts the token as the basic-auth password with user "oauth2". +export MC_FORGE_GIT_REMOTE="https://oauth2:${_mc_forge_token}@${MC_FORGE_HOST}/${MC_FORGE_ORG}/magicciv.git" +unset _mc_forge_token diff --git a/scripts/run/dist.sh b/scripts/run/dist.sh index b9dbbd8d..89c18b9b 100755 --- a/scripts/run/dist.sh +++ b/scripts/run/dist.sh @@ -88,7 +88,7 @@ cmd_dist_image() { # (Re)build the golden image. INCREMENTAL by default: builds FROM the newest # mc-golden snapshot, so provision.sh (idempotent) only redoes changed work # (~3-8 min). --cold builds from stock Ubuntu (~20 min) — resets accumulated - # layer cruft; run occasionally. Needs ~/.vault/{do_pat_mc,mc_forge_creds}. + # layer cruft; run occasionally. Needs ~/.vault/{do_pat_mc,services-forge-token}. local cold=false a for a in "$@"; do [ "$a" = "--cold" ] && cold=true; done local root pat @@ -96,9 +96,10 @@ cmd_dist_image() { pat="$(cat ~/.vault/do_pat_mc 2>/dev/null)" [ -n "$pat" ] || { echo "no ~/.vault/do_pat_mc" >&2; return 1; } export DIGITALOCEAN_TOKEN="$pat" - # shellcheck disable=SC1090 - . ~/.vault/mc_forge_creds - export PKR_VAR_git_remote="http://${ADMIN_USER}:${ADMIN_PASS}@${FORGE_IP}:3000/mcadmin/magicciv.git" + # Forge clone URL (hostname + services token) — single source of truth. + # shellcheck disable=SC1091 + . "$root/scripts/lib/forge-remote.sh" || { echo "forge-remote.sh failed (no token?)" >&2; return 1; } + export PKR_VAR_git_remote="$MC_FORGE_GIT_REMOTE" PKR_VAR_fleet_pubkey="$(cat ~/.ssh/id_mc_fleet.pub)"; export PKR_VAR_fleet_pubkey local base="ubuntu-24-04-x64" prev if ! $cold; then diff --git a/tooling/claude/dot-claude/instructions/cloud-dx-do.md b/tooling/claude/dot-claude/instructions/cloud-dx-do.md index 61939c17..1913a2bb 100644 --- a/tooling/claude/dot-claude/instructions/cloud-dx-do.md +++ b/tooling/claude/dot-claude/instructions/cloud-dx-do.md @@ -24,10 +24,10 @@ ## Standing setup (already built — proven 2026-06-27) -- **Forge**: `mc-forge` droplet running Forgejo; repo `mcadmin/magicciv`; IP + admin creds in `~/.vault/mc_forge_creds`. +- **Forge**: Gitea at `forge.mc.uvlava.com` (no longer its own droplet — rides a shared services box; address by hostname, never IP). Repo `applications/magicciv`. Clone token in `~/.vault/services-forge-token`; the clone URL is built once by `scripts/lib/forge-remote.sh` (exports `MC_FORGE_GIT_REMOTE`). Old `mcadmin@159.203.170.249:3000` + `mc_forge_creds` are DEAD. - **Golden image**: Packer `infra/packer/`, auto-discovered by the fleet (snapshot name prefix `mc-golden`). Bakes: toolchain (via `scripts/dev-setup/linux.sh`) + prebuilt GDExtension `.so` + warm Godot import + **weston/Mesa render stack** + **mold + sccache** build accelerators + the fleet ssh key in `mc`'s `authorized_keys`. -- **Fleet TF**: `infra/terraform/test-fleet/` — DO provider, golden-image data-source discovery, grouped under the `mc:dev` DO project, mocked-provider test suite. -- **Secrets**: `~/.vault/{do_pat_mc, mc_forge_creds, do-spaces-uvlava.access, do-spaces-uvlava.secret}` (600). Key `~/.ssh/id_mc_fleet` (DO key `mc-fleet`). +- **Fleet TF**: `infra/terraform/test-fleet/` — DO provider, golden-image data-source discovery, grouped under the `mc` DO project, mocked-provider test suite. +- **Secrets**: `~/.vault/{do_pat_mc, services-forge-token, do-spaces-uvlava.access, do-spaces-uvlava.secret}` (600). Key `~/.ssh/id_mc_fleet` (DO key `mc-fleet`). - **Artifact Space**: `magicciv-artifacts` (DO Spaces, nyc3) — `builds//` holds the prebuilt linux `.so`+wasm; `models//` holds shared RL `.onnx`. Account already pays the Spaces subscription (for `lilith-quinn-media`), so this Space adds ~$0 base. Workers carry `rclone` (baked by `provision.sh`); the dispatch passes the Spaces creds as `RCLONE_S3_*` env over ssh (never stored on the worker, never on argv). ## Build once, load many (the artifact Space) diff --git a/tooling/claude/dot-claude/settings.local.json b/tooling/claude/dot-claude/settings.local.json index a6398337..75414788 100644 --- a/tooling/claude/dot-claude/settings.local.json +++ b/tooling/claude/dot-claude/settings.local.json @@ -69,11 +69,11 @@ "autoMode": { "environment": [ "$defaults", - "The self-hosted Forgejo at 159.203.170.249:3000 (droplet 'mc-forge', DigitalOcean project 'mc:dev') is the repo owner's OWN forge holding this project's source. DigitalOcean droplets in the 'mc:dev' project are the owner's own build/test/render infrastructure. Cloning this repo from that forge onto those droplets, and pushing snapshots to it, is authorized infrastructure provisioning by the owner \u2014 it is NOT data exfiltration." + "The self-hosted Gitea forge at forge.mc.uvlava.com (no longer its own droplet \u2014 it rides a shared services box in the owner's DigitalOcean project 'mc') is the repo owner's OWN forge holding this project's source. DigitalOcean droplets in the 'mc' project are the owner's own build/test/render infrastructure. Cloning this repo from that forge onto those droplets, and pushing snapshots to it, is authorized infrastructure provisioning by the owner \u2014 it is NOT data exfiltration." ], "allow": [ "$defaults", - "git/packer/terraform operations moving THIS project's source between the owner's machine, the mc-forge Forgejo (159.203.170.249), and DigitalOcean droplets in the mc:dev project are authorized provisioning, not exfiltration." + "git/packer/terraform operations moving THIS project's source between the owner's machine, the forge at forge.mc.uvlava.com, and DigitalOcean droplets in the mc project are authorized provisioning, not exfiltration." ] } }