# Deploying Wiz-War The game runs on a single DigitalOcean droplet: one Node process serves the built client and the websocket on port 8787; Caddy in front terminates TLS with automatic certificates. Room files persist in /var/lib/wizwar/rooms and survive deploys and reboots. ## Current production - Host: `hall` (nyc3, tag `kestrels-hall`), IP 159.203.115.252, shared with the other games of Kestrel's Hall since 2026-09-29; Wiz-War keeps its own service user, port 8787, site file and access log there. - URLs: https://wizwar.kestrelsnest.social and https://wizwar.159.203.115.252.sslip.io (always works, zero DNS). Caddy serves both; NOTE: kestrelsnest.social's authoritative DNS is at HOVER (ns1/ns2.hover.com), not DigitalOcean — records must be added there. A matching record also sits in the DO zone in case nameservers ever move. ## Everyday deploys deploy/deploy.sh 159.203.115.252 Builds the client locally, rsyncs the repo (minus node_modules, data/, .git, research), installs dependencies on the droplet, and restarts the service. Games in progress survive: state lives in room files, and clients reconnect automatically. Before any deploy that touches the engine, run the determinism gate: deploy/verify-ledgers.sh 159.203.115.252 It fetches every production ledger and strictly replays it against the local engine; a single refused command fails the check. A room whose ledger no longer replays becomes unreachable after restart, so this is not optional. ## New droplet from scratch 1. `doctl compute droplet create wizwar --region nyc3 --size s-1vcpu-1gb \ --image ubuntu-24-04-x64 --ssh-keys --tag-name wizwar --wait` 2. `scp deploy/setup-droplet.sh deploy/Caddyfile.tmpl root@:/root/ && ssh root@ \ "bash /root/setup-droplet.sh 'wizwar..sslip.io'"` (the host's Caddyfile gathers each game's site from /etc/caddy/sites/, so the host can be shared) 3. `deploy/deploy.sh ` ## Operations - Logs: `ssh root@ journalctl -u wizwar -f` - Restart: `ssh root@ systemctl restart wizwar` - Nightly backups: `wizwar-backup.sh` runs from root's crontab at 07:17 UTC, pushing /var/lib/wizwar to the `kestrel-wizwar-backups` Space (nyc3) via rclone. It is installed at /usr/local/bin/wizwar-backup.sh on the droplet; `setup-droplet.sh` does NOT install it — on a fresh droplet, copy the script, configure rclone (the script refuses to run while the config still holds its CHANGE_ME placeholder), and add the cron entry by hand. - A player whose browser lost their seat (Safari's seven days, an app's own browser, cleared data) writes to the keeper. On the droplet, `wizwar-reseat.sh ROOM` lists who sits at the table and when each last moved or spoke; `wizwar-reseat.sh ROOM NAME` gives that wizard's seat a new token and prints a link carrying it in the fragment. The old token opens nothing after. The script answers through the game server's keeper door, a listener of its own on 127.0.0.1:9787 that Caddy never reaches, which opens only to the key in /etc/wizwar/keeper.key (root and the wizwar user alone can read it, so the other services on the host cannot use the door). The first deploy makes the key, and every deploy installs the script at /usr/local/bin. - One-off backup: `scp -r root@:/var/lib/wizwar/rooms ./rooms-backup`