Files
sysadmin-chronicles/docs/PROJECT_MAP.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

16 KiB
Raw Blame History

SYSADMIN CHRONICLES — PROJECT MAP

Living document. Update when files are added, moved, removed, or when architecture changes. Version 5.1 | Living document — update when files are added, moved, or removed.


ROOT STRUCTURE

sysadmin-chronicles/
│
├── server/                     ← NEW: Node.js game server
│   ├── src/
│   │   ├── index.js            Entry point — Express + WebSocket
│   │   ├── routes/             auth, state, tickets, mail, docs, sage, vms
│   │   ├── services/           ContentLoader, QuestEngine, TicketService,
│   │   │                       ValidationEngine, VMManager, TrustSystem,
│   │   │                       ProgressionSystem, EmailService, SageService,
│   │   │                       ShiftTimer, IncidentScheduler, ShiftReviewService,
│   │   │                       CertificationService, SaveState
│   │   └── lib/                ssh.js, virsh.js, command.js, eventBus.js, session.js
│   └── package.json
│
├── frontend/                   ← NEW: Svelte web HUD
│   ├── src/
│   │   ├── App.svelte          Root component, WebSocket, panel routing
│   │   ├── components/         TicketsPanel, MailPanel, DocsPanel, SagePanel,
│   │   │                       VmsPanel, ProfilePanel, HeaderBar, SidebarTabs
│   │   ├── lib/api.js          REST API fetch wrapper
│   │   └── main.js
│   ├── dist/                   Built output (served by game server)
│   └── package.json
│
├── scripts/
│   └── start-game.sh           One-shot: start server + open SPICE workstation viewer
│
├── docs/
│   ├── ARCHITECTURE.md               System architecture
│   ├── CHARACTERS.md                 All characters — bios, relationships, story hooks
│   ├── COMPANY_LORE.md               World, company, products, tone guidelines
│   ├── INSTALLER_PLAN.md             Installer design and packaging
│   ├── PRESSURE_PROFILES.md          Time-pressure escalation schema and authoring guide
│   ├── PROJECT_MAP.md                ← this file
│   ├── ROADMAP.md                    Development phases and content status
│   ├── RUNTIME_DEPENDENCIES.md       Host dependencies and version requirements
│   ├── SAVE_SYSTEM.md                Save model, VM persistence policy, recovery flows
│   ├── SNAPSHOT_CHAIN.md             VM snapshot chain and baseline management
│   ├── STORY_DESIGN_CONTEXT.md       How story works — narrative arc, quest model, design constraints
│   ├── VM_BUILD_SYSTEM.md            VM build and provisioning system
│   ├── WORKSTATION_POLISH_BACKLOG.md Outstanding UX polish items
│   └── codex-specs/
│
├── content/                    ← data-driven content loaded by Node.js server
│   ├── quests/         quest JSON files (being reworked — see STORY_DESIGN_CONTEXT.md)
│   ├── tickets/        ticket JSON files (being reworked)
│   ├── incidents/      incident JSON files (being reworked)
│   ├── pressure_profiles/  escalation profiles (schema in PRESSURE_PROFILES.md)
│   ├── dialogue/       character dialogue JSON files (being reworked)
│   ├── world_flags/    world_flags.json (central registry)
│   ├── docs/           onboarding, sage_content, internal_docs, etc.
│   ├── progression/    trust_unlocks.json, access_tiers.json
│   └── vm_profiles/    workstation.json, web_server.json, build_machine.json
│
├── tools/
│   ├── setup/          check-host.sh, seed-vms.sh, first-run-setup.sh, uninstall.sh
│   ├── vm/             build-vm.sh, build-*.sh, snapshot-all.sh, suppress-maintenance-noise.sh
│   │   ├── profiles/   workstation.sh, web-server.sh, build-machine.sh
│   │   └── quest-prep/ Q001Q008 prep/post scripts
│   └── content/        validate-content.js (zero-error gate), verify-clue-fingerprints.js
│
├── company-website/            Axiom Works public website (static HTML/CSS)
│   ├── index.html              Home — hero, product highlights, stats
│   ├── about.html              Company story, values, contact
│   ├── people.html             Team page — Dave, Marcus, Priya, Sarah + filler staff
│   ├── products.html           AxiomFlow, AxiomDash, AxiomSync product pages
│   ├── style.css               Shared corporate CSS (navy/blue scheme)
│   └── assets/                 logo.png, portrait photos for each NPC
│
├── vm/                         images/, snapshots/, cloud-init/, probes/
├── package.json
└── README.md

COMPANY WEBSITE

Static HTML/CSS site serving as the public-facing Axiom Works company website, accessible from the workstation VM.

URL inside the VM: http://www.axiomworks.corp/ (no port)

How it works:

  • The game server serves company-website/ at /company/ (port 3000)
  • nginx is installed in the workstation VM and proxies axiomworks.io and www.axiomworks.io (port 80) → game server port 3000 at /company/
  • /etc/hosts in the workstation maps both hostnames to 127.0.0.1 (localhost → nginx)
  • Result: the player sees a clean http://www.axiomworks.io/ URL in Chromium with no port number

Pages: Home (index.html), About (about.html), Our Team (people.html), Products (products.html)

Team page portraits: NPC photos live in company-website/assets/. The player is not featured on the website.

Domain note: axiomworks.corp uses the IANA-reserved .corp TLD (reserved 2024, can never be publicly delegated). No registration needed — it will never resolve on the real internet. The in-VM /etc/hosts + nginx approach is sufficient for any build.

Player portraits (for the HUD profile panel) are separate from the website portraits. They live in server/public/portraits/ and are served at /public/portraits/. The player selects one via the Profile panel; the choice persists in save.json as player_portrait.


BOOT FLOW (Node.js Server)

bash scripts/start-game.sh
  ↓
node server/src/index.js
  1. ContentLoader.load()      — reads all content/**/*.json into memory
  2. SaveState.load()          — reads ~/.local/share/sysadmin-chronicles/save.json
                                  or creates fresh save
  3. TrustSystem.initialize()  — hydrates trust score + unlock state
  4. ProgressionSystem.initialize()
  5. QuestEngine.initialize()  — restores quest states from save
  6. TicketService.initialize()
  7. EmailService.initialize() — restores inbox, seeds T001 email on fresh save
  8. ShiftTimer.start()        — starts shift clock
  9. IncidentScheduler.start() — begins pressure tick loop (every 30s)
  10. VMManager.ensureWorkstationLive() — virsh start sc-workstation if needed
  ↓
Express + WebSocket listening on PORT (default 3000)
  ↓
remote-viewer opens SPICE connection to sc-workstation
Player sees XFCE desktop → Chromium opens HUD → game is live

TICKET COMPLETION FLOW

Player clicks "Mark Complete" on ticket in HUD
  ↓
POST /api/tickets/:id/complete
  ↓
TicketService.markComplete(ticketId)
  → load ticket + linked quest JSON
  → for each solution_branch (sorted by priority DESC):
      ValidationEngine.check(vmId, branch.validation.rules)
        → VMManager.getIP(vmId)
        → SSH as opsbridge using sc_host_key
        → run each rule check (file_exists, service_state, etc.)
      if all rules pass → winning branch found
  → TrustSystem.adjust(branch.trust_delta)
  → WorldFlags.set(branch.world_flags)
  → QuestEngine.completeQuest(questId)
  → EmailService.send(follow-up NPC email if negative branch)
  → SaveState.write()
  → broadcast trust:changed, mail:new via WebSocket
  ↓
Response: { passed, branch, trust_delta, failures }
HUD shows success toast or failure details

VM IDENTITY TABLE

vm_id SC constant libvirt domain hostname distro ssh_user mgmt_user always_live Quests
workstation SC.VM_WORKSTATION sc-workstation ares Debian 12 player opsbridge yes Q001
web_server SC.VM_WEB_SERVER sc-web-server hermes Debian 12 player no Q002Q005, Q007
build_machine SC.VM_BUILD_MACHINE sc-build-machine vulcan Arch Linux player no Q006, Q008

See docs/VM_BUILD_SYSTEM.md for full build system documentation and profile authoring guide.

SSH key: all host→guest connections use ~/.ssh/sc_host_key (BatchMode, no password).

Baseline snapshots:

  • workstation: baseline.day-one
  • web_server, build_machine: baseline.clean

TERMINAL ARCHITECTURE

The player uses a real Tilix terminal inside the workstation VM (sc-workstation / ares). No terminal simulation. SSH to target VMs is real SSH. There is no in-game terminal widget.

Player opens Tilix on the workstation XFCE desktop
  → types: ssh hermes
  → real SSH to sc-web-server using player's authorized_keys
  → works directly on the target VM

Host-side validation (triggered by "Mark Complete" in HUD):
  ValidationEngine.js SSHes as 'opsbridge' → sudo -H -i -u player
  Runs rule checks (file_exists, service_state, etc.)
  Returns pass/fail to game server

Host SSH options (used by ValidationEngine.js and VMManager.js):

-o StrictHostKeyChecking=no
-o BatchMode=yes
-o ConnectTimeout=5
-o LogLevel=ERROR
-i ~/.ssh/sc_host_key

SERVICE DEPENDENCY GRAPH (Node.js server)

eventBus.js (Node.js EventEmitter — no deps)
  └─ consumed by: all services

ContentLoader
  └─ consumed by: QuestEngine, TicketService, ValidationEngine, TrustSystem,
                  ProgressionSystem, IncidentScheduler, EmailService, SageService

VMManager
  ← wraps virsh.js + ssh.js
  ← called by QuestEngine (start required VMs on quest activation)
  ← called by ValidationEngine (get VM IP for SSH)

ValidationEngine
  ← calls VMManager.getIP(vmId)
  ← SSHes as opsbridge → runs rule checks (file_exists, service_state, etc.)
  ← called by TicketService on mark-complete

QuestEngine
  ← calls VMManager to start required VMs
  ← calls ValidationEngine via TicketService
  ← calls TrustSystem, WorldFlags, EmailService on resolution
  → emits via eventBus: quest:activated, quest:resolved, ticket:received

IncidentScheduler
  ← reads WorldFlags for trigger conditions
  ← tick drives escalation step advancement
  → emits via eventBus: incident:activated, incident:escalated, incident:resolved

TrustSystem
  ← called by QuestEngine on branch resolution
  ← called by IncidentScheduler for ignored incident penalties
  → emits via eventBus: trust:changed

SaveState
  ← called by QuestEngine, TrustSystem, ProgressionSystem
  ← reads/writes ~/.local/share/sysadmin-chronicles/save.json

KEY MODULES

Server (server/src/)

Module File Responsibility
Entry point index.js Express + WS, service wiring, static serving
ContentLoader services/ContentLoader.js Load all content/ JSON at startup
QuestEngine services/QuestEngine.js Quest state machine
TicketService services/TicketService.js Ticket state, mark-complete, branch resolution
ValidationEngine services/ValidationEngine.js SSH rule evaluation (all rule types)
VMManager services/VMManager.js virsh wrappers, IP resolution
TrustSystem services/TrustSystem.js Score, unlocks, revocation
ProgressionSystem services/ProgressionSystem.js Unlocked VMs, docs, access
EmailService services/EmailService.js Inbox, follow-ups, reply options
SageService services/SageService.js Rule-based dialogue / KB
ShiftTimer services/ShiftTimer.js Shift clock, 30s tick broadcasts
IncidentScheduler services/IncidentScheduler.js Pressure tick, incident injection
ShiftReviewService services/ShiftReviewService.js End-of-shift review email
CertificationService services/CertificationService.js Cert awards after quest chains
SaveState services/SaveState.js Read/write save.json
ssh.js lib/ssh.js Promisified SSH execution
virsh.js lib/virsh.js virsh command wrappers
eventBus.js lib/eventBus.js Node.js EventEmitter for service coordination

Frontend (frontend/src/)

Component File Responsibility
Root App.svelte Panel routing, WebSocket connection
Tickets TicketsPanel.svelte List, detail, mark-complete
Mail MailPanel.svelte Inbox, message, reply buttons
Docs DocsPanel.svelte Trust-gated doc viewer
Sage SagePanel.svelte Chat / KB search
VMs VmsPanel.svelte Live VM status indicators
Header HeaderBar.svelte Trust, shift timer, mail badge
API lib/api.js REST fetch wrapper

CONTENT DOMAINS

Domain Purpose
quests/ Objective chains, clue fingerprints, validation rules, branch priorities
tickets/ Player-facing problem statements with initial/current priority
incidents/ Dynamic pressure events with blast_radius and escalation steps
dialogue/ Workplace messages, hints, follow-ups, series threads
pressure_profiles/ Reusable escalation templates referenced by quest branches
world_flags/ Central registry — all world state flags declared here
docs/ Internal documentation + Sage/help content (trust-gated)
progression/ Trust thresholds, unlocks, revocation rules, access tiers
vm_profiles/ Domain names, hostnames, snapshots, networks, resource budgets

FILE NAMING CONVENTIONS

  • Quest files: Q{NNN}-{kebab-case-title}.json
  • Ticket files: T{NNN}.json
  • Incident files: I{NNN}-{kebab-case-title}.json
  • Dialogue files: {character}-Q{NNN}.json or {character}-Q{NNN}-{variant}.json
  • Quest prep scripts: Q{NNN}-prep.sh
  • VM profiles: {snake_case}.json

CONTENT VALIDATION CHECKS

Run: node tools/content/validate-content.js — must exit 0 (zero errors).

Check Rule
JSON well-formed All content files parse without error
No duplicate IDs Unique across quests, tickets, incidents, pressure profiles, dialogue
World flags Every referenced flag exists in world_flags/world_flags.json
required_vms Every entry maps to a valid VM profile
blast_radius Every entry maps to an existing incident file
linked_quest Every ticket's linked_quest maps to an existing quest
ticket_id Every quest's ticket_id maps to an existing ticket
Branch priority Priorities unique per quest (no ties)
follow_up_incident Maps to an existing incident file
pressure_profile Maps to an existing pressure profile file
series_id Every series_id has at least two dialogue members
revokes Trust unlock revoke entries reference valid unlock strings
clue_fingerprint Evidence rule types are valid

KNOWN GAPS (Post-Redesign)

These are gaps in the v4.0 Node.js + Svelte implementation. All content is authored, validator-clean, and reused unchanged.

P0 — Blocking for first playable shift

Gap Notes
Phase 7 workstation VM verification Confirm SPICE display, Chromium autostart, Tilix as default work end-to-end on a freshly seeded VM
Phase 10 full playtest Boot all VMs, play Q001→Q002, validate full server→SSH→HUD loop

P1 — Required before broader testing

Gap Notes
Clue quality as system degrades Evidence should remain legible as incidents escalate (I001/I002/I003 escalation pass)
Viewer smoothness remote-viewer SPICE path is functional but not final-UX smooth; lower priority with real XFCE desktop

P2 — Polish / completeness

Gap Notes
WORKSTATION_POLISH_BACKLOG.md items See that file for outstanding desktop UX polish

GENERATED / LARGE ASSETS

Created by CLI tooling, not hand-managed:

  • vm/images/*.qcow2
  • Imported libvirt domain XML
  • Baseline snapshot exports or manifests
  • Shift checkpoint snapshots
  • Packaged Linux build artifacts