Files
sysadmin-chronicles/docs/INSTALLER_PLAN.md
T
44r0n7 0265afa054 chore: bootstrap lean sysadmin-chronicles repo
Import the runnable game code, content, docs, scripts, and repo guidance while leaving local agent state, dependency installs, build output, and backup copies out of the published tree.
2026-05-02 11:49:07 -04:00

21 KiB
Raw Blame History

Installer & Distribution Plan

Status: Planning — not yet implemented. Covers: installer, uninstaller, VM rebuild, save management, modular script architecture.


Goals

  • Download zip from GitHub/Gitea, run install.sh, done.
  • Friendly tone throughout — this is a game, not a server deployment.
  • No jargon (libvirt, pool, domain, NAT) in any user-facing output.
  • Power users can follow the Manual Install section in README instead.
  • VM images live wherever the user puts the game (portable, large-drive friendly).
  • Full uninstall with explicit choices about what gets removed.
  • Users can rebuild individual VMs if something goes wrong.
  • Save data is resettable; save slots available for experimenting.

start-game.sh Fixes

The current launcher works but has two real bugs, several fragile assumptions, and no user-friendly output. Fix this in the same pass as the rest of the scripts since it will share lib/ui.sh and lib/config.sh.

Bugs to fix

Orphaned server process The script ends with exec remote-viewer, which replaces the shell. The trap that was set to kill the server on EXIT disappears with the shell — so when the player closes the SPICE window, the game server keeps running silently.

Fix: don't exec. Run remote-viewer normally, capture its PID, wait for it to exit, then kill the server cleanly.

# instead of:
exec remote-viewer "$spice_uri"

# do:
remote-viewer "$spice_uri" &
VIEWER_PID=$!
trap 'kill "$SERVER_PID" "$VIEWER_PID" 2>/dev/null || true' EXIT INT TERM
wait "$VIEWER_PID"

sleep 1 server readiness check One second is a race. On a slow machine or if npm install just ran, the server may not be up. On a fast machine it's wasted time.

Fix: poll in a tight loop with a timeout.

wait_for_server() {
    local port="$1" timeout=15 i=0
    while ! ss -tlnp | grep -q ":${port} " 2>/dev/null; do
        sleep 0.3
        ((i++))
        [ $i -ge $((timeout * 3)) ] && return 1
    done
}

Fragile assumptions to fix

  • lsof for port check — not universal. Replace with ss -tlnp (iproute2, present on all modern Linux).
  • No network check — if the sc-internal libvirt network is inactive, the VM starts but has no network. The HUD loads but shows nothing. Check the network is active (and start it if not) before starting the VM.
  • No images-dir check — once portable installs land, SC_IMAGES_DIR might be on an unmounted game drive. Check it exists before trying virsh ops.
  • Frontend build at launch"Building frontend..." at game launch is odd UX. Move this guard to install time. The launcher should only verify dist/index.html exists and fail clearly if it doesn't (don't silently trigger a build).

UX improvements

  • Source lib/ui.sh and lib/config.sh once they exist.
  • Replace raw echo "ERROR: ..." with friendly messages. Examples:
Current Replacement
ERROR: virsh is required. Your system is missing the virtual machine tools.\nRun install.sh to set up the game.
ERROR: missing workstation domain: sc-workstation Your game world hasn't been built yet.\nRun install.sh to finish setup.
ERROR: node is required. Install Node.js 18+. Node.js is required but wasn't found.\nRun install.sh to set up the game.
  • Show brief startup status so the player isn't staring at a blank terminal:
  Starting Sysadmin Chronicles...
  ✓ Game server running
  ✓ Workstation online
  Opening your desk...
  • Add --manage-saves and --reset-save flags (forward to tools/save/manage-saves.sh).

New flag: --stop

Since the server now outlives the viewer when fixed, add start-game.sh --stop that kills any running game server process. Useful if something gets stuck.

Summary of changes to start-game.sh

Area Change
Server shutdown exec → normal run + wait, trap covers both server and viewer
Server readiness sleep 1 → poll loop with 15s timeout
Port check lsofss -tlnp
Network check Add: verify sc-internal active, start if not
Images dir check Add: verify SC_IMAGES_DIR exists before virsh ops
Frontend build Remove from launcher; fail clearly if dist missing
Error messages Replace all with plain-English + fix instructions
Startup output Add three-line status before opening SPICE
New flags --manage-saves, --reset-save, --stop

Script Architecture

All user-facing scripts share a common library layer. No logic is duplicated.

tools/
  lib/
    ui.sh          # colored output, prompts, spinners, progress bars
    deps.sh        # distro detection, package name map, dep check/install
    libvirt.sh     # virsh wrappers: network, pool, domain, snapshot ops
    vm.sh          # build, rebuild, snapshot, revert per VM
    config.sh      # read/write install config (~/.config/sysadmin-chronicles/config)
    save.sh        # save slot management, reset helpers

install.sh         # project root — the entry point for new users
uninstall.sh       # project root — removal with options
start-game.sh      # project root — launcher (checks env, starts server, opens SPICE)

tools/
  setup/
    check-host.sh       # kept, improved UX, used internally by install.sh
    first-run-setup.sh  # kept as internal lib target or merged into install.sh
    seed-vms.sh         # kept as internal lib target, called by install.sh and rebuild
  vm/
    rebuild-vms.sh      # new: rebuild all or specific VMs
  save/
    manage-saves.sh     # new: list/switch/reset save slots

lib/ui.sh

  • sc_step "label" — numbered step header
  • sc_ok "msg", sc_warn "msg", sc_fail "msg" — status lines
  • sc_prompt "question" "default" — interactive prompt, returns answer
  • sc_confirm "question" — yes/no, returns 0/1
  • sc_spinner "label" / sc_spinner_stop — background spinner for long ops
  • sc_progress "label" current total — simple fraction display

lib/deps.sh

  • detect_distro — sets $SC_DISTRO (arch, debian, ubuntu, fedora, opensuse)
  • map_packages — translates canonical dep names to distro package names
  • check_deps — returns list of missing deps
  • install_deps "pkg1 pkg2 ..." — runs the right package manager with sudo, logs what was installed

lib/libvirt.sh

  • ensure_network name xml_path
  • ensure_pool name path
  • pool_path name — returns the pool's target directory
  • domain_exists name, domain_state name
  • snapshot_exists domain name
  • snapshot_create domain name description
  • snapshot_revert domain name
  • snapshot_delete domain name

lib/vm.sh

  • vm_build profile [--dry-run] [--force] — wraps build-vm.sh
  • vm_rebuild profile [--dry-run] — destroy + rebuild from cloud image
  • vm_revert vm_id snapshot_name — revert to named snapshot
  • vm_status vm_id — running / stopped / missing
  • vm_start vm_id, vm_stop vm_id

lib/config.sh

Config file lives at ~/.config/sysadmin-chronicles/config (survives game dir moves).

Variables stored:

SC_GAME_DIR=/home/user/Games/sysadmin-chronicles
SC_IMAGES_DIR=/home/user/Games/sysadmin-chronicles/images
SC_LIBVIRT_URI=qemu:///system
SC_INSTALL_DATE=2026-04-27
SC_INSTALLED_DEPS="libvirt qemu-system-x86 ..."  # what we added, for the log
  • config_read — sources the config file
  • config_write key value
  • config_show — pretty-prints current config

lib/save.sh

  • save_list — lists all save slots with name, date, trust score, quest progress
  • save_switch slot_name — switch active save
  • save_new slot_name — create a new empty save slot
  • save_reset [slot_name] — wipe a slot back to new-game state
  • save_export slot_name path — export save JSON for backup
  • save_import path slot_name — import a save JSON

Installer Design (install.sh)

Phase 1 — Welcome

╔══════════════════════════════════════════╗
║       SYSADMIN CHRONICLES — SETUP       ║
╚══════════════════════════════════════════╝

Welcome! This installer will:
  • Install a few system tools (KVM, QEMU, libvirt)
  • Set up a private virtual network for the game
  • Build three virtual machines (~30 minutes, once only)

Where would you like to install the game?
  [default: ~/Games/sysadmin-chronicles]  >

Phase 2 — System check (silent)

Internally calls check_deps. If all present, skip to Phase 4 silently.

Phase 3 — Dependency install (only if needed)

Your system is missing the following tools:
  • KVM virtualization support (qemu-system-x86)
  • Virtual machine manager (libvirt, virt-install)
  • SPICE display viewer (virt-viewer)
  • Cloud image tools (cloud-image-utils, genisoimage)

Install them now? You'll be asked for your password.  [Y/n]

After install:

  • Log installed packages to ~/.local/share/sysadmin-chronicles/install.log
  • Format: timestamp, package name, version, distro. Human-readable.
  • Note at end: "This log is kept so you know exactly what was added. See it at: ..."

Phase 4 — One-time network and storage setup

── Setting up game network ──────────────────
  ✓ Private game network created
  ✓ VM image storage configured at ~/Games/sysadmin-chronicles/images
  ✓ Game access keys generated

User never sees "libvirt", "storage pool", "sc-internal", "sc-images".

Phase 5 — VM build

── Building your game world ─────────────────
  This happens once and takes about 30 minutes.
  You can leave this running in the background.

  Building workstation (1/3) ........... ✓  8m 14s
  Building web server   (2/3) ........... ✓  4m 02s
  Building build server (3/3) ........... ✓  5m 31s
  Setting up quest scenarios ........... ✓  1m 48s

Phase 6 — Desktop entry

Create a desktop launcher so the game appears in your app menu?  [Y/n]

Creates ~/.local/share/applications/sysadmin-chronicles.desktop if yes.

Phase 7 — Done

╔══════════════════════════════════════════╗
║              SETUP COMPLETE!            ║
╚══════════════════════════════════════════╝

Start the game:
  bash ~/Games/sysadmin-chronicles/start-game.sh
  (or from your app menu if you created a launcher)

If you ever need to rebuild the virtual machines:
  bash ~/Games/sysadmin-chronicles/tools/vm/rebuild-vms.sh

Install log saved at:
  ~/.local/share/sysadmin-chronicles/install.log

Uninstaller Design (uninstall.sh)

Improved from current: shows sizes, explains consequences, three-tier removal.

Menu approach

╔══════════════════════════════════════════╗
║     SYSADMIN CHRONICLES — UNINSTALL     ║
╚══════════════════════════════════════════╝

What would you like to remove?

  1) Everything — full uninstall (recommended)
  2) Game world only — remove VMs, keep game files
  3) Save data only — reset to new game
  4) Custom — choose what to remove

  q) Cancel

>

"Everything" breakdown (shows before confirming)

This will remove:

  Game virtual machines (3 VMs + all snapshots)   ~38 GB
  VM image files on disk                           ~38 GB  ← ask separately
  Game network and storage configuration           <1 MB
  Game access keys (~/.ssh/sc_host_key)            <1 KB
  Desktop launcher (if created)                    <1 KB

  System packages (libvirt, QEMU, etc.)            NOT removed
  ↑ These were installed by your package manager.
    See ~/.local/share/sysadmin-chronicles/install.log
    if you want to remove them manually.

Keep VM image files? If you ever reinstall, keeping them
saves the 30-minute rebuild.  [Y/n — default: keep]

Type REMOVE to confirm:  >

What is never auto-removed

  • System packages (libvirt, qemu, virt-viewer, etc.)
  • Anything not prefixed with sc- in libvirt
  • Any other libvirt VMs or networks not owned by this game

VM Rebuild Tool (tools/vm/rebuild-vms.sh)

For when something goes wrong with a VM or the user wants a clean reset.

Usage:
  rebuild-vms.sh                  Rebuild all VMs from scratch
  rebuild-vms.sh --vm workstation Rebuild a single VM
  rebuild-vms.sh --revert         Revert all VMs to baseline snapshot (fast, ~30s)
  rebuild-vms.sh --revert --vm workstation

Menu (interactive):
  1) Revert all to last known good  (fast — restores baseline snapshot)
  2) Rebuild workstation            (~8 min — rebuilds from cloud image)
  3) Rebuild web server             (~4 min)
  4) Rebuild build server           (~5 min)
  5) Rebuild everything             (~20 min)
  q) Cancel

Key behavior:

  • Always confirm before destroying a VM
  • Show what quest progress will be affected
  • Offer to back up save data before proceeding
  • After rebuild, re-runs the appropriate quest-prep scripts and re-takes baseline snapshot

User Snapshots

Players can take their own named snapshots of any VM — useful before attempting something risky, or to bookmark a state they want to return to.

These are distinct from the game's automatic shift checkpoints and baseline snapshots. User snapshots are never pruned automatically.

The save management menu will include a VM Snapshots section:

VM Snapshots

  workstation (ares)
    1) before-ssh-experiment   2026-05-01 19:14
    2) checkpoint.shift-3      2026-05-01 22:00  [auto]
    3) baseline.day-one                          [protected]

  web server (hermes)
    1) my-nginx-fix            2026-05-02 11:30
    2) checkpoint.shift-3      2026-05-01 22:00  [auto]
    3) baseline.clean                            [protected]

  Actions: [t]ake snapshot  [r]evert  [d]elete  [q]uit

Taking a snapshot prompts for a name (letters, numbers, hyphens only). Reverting shows a confirmation with the snapshot date. Protected snapshots (baseline., checkpoint.) cannot be deleted from this menu.

Via tools/vm/rebuild-vms.sh --snapshot

For scripting or quick one-liners:

rebuild-vms.sh --snapshot --vm workstation --name before-risky-thing
rebuild-vms.sh --snapshot --all --name pre-shift-4
rebuild-vms.sh --revert  --vm workstation --name before-risky-thing

Storage note

Each VM snapshot is an internal qcow2 differential — typically 100 MB2 GB depending on how much disk has changed since the baseline. The uninstaller shows the total size of user snapshots separately so the user can decide whether to keep them.

lib/vm.sh additions needed

  • vm_snapshot_create vm_id name — with name validation
  • vm_snapshot_list vm_id — returns name, date, size, protection flag
  • vm_snapshot_revert vm_id name
  • vm_snapshot_delete vm_id name — refuses if name matches baseline.* or checkpoint.*

Save Management

Save file layout

~/.local/share/sysadmin-chronicles/
  saves/
    autosave.json          ← always-present auto save (current session)
    slot-1.json
    slot-2.json
    slot-3.json
  install.log

Save slot semantics

Save slots store JSON state only:

  • Trust score and history
  • Quest and ticket state
  • World flags
  • Inbox
  • In-world clock

VM state is not per-slot. The shift checkpoint snapshots (checkpoint.shift-N) are the VM save mechanism and are independent of JSON slots. This is a known limitation but keeps disk usage manageable.

When switching slots: if the VM state doesn't match the JSON slot's expected state, warn the user. They may need to revert VMs manually.

tools/save/manage-saves.sh

Usage:
  manage-saves.sh                 Show save slot menu
  manage-saves.sh --reset         Reset current save to new game
  manage-saves.sh --reset slot-1  Reset a specific slot
  manage-saves.sh --list          List all slots

Interactive menu:
  Current save: autosave  (Day 3, Trust: 67, 4/8 quests)

  1) autosave   Day 3  Trust 67  Q4/8  [active]
  2) slot-1     Day 1  Trust 50  Q1/8
  3) slot-2     —empty—
  4) slot-3     —empty—

  Actions: [s]witch  [n]ew  [r]eset  [e]xport  [i]mport  [q]uit

Reset save (standalone, accessible from start-game.sh)

The launcher start-game.sh should have an escape hatch:

start-game.sh --manage-saves     → opens save management menu
start-game.sh --reset-save       → confirms and resets to new game

Launcher Improvements (start-game.sh)

Current issues to fix:

  • Silently fails if images drive not mounted
  • No check that the libvirt network is up before starting
  • sleep 1 to wait for server is fragile

Improvements:

  • config_read to get SC_IMAGES_DIR, check it exists and is writable
  • Check libvirt network is active, start it if not (with clear message)
  • Poll server readiness on /healthz instead of sleeping
  • Show a brief status before launching SPICE: "Starting your workstation..."
  • On failure, show a plain-English error and the fix

Portable Installation Notes

The sc-images libvirt pool target can be any path the host OS can write to. The installer configures it to $SC_IMAGES_DIR (inside the game dir by default).

If the user puts the game on a game drive (/mnt/gamesdrive/sysadmin-chronicles/):

  • SC_IMAGES_DIR=/mnt/gamesdrive/sysadmin-chronicles/images
  • The libvirt pool points there
  • All qcow2 files live on the game drive
  • The launcher checks the drive is mounted before starting

If the drive is unmounted:

  ✗ Can't find your game world.
    The VM images are stored at /mnt/gamesdrive/sysadmin-chronicles/images
    but that location isn't available right now.

    Is your game drive plugged in and mounted?
    Once it's mounted, run start-game.sh again.

Dependency Log Format

~/.local/share/sysadmin-chronicles/install.log

# Sysadmin Chronicles — Install Log
# Created: 2026-04-27 14:32:01
# Distro:  arch (6.19.12-arch1-1)
# Game dir: /home/aaron/Games/sysadmin-chronicles
# Images:   /home/aaron/Games/sysadmin-chronicles/images

[INSTALLED] libvirt                  12.2.0   via pacman
[INSTALLED] qemu-system-x86         11.0.0   via pacman
[INSTALLED] qemu-hw-display-qxl     11.0.0   via pacman
[INSTALLED] qemu-hw-display-virtio-gpu  11.0.0  via pacman
[INSTALLED] qemu-ui-spice-core      11.0.0   via pacman
[INSTALLED] qemu-chardev-spice      11.0.0   via pacman
[INSTALLED] qemu-audio-spice        11.0.0   via pacman
[INSTALLED] virt-install            5.1.0    via pacman
[INSTALLED] virt-viewer             11.0     via pacman
[INSTALLED] cloud-image-utils       0.33     via pacman
[INSTALLED] cdrtools                3.02a09  via pacman
[INSTALLED] libisoburn              1.5.8    via pacman
[SKIPPED]   nodejs                           already installed

# To remove manually:
# sudo pacman -Rns libvirt qemu-system-x86 qemu-hw-display-qxl ...

File Layout After Install

~/Games/sysadmin-chronicles/     ← SC_GAME_DIR
  install.sh
  uninstall.sh
  start-game.sh
  content/
  server/
  frontend/
  docs/
  tools/
    lib/
      ui.sh
      deps.sh
      libvirt.sh
      vm.sh
      config.sh
      save.sh
    setup/
      check-host.sh
      first-run-setup.sh
      seed-vms.sh
    vm/
      rebuild-vms.sh
      build-vm.sh
      ...
    save/
      manage-saves.sh

  images/                        ← SC_IMAGES_DIR (libvirt pool points here)
    sc-workstation.qcow2         (~20 GB)
    sc-web-server.qcow2          (~8 GB)
    sc-build-machine.qcow2       (~10 GB)

~/.config/sysadmin-chronicles/config      ← install config (survives game dir moves)
~/.local/share/sysadmin-chronicles/
  saves/
    autosave.json
    slot-1.json ...
  install.log

Implementation Order

  1. tools/lib/ui.sh — all other scripts depend on this
  2. tools/lib/config.sh — needed by installer and launcher
  3. tools/lib/deps.sh — needed by installer
  4. tools/lib/libvirt.sh — needed by installer and rebuild tool
  5. tools/lib/vm.sh — needed by installer and rebuild tool
  6. tools/lib/save.sh — needed by save manager
  7. install.sh — assembles libs 15
  8. tools/vm/rebuild-vms.sh — assembles libs 1, 3, 4
  9. tools/save/manage-saves.sh — assembles libs 1, 2, 6
  10. uninstall.sh — assembles libs 1, 2, 4
  11. start-game.sh (improved) — assembles libs 1, 2
  12. Update check-host.sh UX
  13. README — manual install section, quick start

README Structure

## Quick Install

curl -fsSL .../install.sh | bash
# or
bash install.sh   # from downloaded zip

## Manual Install

<details>
<summary>For users who want full control or are troubleshooting</summary>
...per-distro dep tables, step-by-step...
</details>