From 113da4df4e52b8ce3ba7e5f42b277cac6a66d490 Mon Sep 17 00:00:00 2001 From: Joshua Date: Thu, 19 Feb 2026 09:46:43 -0800 Subject: [PATCH] chore: cut over repo tooling to git.edut.dev --- README.md | 2 +- .../edut-dev-infra-cutover-checklist.md | 328 ++++++++++++++++++ docs/repo-split-publish-runbook.md | 14 +- scripts/publish_split_repos.sh | 2 +- 4 files changed, 337 insertions(+), 9 deletions(-) create mode 100644 docs/deployment/edut-dev-infra-cutover-checklist.md diff --git a/README.md b/README.md index 0eb77e6..3e531c4 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ README.md ## Scripts -1. `scripts/publish_split_repos.sh` - creates/pushes `launcher`, `governance`, `contracts` repos using either a provided Gitea PAT or git credential-helper auth for `git.workvsg.com`. +1. `scripts/publish_split_repos.sh` - creates/pushes `launcher`, `governance`, `contracts` repos using either a provided Gitea PAT or git credential-helper auth for `git.edut.dev`. ## Internationalization diff --git a/docs/deployment/edut-dev-infra-cutover-checklist.md b/docs/deployment/edut-dev-infra-cutover-checklist.md new file mode 100644 index 0000000..70689f5 --- /dev/null +++ b/docs/deployment/edut-dev-infra-cutover-checklist.md @@ -0,0 +1,328 @@ +# EDUT Dev Infra Cutover Checklist + +This checklist migrates EDUT infrastructure from `git.workvsg.com` to `git.edut.dev` with deterministic validation gates. + +Server target: + +- Host: `edut-prod` +- IP: `5.78.148.229` +- OS: Ubuntu 24.04 LTS + +## Preconditions + +1. DNS records exist and point to the new server: + - `edut.dev` + - `www.edut.dev` + - `api.edut.dev` + - `git.edut.dev` +2. SSH key access as root is available. +3. Cloudflare proxy mode and SSL mode are configured to allow origin TLS. +4. Local private keys for `joshua`, `claude-code`, `codex` are available for validation. + +## Phase 1 - Server Setup + +### 1. Baseline inventory + +```bash +ssh -i ~/.ssh/edut_codex root@5.78.148.229 \ + "hostnamectl --static; cat /etc/os-release | head -n 6; ufw status verbose; docker --version || true" +``` + +Gate: + +1. Host is reachable. +2. OS is Ubuntu 24.04. +3. State is understood before changes. + +### 2. Create Linux users and key-only SSH + +Create users: + +```bash +ssh -i ~/.ssh/edut_codex root@5.78.148.229 \ + "useradd -m -s /bin/bash joshua || true; useradd -m -s /bin/bash claude-code || true; useradd -m -s /bin/bash codex || true" +``` + +Install authorized keys: + +```bash +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "install -d -m 700 /home/joshua/.ssh /home/claude-code/.ssh /home/codex/.ssh" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "printf '%s\n' '' > /home/joshua/.ssh/authorized_keys" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "printf '%s\n' '' > /home/claude-code/.ssh/authorized_keys" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "printf '%s\n' '' > /home/codex/.ssh/authorized_keys" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 \ + "chmod 600 /home/*/.ssh/authorized_keys; chown -R joshua:joshua /home/joshua/.ssh; chown -R claude-code:claude-code /home/claude-code/.ssh; chown -R codex:codex /home/codex/.ssh" +``` + +Gate: + +1. All three users exist. +2. Authorized keys are present with correct perms. + +### 3. Sudo policy + +```bash +ssh -i ~/.ssh/edut_codex root@5.78.148.229 \ + "usermod -aG sudo joshua; usermod -aG sudo claude-code; usermod -aG sudo codex" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 \ + "printf '%s\n' 'claude-code ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-claude-code" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 \ + "printf '%s\n' 'codex ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-codex" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "chmod 440 /etc/sudoers.d/90-claude-code /etc/sudoers.d/90-codex" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "visudo -cf /etc/sudoers" +``` + +Gate: + +1. `visudo -cf` passes. +2. `claude-code` and `codex` have passwordless sudo. +3. `joshua` remains standard sudo (password required). + +### 4. SSH hardening + disable root login + +```bash +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "cat >/etc/ssh/sshd_config.d/99-edut-hardening.conf <<'EOF' +PermitRootLogin no +PasswordAuthentication no +KbdInteractiveAuthentication no +ChallengeResponseAuthentication no +PubkeyAuthentication yes +PermitEmptyPasswords no +EOF" +ssh -i ~/.ssh/edut_codex root@5.78.148.229 "sshd -t && systemctl restart ssh" +``` + +Validation: + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo -n true && echo codex-sudo-ok" +ssh -i ~/.ssh/edut_joshua joshua@5.78.148.229 "id" +ssh -i ~/.ssh/edut_codex -o BatchMode=yes root@5.78.148.229 "echo should-fail" +``` + +Gate: + +1. `codex` login works and sudo works. +2. `joshua` key login works. +3. root login is denied. + +### 5. Firewall, fail2ban, unattended upgrades + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get update" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get install -y ufw fail2ban unattended-upgrades apt-listchanges" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo ufw default deny incoming && sudo ufw default allow outgoing" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo ufw allow OpenSSH && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo ufw --force enable" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo systemctl enable --now fail2ban" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo dpkg-reconfigure -f noninteractive unattended-upgrades" +``` + +Gate: + +1. `ufw status verbose` shows only `22`, `80`, `443`. +2. `fail2ban` active. +3. unattended upgrades enabled. + +### 6. Install Docker + Compose plugin + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get install -y ca-certificates curl gnupg" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo install -m 0755 -d /etc/apt/keyrings" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo chmod a+r /etc/apt/keyrings/docker.gpg" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \$(. /etc/os-release && echo \$VERSION_CODENAME) stable\" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo systemctl enable --now docker" +``` + +Gate: + +1. `docker --version` returns success. +2. `docker compose version` returns success. + +### 7. Deploy Gitea + TLS on git.edut.dev + +Provision directories: + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo install -d -m 755 /opt/edut/gitea/{gitea,postgres,caddy}" +``` + +Deploy stack (Gitea + Postgres + Caddy reverse proxy with auto TLS): + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo tee /opt/edut/gitea/docker-compose.yml >/dev/null <<'EOF' +services: + db: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: gitea + POSTGRES_USER: gitea + POSTGRES_PASSWORD: CHANGE_ME_DB_PASSWORD + volumes: + - /opt/edut/gitea/postgres:/var/lib/postgresql/data + networks: [gitea_net] + + gitea: + image: gitea/gitea:1.22 + restart: unless-stopped + environment: + USER_UID: "1000" + USER_GID: "1000" + GITEA__database__DB_TYPE: postgres + GITEA__database__HOST: db:5432 + GITEA__database__NAME: gitea + GITEA__database__USER: gitea + GITEA__database__PASSWD: CHANGE_ME_DB_PASSWORD + GITEA__server__ROOT_URL: https://git.edut.dev/ + GITEA__server__DOMAIN: git.edut.dev + GITEA__server__HTTP_ADDR: 0.0.0.0 + GITEA__server__HTTP_PORT: 3000 + GITEA__security__INSTALL_LOCK: "true" + GITEA__service__DISABLE_REGISTRATION: "true" + volumes: + - /opt/edut/gitea/gitea:/data + depends_on: [db] + networks: [gitea_net] + + caddy: + image: caddy:2 + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - /opt/edut/gitea/caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - /opt/edut/gitea/caddy/data:/data + - /opt/edut/gitea/caddy/config:/config + depends_on: [gitea] + networks: [gitea_net] + +networks: + gitea_net: +EOF" +``` + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo tee /opt/edut/gitea/caddy/Caddyfile >/dev/null <<'EOF' +git.edut.dev { + reverse_proxy gitea:3000 +} +EOF" +``` + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "cd /opt/edut/gitea && sudo docker compose up -d" +``` + +Gate: + +1. `docker compose ps` shows healthy containers. +2. `curl -I https://git.edut.dev` returns `200/302`. +3. Browser shows valid TLS for `git.edut.dev`. + +### 8. Bootstrap org and repos + +Create admin and org: + +```bash +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo docker exec -u git gitea-gitea-1 gitea admin user create --username joshua --password 'CHANGE_ME_ADMIN_PASSWORD' --email j@edut.dev --admin --must-change-password=false" +ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo docker exec -u git gitea-gitea-1 gitea admin org create --name edut --username joshua || true" +``` + +Create repos: + +```bash +for r in web launcher contracts governance kernel platform-docs; do + curl -u "joshua:CHANGE_ME_ADMIN_PASSWORD" \ + -H "Content-Type: application/json" \ + -X POST "https://git.edut.dev/api/v1/orgs/edut/repos" \ + -d "{\"name\":\"$r\",\"private\":true}" +done +``` + +Gate: + +1. `https://git.edut.dev/edut` lists all repos. + +## Phase 2 - Migration + +### 9. Mirror push histories + +```bash +for r in web launcher contracts governance kernel; do + git -C "/Users/vsg/Documents/VSG Codex/$r" push --mirror "https://git.edut.dev/edut/$r.git" +done +git -C "/Users/vsg/Documents/VSG Codex/platform-docs" push --mirror "https://git.edut.dev/edut/platform-docs.git" +``` + +Gate: + +1. Branches/tags match source repos. + +### 10. Update local remotes + +```bash +for r in web launcher contracts governance kernel platform-docs; do + git -C "/Users/vsg/Documents/VSG Codex/$r" remote set-url origin "https://git.edut.dev/edut/$r.git" +done +``` + +Gate: + +1. `git remote -v` points to `git.edut.dev` for all repos. + +### 11. Update hardcoded host references + +Search: + +```bash +rg -n "git\\.workvsg\\.com" /Users/vsg/Documents/VSG\ Codex +``` + +Replace references in scripts/docs/default flags/manifests. + +Gate: + +1. No remaining production references to `git.workvsg.com`. + +### 12. Freeze old host read-only + +Set old Gitea org/repo archival or remove write access. + +Gate: + +1. No new writes possible on old host. + +## Phase 3 - Verification + +### 13. Smoke checks + +1. Push test branch to each repo. +2. Open PR in each repo. +3. Verify Gitea workflow runs. +4. Create and push lightweight tag. +5. Validate clone/fetch from clean directory. + +Gate: + +1. All checks pass with new host only. + +### 14. Deploy path verification + +1. Ensure `api.edut.dev` deployment docs/scripts reference new Git host where needed. +2. Validate deployment artifact retrieval paths. + +Gate: + +1. No deployment path depends on old host. + +## Phase 4 - Semantic Sweep + +Only after all previous gates pass: + +1. Execute naming/commercial migration sweep. +2. Enforce drift checks in CI. +3. Cut release and decommission old host after soak. diff --git a/docs/repo-split-publish-runbook.md b/docs/repo-split-publish-runbook.md index 1d93f30..62e8cf7 100644 --- a/docs/repo-split-publish-runbook.md +++ b/docs/repo-split-publish-runbook.md @@ -18,7 +18,7 @@ Example (replace ``): ```bash curl -fsSL -H "Authorization: token " -H "Content-Type: application/json" \ - -X POST "https://git.workvsg.com/api/v1/orgs/edut/repos" \ + -X POST "https://git.edut.dev/api/v1/orgs/edut/repos" \ -d '{"name":"launcher","private":true,"description":"EDUT free launcher shell and wallet onboarding app"}' ``` @@ -31,7 +31,7 @@ cd "/Users/vsg/Documents/VSG Codex/web" ./scripts/publish_split_repos.sh ``` -Or run it without arguments to use git credential helper auth for `git.workvsg.com`: +Or run it without arguments to use git credential helper auth for `git.edut.dev`: ```bash cd "/Users/vsg/Documents/VSG Codex/web" @@ -42,15 +42,15 @@ cd "/Users/vsg/Documents/VSG Codex/web" ```bash cd "/Users/vsg/Documents/VSG Codex/launcher" -git remote add origin https://git.workvsg.com/edut/launcher.git +git remote add origin https://git.edut.dev/edut/launcher.git git push -u origin main cd "/Users/vsg/Documents/VSG Codex/governance" -git remote add origin https://git.workvsg.com/edut/governance.git +git remote add origin https://git.edut.dev/edut/governance.git git push -u origin main cd "/Users/vsg/Documents/VSG Codex/contracts" -git remote add origin https://git.workvsg.com/edut/contracts.git +git remote add origin https://git.edut.dev/edut/contracts.git git push -u origin main ``` @@ -61,6 +61,6 @@ git push -u origin main 3. Add cross-repo links in each README. 4. Lock branch protections and required-review settings. -## Current Blocker +## Current Status -Gitea API creation attempts with current credential material return `401 invalid username/password/token`. +Remote repos exist on `git.edut.dev` and this runbook remains the deterministic recovery path. diff --git a/scripts/publish_split_repos.sh b/scripts/publish_split_repos.sh index 1b2a07e..135b0f8 100755 --- a/scripts/publish_split_repos.sh +++ b/scripts/publish_split_repos.sh @@ -8,7 +8,7 @@ usage() { TOKEN="${1:-}" ORG="${2:-edut}" -HOST="${3:-git.workvsg.com}" +HOST="${3:-git.edut.dev}" ROOT="/Users/vsg/Documents/VSG Codex" AUTH_MODE="" AUTH_USERNAME=""