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:
@@ -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/`.**
|
||||
|
||||
Reference in New Issue
Block a user