# Self-Hosting The game can run on your own Windows PC and use router port forwarding. Account registration, sessions, characters, progression, inventory, loot history, and leaderboards are stored in the SQLite database at `data/game.db`. That database file is the persistent storage. It remains after the server or PC restarts. If the file is deleted or lost, all accounts and save data are lost, so keep regular backups. ## Build and run ```powershell npm ci npm run db:init npm run build $env:HOST = "127.0.0.1" $env:PORT = "4173" $env:TRUST_PROXY = "1" npm start ``` Run `npm run db:init` after pulling schema changes. It creates missing tables and seed content without erasing existing player data. ## Internet access Do not expose the Node port directly over unencrypted HTTP. Passwords would travel across the internet without encryption. Put Caddy or another HTTPS reverse proxy in front of the game: ```caddyfile game.example.com { reverse_proxy 127.0.0.1:4173 } ``` Point a domain or dynamic-DNS name to your public IP, forward router ports 80 and 443 to the PC running Caddy, and allow those ports through Windows Firewall. Caddy obtains and renews the HTTPS certificate. Keep the game server bound to `127.0.0.1`. Set `TRUST_PROXY=1` only when the server can be reached solely through your local reverse proxy. This lets account limits use the visitor's public IP instead of the proxy's address. ## TrueNAS single-container hosting ### TrueNAS SCALE runbook This is the simplest TrueNAS setup. One container serves the browser game, auth routes, game API routes, and one SQLite database. Use this when you want `iwanttoheal.phenomrom.com` to host the playable browser version and you want code updates to be a Git pull plus app restart. Portainer is not required. Use TrueNAS **Apps > Discover > Install via YAML**. Repository: ```text https://git.whoagland.com/phenom/i-want-to-heal.git ``` TrueNAS paths: ```text /mnt/usbssds/apps/iwanttoheal/app /mnt/usbssds/apps/iwanttoheal/data ``` Create the app directory and clone the repo: ```sh sudo mkdir -p /mnt/usbssds/apps/iwanttoheal cd /mnt/usbssds/apps/iwanttoheal sudo git clone https://git.whoagland.com/phenom/i-want-to-heal.git app ``` Because the clone was run with `sudo`, give the normal TrueNAS user ownership: ```sh sudo chown -R truenas_admin:truenas_admin /mnt/usbssds/apps/iwanttoheal ``` Create the persistent data folder: ```sh mkdir -p /mnt/usbssds/apps/iwanttoheal/data ``` Check that the production server file exists: ```sh ls /mnt/usbssds/apps/iwanttoheal/app/server/production.mjs ``` If that file is missing, push the latest code to `git.whoagland.com` from the development machine, then pull on TrueNAS: ```sh cd /mnt/usbssds/apps/iwanttoheal/app git pull ``` If Git fails with `chmod ... Operation not permitted`, do not use a media or SMB dataset for the repo. Git needs normal file locking and chmod behavior. Create or use a dedicated apps dataset and clone under `/mnt/usbssds/apps/...`. ### TrueNAS app YAML In TrueNAS: 1. Open **Apps**. 2. Open **Discover**. 3. Click the three-dot menu. 4. Choose **Install via YAML**. 5. Name the app `iwanttoheal`. 6. Paste this YAML: ```yaml services: iwanttoheal: image: node:24-bookworm-slim working_dir: /app command: sh -lc "npm ci && npm run db:init && npm run build && npm start" environment: HOST: 0.0.0.0 PORT: "4173" TRUST_PROXY: "1" COOKIE_SECURE: "1" CORS_ORIGINS: "http://localhost,https://localhost,capacitor://localhost,https://iwanttoheal.phenomrom.com,https://auth.phenomrom.com" ports: - "4173:4173" volumes: - /mnt/usbssds/apps/iwanttoheal/app:/app - /mnt/usbssds/apps/iwanttoheal/data:/app/data restart: unless-stopped ``` The app listens inside Docker on port `4173`. The database lives at `/mnt/usbssds/apps/iwanttoheal/data/game.db` because that host directory is mounted into the container as `/app/data`. The startup command installs dependencies, applies schema migrations, builds the web app, and starts the production server. Test the local TrueNAS service: ```sh curl http://TRUENAS-IP:4173/api/auth/session ``` Expected response: ```json {"account":null,"profile":null} ``` ### Reverse proxy Point `iwanttoheal.phenomrom.com` at the TrueNAS app through HTTPS. Do not expose port `4173` directly to the internet. Put Caddy or another reverse proxy in front: ```caddyfile iwanttoheal.phenomrom.com { reverse_proxy TRUENAS-IP:4173 } auth.phenomrom.com { reverse_proxy TRUENAS-IP:4173 } ``` Both hostnames can point at the same container. `iwanttoheal.phenomrom.com` serves the browser game. `auth.phenomrom.com` stays available as an auth URL for Android or other clients that need a dedicated auth hostname. DNS should point both hostnames at the public IP or dynamic DNS name that reaches the reverse proxy. Forward public ports `80` and `443` to the reverse proxy host. Test the public game and auth URLs: ```sh curl https://iwanttoheal.phenomrom.com curl https://auth.phenomrom.com/api/auth/session ``` Expected auth response: ```json {"account":null,"profile":null} ``` ### App build config For the hosted browser game, no separate auth build setting is needed. The web app can call same-origin routes like `/api/auth/login` and `/api/profile`. For an Android build that should use the TrueNAS-hosted game API, build with: ```sh npm run android:apk:truenas ``` If you intentionally want Android auth calls to use `auth.phenomrom.com`, also set `VITE_AUTH_API_BASE_URL=https://auth.phenomrom.com`. Otherwise, leave it unset and auth uses the same base URL as the game API. Android runs the bundled web app from a local Capacitor origin, not from `iwanttoheal.phenomrom.com`. The hosted server must allow that origin through CORS, which is why the TrueNAS YAML includes `http://localhost`, `https://localhost`, and `capacitor://localhost`. ### Updating the TrueNAS game app Push changes from the development machine to `git.whoagland.com`, then pull them on TrueNAS: ```sh cd /mnt/usbssds/apps/iwanttoheal/app git pull ``` Restart the `iwanttoheal` app in the TrueNAS Apps UI after pulling. The app command runs `npm ci`, `npm run db:init`, `npm run build`, and `npm start` on startup, so dependency, schema, and browser bundle changes are applied each time the container restarts. Normal update workflow: ```sh # development machine git add . git commit -m "Update game" git push origin main # TrueNAS shell cd /mnt/usbssds/apps/iwanttoheal/app git pull ``` Then restart the TrueNAS app. ### Existing auth-only app If `iwanttoheal-auth` was already created during earlier testing, the simplest path is to stop that app and use the single `iwanttoheal` app above. The single container serves both domains and avoids two processes sharing one SQLite file. ## Account limits Registration permits one account per public IP by default. Login and API rate limits also apply. To allow a shared household to create more accounts, run a local administrator command: ```powershell npm run accounts:ip -- set 203.0.113.42 4 Smith-household npm run accounts:ip -- list npm run accounts:ip -- remove 203.0.113.42 ``` `set` changes the total number of accounts that address may create. `remove` returns it to the default one-account limit. These commands must be run on the host PC and are not exposed through the website. An IP account limit reduces casual account spam, but it is not DDoS protection. Your router and internet connection can still be overwhelmed before requests reach the game. Keep Windows patched, expose only required ports, and use router or ISP protections where available. ## Backups Create a consistent live backup with: ```powershell npm run db:backup ``` Backups are written to `backups/game-.db`. Copy them to another disk or cloud backup location; backups left only on the game PC will not help if its drive fails. Test restoration occasionally by stopping the server, preserving the current `data/game.db`, and placing a backup at that path. ## Current scaling boundary SQLite is a good fit for a small private server running one game process. Do not run multiple Node server instances against this setup. Combat currently runs in the browser. It is suitable for a friend group, but a modified client could submit false completion metrics. Move combat simulation and run validation to the server before treating rankings as cheat-resistant.