Developer Guide

Deploy to GFI VPS (dev)

Run OpenStory as a gfi-run HTTP service on the Hetzner testnet VPS

OpenStory's production target is Cloudflare Workers. The VPS path is a dev replica of bun dev: Vite + Workerd/Miniflare with local D1 and R2, not a Workers deploy.

URL

https://sh-openstory-dev.gfigroup.io

Sign-in is the admin email + password from the service env (ADMIN_EMAILS, ADMIN_PASSWORD).

One-time owner registration

On the VPS, as ggcloud:

gfi-run app create openstory \
  --repo-dir /home/ggcloud/repos/openstory \
  --dockerfile Dockerfile

gfi-run service create openstory-dev \
  --app openstory \
  --type http \
  --port 3000 \
  --health-path /healthz \
  --public-write \
  --env-file /home/ggcloud/data/apps-env/openstory-dev/testnet.env \
  --data-mount wrangler-state:/app/.wrangler:rw

--public-write is required because login and generation are POST. The bind mount keeps Miniflare D1/R2 across recreates.

gfi-run drops all container capabilities, so root inside the container cannot write a 0700 host mount owned by ggcloud. After service create, make the Miniflare dir world-writable once:

chmod 0777 /home/ggcloud/data/apps-data/openstory-dev/wrangler-state

Env

Copy .env.local to /home/ggcloud/data/apps-env/openstory-dev/testnet.env (mode 0600). Set:

VITE_APP_URL=https://sh-openstory-dev.gfigroup.io
VITE_ALLOWED_HOSTS=all
CLOUDFLARE_INCLUDE_PROCESS_ENV=true
CREDITS_UNLIMITED=true

Do not bake secrets into the image. gfi-run push / deploy refuse .env.local in the source tree.

Deploy from a laptop

# source directory must not contain .env / .env.local
./scripts/gfi-run --target [email protected] \
  push openstory-dev /path/to/openstory

Or rsync to /home/ggcloud/repos/openstory and run gfi-run deploy openstory-dev /home/ggcloud/repos/openstory on the VPS.

Verify

gfi-run status openstory-dev
curl -fsS https://sh-openstory-dev.gfigroup.io/healthz