Files
wizwar6e/deploy
Eric WagonerandClaude Fable 5.1 6e49fc9020 Credibility pass: comments say what the code can't, and the seams are gone
Engine: pit-rim exits live in one helper (pitRimExits) shared by the
resolver and the automaton; chargeReach names the six-stride cap; dust
sight is inDust + dustAtEnd; PushPending is exported once; the force-field
counter builds its events in one list; drawUnderSlowDeath says what it
draws under. Test groups are named for the rule they pin, not the
revision that introduced it.

Client: one smoothstep; one edgeScar per battle-scarred edge; one
board-zone rule (which also seats the caption strip on phones); the
compass names live in SIDE_NAMES; net.flash() is the one toast; MediaQuery
replaces two hand-rolled matchMedia listeners; sprites carry their sizes
as plain numbers and their CSS in one rule each; canvas helpers are
formatted like the rest of the tree. Comments that told how a cel or a
fallback used to look now say what it draws. The actions-over notice no
longer blames a pickup when slime ended the turn.

Server and ops: dataRoot() is the one data directory; headlineOf builds
its deeds without a cast; the rollup and skills read the same way the
code behaves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
2026-09-16 00:21:34 -04:00
..

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

  • Droplet: wizwar (nyc3, s-1vcpu-1gb, tag wizwar), IP 104.236.96.198
  • URLs: https://wizwar.kestrelsnest.social and https://wizwar.104.236.96.198.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 104.236.96.198

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 104.236.96.198

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 <your-key-ids> --tag-name wizwar --wait
  2. scp deploy/setup-droplet.sh root@<ip>:/root/ && ssh root@<ip> \ "bash /root/setup-droplet.sh wizwar.<ip>.sslip.io"
  3. deploy/deploy.sh <ip>

Operations

  • Logs: ssh root@<ip> journalctl -u wizwar -f
  • Restart: ssh root@<ip> 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.
  • One-off backup: scp -r root@<ip>:/var/lib/wizwar/rooms ./rooms-backup