fix(cloud-dx): repoint forge from dead mc-forge droplet to live forge.mc.uvlava.com
Some checks are pending
ci / regression gate (push) Waiting to run

The dedicated mc-forge droplet (159.203.170.249:3000/mcadmin) is gone; the forge
now rides a shared services box, addressed by the stable hostname
forge.mc.uvlava.com/applications. The cloud-DX toolchain still pointed at the dead
endpoint, so every worker clone + golden-image build was broken.

- scripts/lib/forge-remote.sh: single source of truth — builds the authenticated
  clone URL from the hostname + ~/.vault/services-forge-token (relocation-proof;
  no hardcoded IP). Exports MC_FORGE_GIT_REMOTE.
- cloud-bringup.sh / dist.sh: source the helper instead of the dead
  mc_forge_creds + 159.203 URL. Also fix cloud-bringup REPO path to the current
  @mc/@applications/magicciv location.
- settings.local.json autoMode trust block: name the new forge host + 'mc' DO
  project (was 159.203 + 'mc:dev'), else cloud provisioning is denied as exfil.
- cloud-dx-do.md: document the new forge + token.

Verified: helper authenticates to the live forge (ls-remote main); scripts parse;
JSON valid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-30 01:39:54 -04:00
parent dfd87b87d3
commit ab8fd4d707
5 changed files with 50 additions and 15 deletions

View file

@ -13,17 +13,17 @@
# Reads all secrets from ~/.vault/ — nothing sensitive is hardcoded here. # Reads all secrets from ~/.vault/ — nothing sensitive is hardcoded here.
set -uo pipefail set -uo pipefail
REPO="$HOME/Code/@projects/@magic-civilization" REPO="$HOME/Code/@mc/@applications/magicciv"
cd "$REPO" || exit 1 cd "$REPO" || exit 1
# --- auth (from vault) --- # --- auth (from vault) ---
export DIGITALOCEAN_TOKEN; DIGITALOCEAN_TOKEN="$(cat ~/.vault/do_pat_mc)" export DIGITALOCEAN_TOKEN; DIGITALOCEAN_TOKEN="$(cat ~/.vault/do_pat_mc)"
export TF_VAR_do_token="$DIGITALOCEAN_TOKEN" export TF_VAR_do_token="$DIGITALOCEAN_TOKEN"
# shellcheck disable=SC1090 # Forge clone URL (hostname + services token) — single source of truth.
. ~/.vault/mc_forge_creds # FORGE_IP ADMIN_USER ADMIN_PASS ... # shellcheck disable=SC1091
GITR="http://${ADMIN_USER}:${ADMIN_PASS}@${FORGE_IP}:3000/mcadmin/magicciv.git" . "$REPO/scripts/lib/forge-remote.sh" || { echo "!!! forge-remote.sh failed (no token?)"; exit 1; }
export TF_VAR_git_remote="$GITR" # workers pull latest from the forge export TF_VAR_git_remote="$MC_FORGE_GIT_REMOTE" # workers pull latest from the forge
export PKR_VAR_git_remote="$GITR" # packer reads the creds from env, not argv 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 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 # 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 ssh-add ~/.ssh/id_mc_fleet 2>/dev/null || true # so the dispatch ssh (mc@worker) authenticates

View file

@ -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

View file

@ -88,7 +88,7 @@ cmd_dist_image() {
# (Re)build the golden image. INCREMENTAL by default: builds FROM the newest # (Re)build the golden image. INCREMENTAL by default: builds FROM the newest
# mc-golden snapshot, so provision.sh (idempotent) only redoes changed work # mc-golden snapshot, so provision.sh (idempotent) only redoes changed work
# (~3-8 min). --cold builds from stock Ubuntu (~20 min) — resets accumulated # (~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 local cold=false a
for a in "$@"; do [ "$a" = "--cold" ] && cold=true; done for a in "$@"; do [ "$a" = "--cold" ] && cold=true; done
local root pat local root pat
@ -96,9 +96,10 @@ cmd_dist_image() {
pat="$(cat ~/.vault/do_pat_mc 2>/dev/null)" pat="$(cat ~/.vault/do_pat_mc 2>/dev/null)"
[ -n "$pat" ] || { echo "no ~/.vault/do_pat_mc" >&2; return 1; } [ -n "$pat" ] || { echo "no ~/.vault/do_pat_mc" >&2; return 1; }
export DIGITALOCEAN_TOKEN="$pat" export DIGITALOCEAN_TOKEN="$pat"
# shellcheck disable=SC1090 # Forge clone URL (hostname + services token) — single source of truth.
. ~/.vault/mc_forge_creds # shellcheck disable=SC1091
export PKR_VAR_git_remote="http://${ADMIN_USER}:${ADMIN_PASS}@${FORGE_IP}:3000/mcadmin/magicciv.git" . "$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 PKR_VAR_fleet_pubkey="$(cat ~/.ssh/id_mc_fleet.pub)"; export PKR_VAR_fleet_pubkey
local base="ubuntu-24-04-x64" prev local base="ubuntu-24-04-x64" prev
if ! $cold; then if ! $cold; then

View file

@ -24,10 +24,10 @@
## Standing setup (already built — proven 2026-06-27) ## 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`. - **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. - **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, mc_forge_creds, do-spaces-uvlava.access, do-spaces-uvlava.secret}` (600). Key `~/.ssh/id_mc_fleet` (DO key `mc-fleet`). - **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/<sha>/` holds the prebuilt linux `.so`+wasm; `models/<run>/` 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). - **Artifact Space**: `magicciv-artifacts` (DO Spaces, nyc3) — `builds/<sha>/` holds the prebuilt linux `.so`+wasm; `models/<run>/` 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) ## Build once, load many (the artifact Space)

View file

@ -69,11 +69,11 @@
"autoMode": { "autoMode": {
"environment": [ "environment": [
"$defaults", "$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": [ "allow": [
"$defaults", "$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."
] ]
} }
} }