No description
  • TypeScript 82.7%
  • Svelte 16.2%
  • CSS 0.8%
  • HTML 0.3%
Find a file
2026-09-18 20:00:41 +02:00
src fix new games ignoring the selected pack, tighten replace warning 2026-09-18 20:00:41 +02:00
tests fix new games ignoring the selected pack, tighten replace warning 2026-09-18 20:00:41 +02:00
.gitignore first commit 2026-09-17 10:30:57 +02:00
.oxfmtrc.json first commit 2026-09-17 10:30:57 +02:00
.oxlintrc.json first commit 2026-09-17 10:30:57 +02:00
AGENTS.md add mission trees and make construction missions the only way to build 2026-09-18 19:15:49 +02:00
deno.json split missions into their own file, tag events by mission type, and harden saves with valibot 2026-09-18 18:20:25 +02:00
deno.lock split missions into their own file, tag events by mission type, and harden saves with valibot 2026-09-18 18:20:25 +02:00
index.html first commit 2026-09-17 10:30:57 +02:00
README.md improve mobile layout and food economy 2026-09-18 15:29:05 +02:00
svelte.config.js first commit 2026-09-17 10:30:57 +02:00
tsconfig.json first commit 2026-09-17 10:30:57 +02:00
vite.config.ts improve mobile layout and food economy 2026-09-18 15:29:05 +02:00

Path of Steel

Path of Steel is a web based, text-based game inspired by Oregon Trail.

In a not too distant future, society is being rebuilt by a small group of skilled people with access to an battle mech which they now use for scavenging and construction.

The player sets up the party of skilled people and sends them and/or the mech on missions to scavenge resources or build up the community. During those missions, random events can happen that can be beneficial or detrimental to the mission. They also pick up survivors and bring them in, as well as sometimes meet new people to add to the team.

Implementation

The game is implemented as a web-based Svelte application that can be played entirely played in the browser. The game is running using deno. The user's game save is in localStorage. The player can have many save slots for ongoing games. The user is presented with a console-style interface with the choices as buttons to click on.

Events are fully configurable in JSON files that can be edited before launching the game. Resources, skills and starting amounts are also configured in JSON files. Names, gender and skills of the starting team are random.

Deploying on a server with nginx

The game is a static single-page app once built: deno task build produces a dist/ folder of plain HTML/CSS/JS. You can build on your local machine and just upload that folder — the build output has no platform-specific parts, so it works anywhere nginx can serve static files. No Deno/Node process needs to run on the server, and Deno does not need to be installed there.

  1. On your local machine, install Deno (skip if already installed):

    curl -fsSL https://deno.land/install.sh | sh
    

    Make sure ~/.deno/bin is on your PATH afterwards.

  2. From the project directory, build the static site:

    deno task build
    

    This writes the deployable files to dist/. All game content lives in src/data/*.json, so make any content edits before this step; dependencies are fetched automatically on the first run.

    The build adds Subresource Integrity hashes (integrity="sha384-...") to the <script> and <link> tags in dist/index.html (via vite-plugin-sri3), so browsers reject any tampered JS/CSS files.

  3. Upload dist/ to the server, for example into /var/www/path-of-steel (adjust user@server):

    rsync -av --delete dist/ user@server:/var/www/path-of-steel/
    

    On the server, make sure the directory is writable by you (e.g. sudo mkdir -p /var/www/path-of-steel && sudo chown $USER /var/www/path-of-steel). Plain scp works too: scp -r dist/. user@server:/var/www/path-of-steel/.

  4. Create an nginx server block, e.g. /etc/nginx/sites-available/path-of-steel:

    server {
        listen 80;
        server_name example.com;
    
        root /var/www/path-of-steel;
        index index.html;
    
        # Security headers. nginx's add_header is only inherited by a location
        # if that location defines no add_header of its own, so the shared set
        # lives in one snippet that every location includes (see below).
        include snippets/path-of-steel-headers.conf;
    
        # SPA fallback: unknown paths are handled by the app's entry point.
        location / {
            try_files $uri $uri/ /index.html;
            include snippets/path-of-steel-headers.conf;
        }
    
        # Long-lived caching for hashed build assets.
        location /assets/ {
            expires 1y;
            add_header Cache-Control "public, immutable";
            include snippets/path-of-steel-headers.conf;
        }
    }
    

    Replace example.com with your domain (or the server's IP).

    Then create the headers snippet, e.g. /etc/nginx/snippets/path-of-steel-headers.conf:

    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
    add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;
    

    The CSP is minimal on purpose: the app is fully self-contained (no CDN, no API calls, no images, no inline scripts or styles), so default-src 'self' covers scripts, styles, fonts, images, and connections in one stroke. The four extra directives exist because they do not fall back to default-src: object-src 'none' bans plugins, base-uri 'self' blocks <base> hijacking, form-action 'self' contains any injected form, and frame-ancestors 'none' (with the belt-and-braces X-Frame-Options) forbids framing entirely. Script and stylesheet tags carry SRI hashes (see step 2), so a tampered asset is rejected even though it is same-origin.

  5. Enable the site and reload nginx:

    sudo ln -s /etc/nginx/sites-available/path-of-steel /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    
  6. Open the site in a browser. To add HTTPS, run sudo certbot --nginx -d example.com (Certbot rewrites the server block for you).

To ship an update later, re-run deno task build locally and repeat the upload from step 3.