# Client demo deploy — runbook How to set up `parsons.tensordesign.com.au` for the first time, and how to push updates after. Companion to [`client-demo-hosting-plan.md`](./client-demo-hosting-plan.md), which has the why and the architecture. This file is the actionable how. --- ## One-time server setup You'll do this once. After that, deploys are a single script. ### 1. DNS Point `parsons.tensordesign.com.au` at your home IP (or DDNS hostname). One A-record, same as your other subdomains on `tensordesign.com.au`. Verify after propagation (can take minutes): ```bash dig +short parsons.tensordesign.com.au ``` Should return your home IP. ### 2. swag — add `parsons` to SUBDOMAINS In your swag container's environment (compose file or `docker run` flags), add `parsons` to the comma-separated `SUBDOMAINS` list. Then restart the container: ```bash docker compose restart swag # or: docker restart swag ``` Watch the logs until you see Let's Encrypt issue the cert: ```bash docker logs -f swag # look for: "Certificate for parsons.tensordesign.com.au issued" ``` ### 3. Document root on the host Pick where the static files live on the host filesystem. Suggested: ```bash sudo mkdir -p /srv/parsons-demos sudo chown -R "$USER:$USER" /srv/parsons-demos ``` Make sure swag has access to it. Either: - **Mount it into swag** at `/config/www/parsons-demos/` (preferred — keeps swag's container view tidy): ```yaml # in your swag compose service: volumes: - /srv/parsons-demos:/config/www/parsons-demos:ro ``` Restart swag after editing compose. - Or symlink inside the existing swag config volume — works but messier. ### 4. Drop the nginx config in The repo has the conf at `nginx/parsons-demos.conf`. Copy it into swag's `site-confs/` directory: ```bash cp nginx/parsons-demos.conf /path/to/swag/config/nginx/site-confs/ docker compose exec swag nginx -t # syntax check docker compose exec swag nginx -s reload ``` If `nginx -t` complains, fix before reloading (a bad config will take swag down). ### 5. Create the htpasswd Pick a username (suggestion: `client`) and a strong shared password: ```bash docker compose exec swag htpasswd -c /config/nginx/.htpasswd-parsons client # prompts for password ``` For additional users later (e.g. one credential per client engagement), drop the `-c`: ```bash docker compose exec swag htpasswd /config/nginx/.htpasswd-parsons another-user ``` ### 6. Verify the auth + 404 Visit `https://parsons.tensordesign.com.au/` in a fresh browser. You should see: 1. SSL is valid (no cert warning) 2. Browser asks for username + password 3. After auth: empty page or 404 (no slices deployed yet — that's fine) If you see this far, the server is ready. --- ## Per-deploy workflow Once setup is done, the loop is two commands: ```bash # 1. Build the slice npm run demo:build -- --mode arrangement # 2. Push it up ./scripts/deploy-demo.sh arrangement ``` The deploy script: - Verifies `dist-demo/arrangement/` exists and isn't empty (aborts if not — won't rsync a half-built bundle over a working demo) - `rsync -az --delete` to the server (removes stale asset hashes) - Prints the URL to visit **Before first deploy:** edit `scripts/deploy-demo.sh` and set: - `TARGET_HOST="@tensordesign.com.au"` - `TARGET_BASE="/srv/parsons-demos"` (or wherever you put the document root) Make sure SSH key auth works (`ssh "$TARGET_HOST" echo ok` should succeed without a password prompt) so rsync doesn't stall. The script lives in `scripts/` which is gitignored, so your server-specific paths won't leak into the repo. --- ## Adding a second slice later 1. Build the new app under `src/demo/apps//` (mirror `arrangement/`'s structure). 2. `npm run demo:build -- --mode `. 3. `./scripts/deploy-demo.sh `. The nginx config catches `//...` automatically — no server changes needed for new slices. --- ## Optional: landing page at `/` Until you have one, `https://parsons.tensordesign.com.au/` returns 404. To add a tiny index listing available slices: ```bash cat > /srv/parsons-demos/index.html <<'EOF' Parsons demos

Parsons demos

EOF ``` Add `
  • ` entries as new slices ship. --- ## Troubleshooting **Cert didn't issue.** Check swag logs (`docker logs swag`). Common causes: DNS not propagated yet, port 80 blocked by router, `SUBDOMAINS` value missing or misspelled. **`nginx -t` fails after dropping the conf.** Most likely a path mismatch — `/config/www/parsons-demos` doesn't exist inside the container because the bind mount isn't set. Check `docker compose config` to confirm the mount is in effect. **Auth prompt loops / 401s after correct password.** htpasswd file path mismatch between conf and `htpasswd -c` location. Both must agree. **Demo loads but assets 404.** Vite `base` path mismatch. The build must use `--mode ` so assets are prefixed with `//`. Re-run the build and check `dist-demo//index.html` — script src should look like `//assets/index-XXX.js`. **`rsync` stalls or asks for password.** SSH key auth not set up. Run `ssh-copy-id ` once. **Want to roll back a deploy.** rsync with `--delete` is irreversible — there's no built-in undo. Either keep the previous build locally and re-deploy, or rebuild the previous git commit. For demo-grade work this is fine; if you need versioned deploys later, switch to dated subfolders + a symlink swap.