This walks through getting RuneVault running locally end-to-end.
- Go 1.22+ —
go version - Node 22+ —
node --version - Docker and Docker Compose —
docker --version - PostgreSQL client (optional, for direct DB access) —
psql --version - A Steam account that owns Quinfall, only if you want the live game connection working. The HTTP API and most features work without one (see "Headless mode" below).
git clone https://github.com/Velm14/runevault.git
cd runevault
cp .env.example .envOpen .env in your editor and set, at minimum:
# Two 64-hex-char secrets — never reuse examples:
JWT_SECRET=$(openssl rand -hex 32)
TOKEN_ENCRYPTION_KEY=$(openssl rand -hex 32)
DB_PASSWORD=<pick anything strong>- Go to https://discord.com/developers/applications → New Application.
- Under OAuth2 → General:
- Copy the Client ID into
DISCORD_CLIENT_ID. - Click Reset Secret, copy into
DISCORD_CLIENT_SECRET. - Add a Redirect URI:
http://localhost:8080/api/auth/discord/callback.
- Copy the Client ID into
- In
.env, setDISCORD_REDIRECT_URIto the same URI exactly. FRONTEND_URL=http://localhost:5174(where users land after login).
STEAM_USER=your_steam_username
STEAM_PASS=your_steam_password
GAME_TOKEN=<capture from a real Quinfall client>
HW_FINGERPRINT=<capture from a real Quinfall client>
GAME_SERVER=<host:port of the Quinfall game server>GAME_TOKEN and HW_FINGERPRINT are values your real Quinfall client
sends during login. You'll need to capture them with packet inspection of
a real session — see docs/protocol/reverse-engineering-report.md
for the framing details. The repo does not ship these values.
If you don't have Quinfall, set:
SKIP_PUBLIC_CONNECTION=trueThe backend boots without attempting the shared game-server login.
Auction search will return an error (no live connection), but everything
else (Discord login, alerts CRUD, price-history queries against scraped
data, the frontend UI) works. The steam-auth-public sidecar container
still starts under Docker Compose, but its credentials check fails fast
and it idles harmlessly.
docker compose up -d --build backend app-db frontend caddyThis starts:
app-db— PostgreSQL 16. Migrations run automatically on first start.backend— Go HTTP API on:8080.frontend— nginx serving the built React app.caddy— reverse proxy (you usually don't need it in dev; skip it if you're going to use the Vite dev server below).steam-auth-public— the Steam login sidecar (skipped ifSKIP_PUBLIC_CONNECTION=true).
Check it's alive:
docker compose ps
curl http://localhost:8080/api/auth/me # → 401 unauthorized (correct)cd frontend
npm install
npm run devVite serves at http://localhost:5174. It proxies /api/* to the
backend (see frontend/vite.config.ts). Open the URL, click Sign in
with Discord, complete the OAuth flow.
# Backend logs
docker compose logs -f backend
# Restart just the backend after a code change
go build ./... && docker compose up -d --build backend
# Reset the database
docker compose down -v app-db && docker compose up -d app-db
# Open a psql shell
docker compose exec app-db psql -U quinfall
# Build the frontend the way Docker does
cd frontend && npm run buildThe scraper is off by default (SCRAPER_ENABLED=false). To enable:
-
Provision a separate Steam account from the one in
STEAM_USER— sharing the same account causes the game server to kick one of the sessions withLogonSessionReplaced. -
Fill in the scraper-specific env vars in
.env:SCRAPER_ENABLED=true SCRAPER_STEAM_USER=... SCRAPER_STEAM_PASS=... SCRAPER_GAME_TOKEN=... # optional, falls back to GAME_TOKEN SCRAPER_HW_FINGERPRINT=... # optional, falls back to HW_FINGERPRINT # SCRAPER_SOCKS_PROXY=socks5://host:port # optional
-
Start the scraper profile:
docker compose --profile scraper up -d steam-auth-scraper backend
The scraper writes to the listing_snapshots table, which powers price
history, market movers, deals, and alert matching.
npx web-push generate-vapid-keys --json
# Paste the keys into .env:
VAPID_PUBLIC_KEY=...
VAPID_PRIVATE_KEY=...
ADMIN_CONTACT_EMAIL=you@example.comThe frontend will then prompt premium users to enable push for alerts.
Free CAPTCHA-like challenge for auction search. Get a site key + secret from https://dash.cloudflare.com/?to=/:account/turnstile:
TURNSTILE_SECRET=...The frontend's site key is configured in frontend/src/hooks/useTurnstile.ts.
docs/ARCHITECTURE.md— backend layers and data flow.docs/TROUBLESHOOTING.md— common failures.docs/protocol/— how the game protocol works.