LANE5: deploy web toy to digalot.fyi/destroy + depot already hardened

Ship the browser smash toy publicly and document it.

Deploy: web/ is live at https://digalot.fyi/destroy/ — served on the dealgod VPS like
thriftgod (an nginx:alpine static container on dealgod_default + a /destroy/ location in
the shared reverse proxy). README.md now carries the exact deploy/update steps + a full
rollback runbook. No app code changed.

3GOD hardening (LANE5 deliverable B) was found ALREADY DONE on the live instance and
verified — not re-changed:
  - GOD3_OPEN is off: unauthenticated write via Cloudflare → 403; GET /api/list → authed:false.
  - SSRF plugged: POST /api/fetch with file:///etc/hostname and http://169.254.169.254/ →
    rejected ("only public http(s) URLs allowed"), nothing written.
  - Reads stay public (CF GET 200 + CORS *); trusted tailnet peers still push passwordless
    via the TS_ALLOW IP allowlist (this session's meshgod pushes used that path).

Verified public: /destroy/ 200 over CF, JS as application/javascript, props (intact +
fractured) load from /3god/a/* with CORS *, printer boss shatters into 16 GLB chunks,
no console errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Monster Robot Party 2026-07-14 23:24:49 +10:00
parent a7e409593f
commit 17797b9ba0

View File

@ -54,3 +54,44 @@ Pointer Lock (embedded frames / smoke tests).
fetch and **no COOP/COEP headers**.
- Active physics bodies are capped and settled debris is despawned, so a long smashing
session stays bounded (JS GC is the ceiling).
## Deploy (public) — https://digalot.fyi/destroy/
Live on the digalot.fyi VPS (`dealgod`), served exactly like `thriftgod`: a small
`nginx:alpine` static container on the `dealgod_default` docker network, with a
`location /destroy/` proxy in the shared reverse proxy. Zero build step — it's the raw
`web/` folder. LANE5, 2026-07-14.
**Update the live toy** (after committing changes to `web/`), from the monorepo on a
tailnet machine with `root@dealgod` access:
```sh
rsync -az --delete --exclude '.DS_Store' web/ root@100.94.195.115:/opt/destroyulator/web/
# static files are volume-mounted read-only, so no container restart is needed;
# hard-refresh past the CF/browser cache. To force-cycle the container:
ssh root@100.94.195.115 'docker restart destroyulator'
```
**How it was wired (one-time):**
1. `rsync web/ → dealgod:/opt/destroyulator/web/`
2. `docker run -d --name destroyulator --restart unless-stopped --network dealgod_default \`
` -v /opt/destroyulator/web:/usr/share/nginx/html:ro nginx:alpine`
3. Added to the `digalot.fyi` server block in `/opt/dealgod/nginx/dealgod-servers.conf`:
```nginx
location = /destroy { return 301 /destroy/; }
location /destroy/ { proxy_pass http://destroyulator/; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 60s; }
```
then `docker exec dealgod-nginx nginx -t && docker exec dealgod-nginx nginx -s reload`.
**Rollback (full teardown, no effect on other sites):**
```sh
ssh root@100.94.195.115 '
cp /opt/dealgod/nginx/dealgod-servers.conf.bak-lane5 /opt/dealgod/nginx/dealgod-servers.conf
docker exec dealgod-nginx nginx -s reload
docker rm -f destroyulator
rm -rf /opt/destroyulator'
```
The depot (`/3god`) is already hardened — writes require the tailnet-peer allowlist or the
`3g` auth cookie, `/api/fetch` rejects `file://` + private/metadata hosts, reads stay
public with CORS `*`. The toy only reads, and the `assets/` mirror covers a depot outage.