docs(deploy): correct stale infra facts after GCP name-alignment

The deploy docs predated the GCP name-alignment and described
infrastructure that no longer exists. Discovered during the v0.31.4
deploy. Corrections:

- Project wiggleverse-rfc -> wiggleverse-ohm; VM rfc-app -> ohm-rfc-app;
  install path /opt/rfc-app -> /opt/ohm-rfc-app; system user + service
  rfc-app -> ohm-rfc-app; external IP 34.132.29.41 -> 136.116.40.66.
- SSH is now IAP-only (direct port 22 times out): document
  --tunnel-through-iap on every gcloud compute ssh.
- Meta repo wiggleverse/meta -> wiggleverse/ohm-content.
- Two-remote reality: the VM's git origin is git.benstull.org/benstull/
  rfc-app, a SEPARATE Gitea from the release one (git.wiggleverse.org)
  that does not auto-mirror — so a release must be pushed to the
  benstull remote before the VM can fetch it. The VM tracks a detached
  release TAG, not a branch.
- Frontend-only changes are live once dist/ is rebuilt (nginx serves it
  directly); pip install only when requirements.txt changed.

Docs-only; no version bump (cf. the docs-only commit after v0.31.3).
The First-Time Deployment blocks keep the structural commands with a
substitution note rather than unvalidated rewrites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-01 10:15:58 -07:00
parent 551d240967
commit 714c2aed86
2 changed files with 124 additions and 40 deletions
+79 -30
View File
@@ -20,15 +20,24 @@ The v1 build is complete (8 slices shipped, 125 passing integration tests). New
The RFC app runs on its own dedicated GCP VM in a separate project from the Gitea VM. The two coexist under `wiggleverse.org` but are otherwise unrelated infrastructure.
> **⚠️ Infrastructure was realigned (GCP name-alignment, ~2026-05).** The
> GCP project, VM, install path, system user, and systemd unit were all
> renamed, the static IP changed, and SSH is now **IAP-only** (direct
> port-22 connections time out). The tables below are the current truth;
> if you find an older clone of this doc naming `wiggleverse-rfc` /
> `rfc-app` / `/opt/rfc-app` / `34.132.29.41`, it predates the alignment.
| Property | Value |
|----------|-------|
| GCP project | `wiggleverse-rfc` |
| VM name | `rfc-app` |
| GCP project | `wiggleverse-ohm` |
| VM name | `ohm-rfc-app` |
| VM type | e2-small |
| Zone | us-central1-a |
| OS | Debian 12 (bookworm) |
| Static IP | 34.132.29.41 |
| Linux user (OS Login) | `benstull` |
| External IP | `136.116.40.66` |
| SSH | **IAP-only**`gcloud compute ssh … --tunnel-through-iap` (port 22 is firewalled off the public internet) |
| OS Login SSH user | `ben_wiggleverse_org` (auto-derived; you don't type it) |
| App system user | `ohm-rfc-app` |
For reference, the separate Gitea VM is `wiggleverse` project / `gitea` VM / 34.55.46.221.
@@ -36,7 +45,7 @@ For reference, the separate Gitea VM is `wiggleverse` project / `gitea` VM / 34.
| Record | Type | Value | Proxy |
|--------|------|-------|-------|
| `ohm.wiggleverse.org` | A | 34.132.29.41 | DNS-only (gray cloud) |
| `ohm.wiggleverse.org` | A | `136.116.40.66` | DNS-only (gray cloud) |
| `_dmarc.wiggleverse.org` | TXT | `v=DMARC1; p=none; rua=mailto:ben@wiggleverse.org` | n/a |
> Note: `ohm.wiggleverse.org` uses **Let's Encrypt via certbot** directly on the VM. Keep the A record **DNS only (gray cloud)** — Cloudflare Flexible SSL would conflict with certbot.
@@ -48,24 +57,25 @@ SPF (`v=spf1 include:_spf.google.com ~all`) and DKIM (`google._domainkey`) for `
| Component | Details |
|-----------|---------|
| Backend | Python 3.11, FastAPI, uvicorn (single process) |
| Database | SQLite in WAL mode at `/opt/rfc-app/backend/data/rfc-app.db` |
| Database | SQLite in WAL mode at `/opt/ohm-rfc-app/backend/data/rfc-app.db` |
| Frontend | React 19, Vite 8, Tiptap 3, React Router 7 |
| Web server | nginx — serves `frontend/dist/` as static SPA, proxies `/api/` and `/auth/` to uvicorn on `127.0.0.1:8000` |
| Process manager | systemd unit `rfc-app.service`, runs as `rfc-app` system user |
| Process manager | systemd unit `ohm-rfc-app.service`, runs as `ohm-rfc-app` system user |
| TLS | Let's Encrypt via certbot |
| Git backend | Gitea at `git.wiggleverse.org`, bot service account `rfc-bot` |
| Content Gitea (the bot's writes) | Gitea at `git.wiggleverse.org`, org `wiggleverse`, meta repo `ohm-content` (`wiggleverse/ohm-content`), bot service account `rfc-bot` |
| Code-deploy source (the VM's git origin) | **`https://git.benstull.org/benstull/rfc-app.git`** — a *different* Gitea from the content one. See the two-remote note under "Deploying a New Version." |
| Email | Google Workspace SMTP relay (`smtp-relay.gmail.com:587`), AUTH'd as `ben@wiggleverse.org`, From `notifications@wiggleverse.org` |
### Key Paths on the VM
| Path | Contents |
|------|---------|
| `/opt/rfc-app/` | App root (owned by `rfc-app` user) |
| `/opt/rfc-app/backend/.env` | All secrets and config (mode 0600) |
| `/opt/rfc-app/backend/data/rfc-app.db` | SQLite database |
| `/opt/rfc-app/frontend/dist/` | Built React SPA (served by nginx) |
| `/opt/ohm-rfc-app/` | App root (owned by `ohm-rfc-app` user) |
| `/opt/ohm-rfc-app/backend/.env` | All secrets and config (mode 0600) |
| `/opt/ohm-rfc-app/backend/data/rfc-app.db` | SQLite database |
| `/opt/ohm-rfc-app/frontend/dist/` | Built React SPA (served by nginx) |
| `/etc/nginx/sites-available/ohm.wiggleverse.org` | nginx vhost config |
| `/etc/systemd/system/rfc-app.service` | systemd unit |
| `/etc/systemd/system/ohm-rfc-app.service` | systemd unit |
---
@@ -91,7 +101,7 @@ Created in Gitea as `rfc-bot`. Token scopes: `write:repository`, `write:user`, `
### Meta repo
`wiggleverse/meta` — seeded by `scripts/seed_meta_repo.py`. Contains `PHILOSOPHY.md`, `README.md`, `CONTRIBUTING.md`, and `rfcs/` directory. Gitea webhook registered to `https://ohm.wiggleverse.org/api/webhooks/gitea`.
`wiggleverse/ohm-content` (the `META_REPO` value is `ohm-content`) — seeded by `scripts/seed_meta_repo.py`. Contains `PHILOSOPHY.md`, `README.md`, `CONTRIBUTING.md`, and `rfcs/` directory. Gitea webhook registered to `https://ohm.wiggleverse.org/api/webhooks/gitea`.
### OAuth2 app
@@ -167,23 +177,52 @@ HYGIENE_TICK_SECONDS=3600
## Deploying a New Version
SSH into the VM:
> **⚠️ Two-remote step — do this FIRST.** The framework's release flow
> (branches, PRs, version tags) happens on **`git.wiggleverse.org`**
> (`ben.stull/rfc-app`). But the VM's git origin is a *separate* Gitea,
> **`git.benstull.org/benstull/rfc-app`**, which does **not** auto-mirror
> from the release Gitea. So after a release is merged + tagged on
> `git.wiggleverse.org`, you must push `main` and the new tag to the
> `benstull` remote before the VM can fetch them:
> ```bash
> # from your local rfc-app clone, on main at the merged release tip:
> git push benstull main vX.Y.Z # benstull = git@git.benstull.org:benstull/rfc-app.git
> ```
> If `git ls-remote benstull vX.Y.Z` comes back empty, the VM cannot see
> the release yet — push it first. (Wiring the two Gitea instances to
> mirror would remove this step; until then it's manual.)
SSH into the VM (IAP-only — the `--tunnel-through-iap` flag is required):
```bash
gcloud compute ssh rfc-app --zone=us-central1-a --project=wiggleverse-rfc
gcloud compute ssh ohm-rfc-app --zone=us-central1-a --project=wiggleverse-ohm --tunnel-through-iap
```
Pull the latest code, reinstall deps, restart:
Fetch + check out the release tag (the VM tracks a **detached** tag, not
a branch), then restart. Backend deps only need reinstalling when
`requirements.txt` changed:
```bash
sudo -u rfc-app git -C /opt/rfc-app pull
sudo -u rfc-app /opt/rfc-app/backend/.venv/bin/pip install \
-r /opt/rfc-app/backend/requirements.txt
sudo systemctl restart rfc-app
sudo -u ohm-rfc-app git -C /opt/ohm-rfc-app fetch origin --tags
sudo -u ohm-rfc-app git -C /opt/ohm-rfc-app checkout vX.Y.Z
# only if backend deps changed:
sudo -u ohm-rfc-app /opt/ohm-rfc-app/backend/.venv/bin/pip install \
-r /opt/ohm-rfc-app/backend/requirements.txt
sudo systemctl restart ohm-rfc-app
```
For frontend changes, build on the VM directly (Node 20+ is already there):
```bash
cd /opt/rfc-app/frontend && sudo -u rfc-app npm ci
sudo -u rfc-app npm run build
cd /opt/ohm-rfc-app/frontend && sudo -u ohm-rfc-app npm ci
sudo -u ohm-rfc-app npm run build
```
Smoke test (asset hashes change on every rebuild, so a match proves the
new bundle is live):
```bash
# public:
curl -s -o /dev/null -w '%{http_code} %{remote_ip}\n' https://ohm.wiggleverse.org/
curl -s https://ohm.wiggleverse.org/ | grep -oE 'assets/index-[A-Za-z0-9_-]+\.(js|css)'
# backend startup line, on the VM:
sudo journalctl -u ohm-rfc-app -n 20 --no-pager | grep 'RFC app started'
```
`npm ci` installs strictly from the committed `package-lock.json` and
@@ -192,7 +231,7 @@ to rewrite the lockfile in place (notably stripping `libc` fields on
optional rollup native packages), which then conflicts with `git
checkout <tag>` on the next deploy.
The output lands in `/opt/rfc-app/frontend/dist/` owned by `rfc-app` — nginx serves it directly, no copy step needed.
The output lands in `/opt/ohm-rfc-app/frontend/dist/` owned by `ohm-rfc-app` — nginx serves it directly, no copy step needed.
(Building locally and `gcloud compute scp`-ing the dist also works. Plain `rsync -e ssh` from the Mac fails because OS Login uses short-lived SSH certs that only the gcloud wrapper can mint interactively.)
@@ -202,6 +241,15 @@ Schema migrations run automatically on restart (append-only, safe to re-run).
## First-Time Deployment (new server)
> **Note on names:** the command blocks in this section predate the GCP
> name-alignment and still spell the pre-alignment project/VM/path/user
> (`wiggleverse-rfc` / `rfc-app` / `/opt/rfc-app` / `rfc-app` /
> `34.132.29.41`). They're kept as the structural reference. When standing
> up a box today, substitute the current values from the Host/Paths tables
> at the top: project `wiggleverse-ohm`, VM `ohm-rfc-app`, install path
> `/opt/ohm-rfc-app`, system user `ohm-rfc-app`, service `ohm-rfc-app`, IP
> `136.116.40.66`, and SSH via `--tunnel-through-iap`.
### 1. Add DNS record
Add `ohm.wiggleverse.org` → 34.132.29.41 as an A record in Cloudflare, **DNS only (gray cloud)**. Do not proxy — certbot needs to reach the VM directly.
@@ -307,7 +355,7 @@ Paste the following into a new Claude session to continue development:
---
> I'm working on the **Wiggleverse RFC App** — a FastAPI + SQLite + React + Vite application deployed at `ohm.wiggleverse.org` on a GCP e2-small VM (`rfc-app` in the `wiggleverse-rfc` project; separate from the `gitea` VM in `wiggleverse` that runs Gitea at `git.wiggleverse.org`). The app is the primary interface for the Open Human Model (OHM) RFC working group.
> I'm working on the **Wiggleverse RFC App** — a FastAPI + SQLite + React + Vite application deployed at `ohm.wiggleverse.org` on a GCP e2-small VM (`ohm-rfc-app` in the `wiggleverse-ohm` project; separate from the `gitea` VM in `wiggleverse` that runs Gitea at `git.wiggleverse.org`). The app is the primary interface for the Open Human Model (OHM) RFC working group.
>
> **Stack:**
> - Backend: Python 3.11, FastAPI, uvicorn (single process), SQLite WAL mode
@@ -323,11 +371,12 @@ Paste the following into a new Claude session to continue development:
> - 10 append-only schema migrations in `backend/migrations/`
>
> **Deployment:**
> - SSH: `gcloud compute ssh rfc-app --zone=us-central1-a --project=wiggleverse-rfc`
> - Code at `/opt/rfc-app/` on the VM, owned by `rfc-app` system user
> - `.env` at `/opt/rfc-app/backend/.env` (mode 0600)
> - Frontend built **on the VM** (Node 20 is installed there) with `npm run build` directly into `/opt/rfc-app/frontend/dist/`
> - Restart to deploy: `sudo systemctl restart rfc-app`
> - SSH (IAP-only): `gcloud compute ssh ohm-rfc-app --zone=us-central1-a --project=wiggleverse-ohm --tunnel-through-iap`
> - Code at `/opt/ohm-rfc-app/` on the VM, owned by `ohm-rfc-app` system user; tracks a detached release **tag**
> - The VM's git origin is `git.benstull.org/benstull/rfc-app` — a *different* Gitea from the release one (`git.wiggleverse.org`); push `main` + the new tag to the `benstull` remote before deploying
> - `.env` at `/opt/ohm-rfc-app/backend/.env` (mode 0600)
> - Frontend built **on the VM** (Node 20 is installed there) with `npm run build` directly into `/opt/ohm-rfc-app/frontend/dist/`
> - Restart to deploy: `sudo systemctl restart ohm-rfc-app`
> - Migrations run automatically on startup
>
> **Source is at `~/git/rfc-app/`.**