Files
esh-pfi-infrastructure/stacks/pewpewstudio

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:<rev> (api) and pewpewstudio-web:<rev> (Caddy + web/dist), built on nh3-dev and loaded here; PEWPEW_REV in .env picks the pair. Live: 3f73448 (since 2026-10-15 2327; was 26bba3c, whose images stay on the host for rollback).
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)

REV=<short sha>; 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.