# pewpewstudio Laser job studio (FastAPI backend + React SPA, gitea `vh/pewpewstudio`). It composes Thunder Aurora marking jobs and returns LightBurn `.lbrn` files as browser downloads, and never touches hardware. Deployed 2026-10-13 on Prime's directive via pewpew-dev (thread `01M4WATRFQX3V7TSJ4XPGABWMN`). | | | |---|---| | **URL** | `http://10.100.50.40:8185/` (= `http://nh3-docker.nh3.internal:8185/`). Homepage tile in *AI - Studios*. | | **Host** | nh3-docker: fast local disk for SQLite WAL (fsync ~2.7 ms, unlike ana-docker's HDD pool), and it reaches arbo `100.64.0.6:8201` and Worldtree DEMO `10.250.50.152:8080`. | | **Auth** | NONE in v1. LAN/mesh only: published on the host's LAN address, with no public ingress. | | **Images** | `pewpewstudio:` (api) and `pewpewstudio-web:` (Caddy + `web/dist`), built on nh3-dev and loaded here; `PEWPEW_REV` in `.env` picks the pair. | | **State** | volume `pewpewstudio_state` (`/state`: `shop.db` + `assets/`) and volume `pewpewstudio_config` (`/config`: machine profiles, lenses, denylist, sizing, fonts, which **the app edits from the browser**). Both are covered by nh3-pve's nightly 21:00 whole-VM vzdump of VM 100. | | **Secrets** | `.env` (0600, infra-ops): `WORLDTREE_API_KEY` = vault `nh3-dev/.config/worldtree/pewpewstudio-demo-key`. | ## Topology `web` (Caddy, published `10.100.50.40:8185 → :80`) serves the SPA at `/`. It sends `/api/*` to `api:8000` with the prefix **stripped** (the backend has no `/api` routes and no CORS, so both must be one origin) and Host rewritten to `localhost`, which the backend's TrustedHost accepts. Because of that rewrite, **Caddy refuses any Host other than `10.100.50.40` / `nh3-docker.nh3.internal` with 400**, to keep the backend's DNS-rebinding defence. Add a name to `conf/Caddyfile` before publishing one. Verified 2026-10-13: foreign Host → 400; IP and name → 200. ⚠ That check must stay inside `route { }`. Outside it, Caddy orders `respond` after `handle_path`, and the check silently never runs: a foreign Host got 200 until it was wrapped. ⚠ The Caddy config is mounted as the DIRECTORY `/opt/docker/conf/pewpewstudio → /etc/caddy`. deploy-stack replaces the file, and a single-file bind mount keeps showing the old inode. ## Build and deploy (from nh3-dev) ```bash REV=; C=$(mktemp -d) git -C ~/development/pewpewstudio archive $REV | tar -x -C $C cp stacks/pewpewstudio/build/Dockerfile $C/Dockerfile.eshpfi # hamr is a git dependency over gitea SSH (10.250.50.70:222); BuildKit forwards lkraven's gitea key: (cd $C && docker build -f Dockerfile.eshpfi --ssh default=$HOME/.ssh/id_rsa --target api -t pewpewstudio:$REV . \ && docker build -f Dockerfile.eshpfi --target web -t pewpewstudio-web:$REV .) docker save pewpewstudio:$REV pewpewstudio-web:$REV | ssh infra-ops@10.100.50.40 docker load scripts/deploy-stack.sh nh3-docker pewpewstudio # compose + conf/Caddyfile; never .env # on nh3-docker: set PEWPEW_REV=$REV in .env (in place, keep 0600), then cd /opt/docker/compose/pewpewstudio && docker compose up -d ``` **First deploy only:** the config volume was seeded once from the repo's `config/` plus the gitignored `config/machine.local.toml` from nh3-dev's checkout (machine parameters, no secrets). Without a `machine.*.toml` (the registry skips `machine.example.toml`), startup fails "no machine.*.toml files". **Never reseed a populated volume**: it holds the shop's own edits. The database started fresh; the dev `shop.db` was smoke-test data. ## Checks - `GET /` 200 (the SPA); `GET /api/machines` 200 (one machine, `aurora-mopa-100w-f160`); `GET /api/generate/workflows` 200. - The api healthcheck is `GET /machines` on loopback.