Files
healer-man/DEPLOYMENT.md
T
2026-08-14 15:56:39 -04:00

10 KiB

TrueNAS deployment

Healer Man uses the same local-Gitea deployment pattern as the previous i-want-to-heal-mmo game: clone the Gitea bare repository through the TrueNAS filesystem, mount the runnable checkout into one Node container, and update it with a local fast-forward pull plus an app restart.

The public URL remains https://iwanttoheal.phenomrom.com. The old game and Healer Man cannot both own host port 4173; stop the old app before the final cutover. Keep its checkout and database until the replacement is verified.

What the TrueNAS server does

The healer-man TrueNAS app is the browser host and online account server. One Node 24 process serves the production Vite bundle and authenticated /api routes on port 4173. Its SQLite database stores accounts, hashed credentials, sessions, and cloud rosters under /app/data/game.db.

The source checkout and persistent data are separate mounts. Replacing the container, rebuilding dist, or pulling source must not replace the database. TrueNAS Gitea stores the source repository and APK release attachments; it is not the game database.

Large runtime assets are published separately under /app/content. Android clients download verified content objects from /content and retain them in app-private storage for offline play. See CONTENT_UPDATES.md for the manifest, atomic activation, compatibility, and rollback contract.

Paths

Local Gitea bare repository:
/mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/healer-man.git

Runnable working checkout:
/mnt/usbssds/apps/healer-man/app

Persistent game data:
/mnt/usbssds/apps/healer-man/data

Published downloadable content:
/mnt/usbssds/apps/healer-man/content

Database backups:
/mnt/usbssds/apps/healer-man/backups

Public URL:
https://iwanttoheal.phenomrom.com

Container/host port:
4173

The Gitea bare repository is not runnable. Clone it with Git; never copy it with cp. The checkout contains several gigabytes of required GLB assets, so the initial clone and first build will take substantially longer than later fast-forward updates.

Do not turn the GLBs into Git LFS pointers without also changing this deployment procedure. Gitea keeps LFS objects outside the bare repository, so a bare-path clone alone cannot retrieve them.

First installation

Create an empty Gitea repository named phenom/healer-man, then initialize and push the local D:\Projects\HealerMan project to its main branch.

The current project directory is not yet a Git working tree. After creating the empty Gitea repository, run this once from PowerShell on the development PC:

Set-Location D:\Projects\HealerMan
git init -b main
git remote add origin https://git.whoagland.com/phenom/healer-man.git
git add .
git commit -m "Initial Healer Man import"
git push -u origin main

If the repository slug differs, change both the remote URL here and every healer-man.git bare-repository path below. Do not commit .env files, keystores, API tokens, content, dist, dist-android, or .android-public.

On TrueNAS, confirm the bare repository path. If the first command fails, use the search command:

sudo test -d /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/healer-man.git
sudo find /mnt -type d -name "healer-man.git" -prune -print 2>/dev/null

Clone entirely through the local filesystem:

sudo mkdir -p /mnt/usbssds/apps/healer-man/{app,data,content,backups}
sudo git config --global --add safe.directory \
  /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/healer-man.git
sudo git clone \
  /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/healer-man.git \
  /mnt/usbssds/apps/healer-man/app
sudo chown -R truenas_admin:truenas_admin /mnt/usbssds/apps/healer-man

Verify the checkout:

git -C /mnt/usbssds/apps/healer-man/app remote -v
git -C /mnt/usbssds/apps/healer-man/app branch --show-current
ls /mnt/usbssds/apps/healer-man/app/package.json
ls /mnt/usbssds/apps/healer-man/app/server/server.mjs

Expected branch: main. Expected origin: the local Gitea path above.

Install as a TrueNAS app

  1. Stop the old iwanttoheal-mmo app so port 4173 is free.
  2. Open Apps and then Discover.
  3. Open the three-dot menu and select Install via YAML.
  4. Name the new app healer-man.
  5. Paste the following YAML:
services:
  healerman:
    image: node:24-bookworm-slim
    command: >-
      sh -lc "npm ci --include=dev && npm run db:init && npm run content:publish && npm run build && npm start"
    environment:
      CORS_ORIGINS: "https://iwanttoheal.phenomrom.com,capacitor://localhost,http://localhost,https://localhost"
      CONTENT_DIR: /app/content
      DATA_DIR: /app/data
      HOST: 0.0.0.0
      NODE_OPTIONS: "--max-old-space-size=4096"
      PORT: "4173"
      SESSION_TTL_DAYS: "30"
      STATIC_DIR: /app/dist
      TRUST_PROXY: "true"
      VITE_API_BASE_URL: "https://iwanttoheal.phenomrom.com"
    init: true
    ports:
      - "4173:4173"
    restart: unless-stopped
    volumes:
      - /mnt/usbssds/apps/healer-man/app:/app
      - /mnt/usbssds/apps/healer-man/data:/app/data
      - /mnt/usbssds/apps/healer-man/content:/app/content
    working_dir: /app

Do not remove either persistent mount. /app/data owns account/cloud-save data; /app/content owns downloadable manifests and immutable content objects. The startup command installs locked dependencies, initializes the schema, publishes new or changed content objects, builds the full browser bundle, and starts the combined static/API/content server. Existing content objects are reused.

After startup, test on the LAN:

curl -I http://TRUENAS-IP:4173
curl http://TRUENAS-IP:4173/api/health
curl http://TRUENAS-IP:4173/content/manifest.json

Expected results are HTTP 200 and a JSON response containing "service":"healer-man". Keep the existing reverse proxy for iwanttoheal.phenomrom.com pointed at TRUENAS-IP:4173; no DNS change is required if that proxy target stays the same.

Verify account creation, sign-out/sign-in, character creation, and a second login before deleting the old TrueNAS app. The old game's database is not schema-compatible with Healer Man and must not be copied over the new one.

Update workflow

Push main from the development PC. Then run on TrueNAS:

git -C /mnt/usbssds/apps/healer-man/app pull --ff-only \
  /mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/healer-man.git \
  main

Restart healer-man in the TrueNAS Apps UI afterward. Container startup publishes immutable content objects and replaces manifest.json only after all objects exist, then rebuilds the browser client and starts the server. Android devices discover the small new manifest and download only missing or changed files. Asset-only updates do not require a new APK. A failed fast-forward pull deliberately leaves the running checkout unchanged; resolve diverged history on the development machine rather than forcing the TrueNAS checkout.

To publish content manually without restarting the app container, run inside a Node 24 environment that mounts the checkout and content volume:

CONTENT_DIR=/mnt/usbssds/apps/healer-man/content \
npm --prefix /mnt/usbssds/apps/healer-man/app run content:publish

Database backup

Create a consistent SQLite backup before migrations or destructive maintenance:

DATA_DIR=/mnt/usbssds/apps/healer-man/data \
BACKUP_DIR=/mnt/usbssds/apps/healer-man/backups \
npm --prefix /mnt/usbssds/apps/healer-man/app run db:backup

Back up the whole /mnt/usbssds/apps/healer-man/data dataset with TrueNAS snapshots as well. The script backup is convenient for point-in-time database copies; it does not replace dataset-level protection.

APK releases and Obtainium

Keep APK binaries out of the Git branch. The Android build uses dist-android, which contains the game runtime, UI, equipment, and Wailing Caverns starter content rather than the full multi-gigabyte browser catalog. Downloaded packs survive APK updates.

Install JDK 21 and Android SDK Platform 36 on the development PC and set JAVA_HOME plus ANDROID_SDK_ROOT. The build wrapper also recognizes the project's existing portable JDK/SDK layout when present.

For each Android version, push main first. Then set the permanent signing-key secrets and Gitea token in the development shell:

$env:ANDROID_KEYSTORE_FILE = 'D:\secure\healer-man-release.jks'
$env:ANDROID_KEYSTORE_PASSWORD = '...'
$env:ANDROID_KEY_ALIAS = 'healer-man'
$env:ANDROID_KEY_PASSWORD = '...'
$env:GITEA_TOKEN = '...'

Do not commit those values or the keystore. Build, sign, checksum, create the Gitea release/tag, and upload both attachments with:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/publish-gitea-release.ps1 `
  -VersionName 0.1.1 `
  -VersionCode 2 `
  -ReleaseNotes 'Healer Man client update.'

The result contains:

Tag: v0.1.1
APK: healer-man-0.1.1-release.apk
Checksum: healer-man-0.1.1-release.apk.sha256

Obtainium should monitor:

https://git.whoagland.com/phenom/healer-man/releases

Generic Gitea is not listed as a dedicated Obtainium source. Add the public releases URL above; if automatic detection does not select it correctly, set the source override to HTML and filter APK links with:

healer-man-.*-release\.apk$

The repository or a separate release-only repository must be publicly readable for players. Never distribute a privileged Gitea token in the app or Obtainium configuration.

Use one permanent Android release-signing key and increase versionCode for every release. Losing or changing that key prevents installed copies from updating in place. If the source repository is private, expose a public read-only release repository or download page rather than storing a privileged Gitea token on the game device.

Asset, texture, audio, and compatible data updates use content:publish and do not need an Obtainium release. JavaScript/React gameplay changes, loader or shader changes, native Android changes, permissions, or manifest-schema changes require a new signed APK with a higher versionCode.