diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..126dcb1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,121 @@ +# CLAUDE.md — OTIVM Project + +This file is read automatically by Claude Code at the start of every session. +Read it completely before doing anything else. + +--- + +## What OTIVM Is + +A browser-based Roman merchant idle game. One screen, one resource loop. +The narrative unfolds through trade goods and journal entries. +It is a standalone light-hearted project — not part of CIVICVS or TESSERA infrastructure. + +**Live URL:** https://otium.civicus.us +**Repo:** https://gitea.barternetwork.us/TheRON/OTIVM (branch: main) + +--- + +## Stack + +- React 19 + Vite frontend +- Node.js v22 +- PM2 under otivm user (never root) +- Python venv at /home/otivm/venv (never run Python outside venv) +- No database — JSON flat files for any local state + +--- + +## Ground Rules — Do Not Violate + +- **Never run PM2 as root** — always as otivm user +- **Never run Python outside the venv** — /home/otivm/venv always +- **Never commit secrets** — no tokens, no keys, no passwords in any file +- **Never push to main without testing** — test locally on port 3000 first +- **One screen, one loop** — do not add complexity without explicit instruction +- **No database** — JSON flat files only, consistent with TheRON design principles + +--- + +## Container Facts + +| Property | Value | +|----------|-------| +| Hostname | otivm-dev | +| LAN IP | 10.0.0.23 | +| WireGuard | 10.110.0.18 | +| App user | otivm | +| App port | 3000 | +| Python venv | /home/otivm/venv | +| PM2 home | /home/otivm/.pm2 | +| Repo path | /home/otivm/OTIVM | + +--- + +## PM2 Commands + +```bash +# Always as otivm user +pm2 start ecosystem.config.js +pm2 restart otivm +pm2 logs otivm +pm2 save +``` + +--- + +## Git Workflow + +```bash +git add +git commit -m "imperative mood, under 72 chars" +git push origin main +``` + +Commit messages: imperative mood, present tense, under 72 characters. +Example: `Add trade route unlock mechanic` not `Added trade routes`. + +--- + +## TheRON Infrastructure Context + +- **wg-pk** (198.58.111.109) — Linode hub, Nginx proxy, WireGuard hub +- **srv-a** (10.0.0.11) — dev Proxmox host, runs this container +- **mcp.civicus.us** — gitea-mcp server, gives Claude chat read access to this repo +- **Gitea** — https://gitea.barternetwork.us (owner: TheRON) + +--- + +## Design Notes + +The game carries quiet atmospheric references to Mesolithic cultures +(Maglemosian, Ertebølle, Sauveterrian, Azilian) through trade goods, +place names, and merchant journal entries. These are never labelled +academically — they are atmosphere only. The amber road, the northern +forests, the flint from the Pyrenean foothills. + +The Roman merchant's journey: Ostia → Capua → Brundisium → Carthago → Alexandria. +Five chapters. Five trade routes. Five journal entries. + +--- + +## Session Start Checklist + +1. Read this file ✓ +2. Check `git status` and `git log --oneline -5` +3. Check `pm2 list` (as otivm user) +4. Proceed with the task + +--- + +## Aliases + +| Location | Alias | Effect | +|----------|-------|--------| +| root@otivm-dev ~/.bashrc | webmin-on | systemctl start webmin | +| root@otivm-dev ~/.bashrc | webmin-off | systemctl stop webmin | +| otivm@otivm-dev ~/.bashrc | work | cd ~/OTIVM && claude | + +--- + +*CLAUDE.md — OTIVM — TheRON* diff --git a/README.md b/README.md index 2adecfb..1f1f90b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,61 @@ -# otivm +# OTIVM +*otium* — Latin: leisure, rest, creative withdrawal. The condition under which civilisation advances. + +OTIVM is a browser-based idle game with a Classical Roman motif. It is a standalone light-hearted project under TheRON, intended as a low-pressure development activity and a public-facing entry point to the Roman world shared across TheRON projects. + +--- + +## Concept + +You are a Roman merchant. You start with a single trade route and a handful of denarii. You build a network spanning the known world — from Ostia to Alexandria — one dispatch at a time. The narrative unfolds through the goods you trade and the journal entries they unlock. + +The core tension is Roman in origin: *otium* versus *negotium* — leisure versus productive work. Active trading accumulates wealth. Rest and reflection accumulate *auctoritas* — the reputation and wisdom that unlocks what comes next. + +--- + +## Where It Runs + +| Property | Value | +|----------|-------| +| URL | https://otium.civicus.us | +| Container | LXC 1105 (otivm-dev) on srv-a | +| WireGuard | 10.110.0.18 | +| LAN IP | 10.0.0.23 | +| App port | 3000 | +| App user | otivm | +| Process manager | PM2 (pm2-otivm systemd service) | +| Proxy | Nginx on wg-pk (198.58.111.109) | + +--- + +## Stack + +- **Frontend:** React 19, Vite +- **Runtime:** Node.js v22 +- **Process manager:** PM2 under otivm user +- **Python:** venv at /home/otivm/venv (auto-activates on login) + +--- + +## Repository + +- **Gitea:** https://gitea.barternetwork.us/TheRON/OTIVM +- **Branch:** main +- **Push node:** otivm@otivm-dev (10.0.0.23) +- **License:** BSD 2-Clause + +--- + +## Design Principles + +- One screen, one resource loop — ship fast +- Narrative unfolds through goods and journal entries, not cutscenes +- Roman vocabulary throughout — Latin terms, Roman place names, Roman motifs +- Atmosphere carries quiet references to Mesolithic cultures and trade routes — the deep prehistory beneath the Roman world +- Never academic, always entertaining +- No backend required initially — React artifact first + +--- + +*OTIVM is part of TheRON — The Residential Owner-operated Network.* diff --git a/docs/infrastructure.md b/docs/infrastructure.md new file mode 100644 index 0000000..3c46673 --- /dev/null +++ b/docs/infrastructure.md @@ -0,0 +1,271 @@ +# OTIVM Infrastructure + +*Established: April 2026* + +This document describes the complete infrastructure built for OTIVM. +It is the authoritative reference for all incoming assistants and the project owner. + +--- + +## 1. Overview + +OTIVM runs on a dedicated LXC container (CT 1105) on the TheRON development +Proxmox host (srv-a). It is proxied through the WireGuard mesh hub (wg-pk) +and served over HTTPS at https://otium.civicus.us. + +OTIVM is completely isolated from CIVICVS and TESSERA infrastructure. +It shares only the WireGuard mesh and the Nginx proxy node. + +--- + +## 2. Network Topology + +``` +Browser + → otium.civicus.us (DNS A record → 198.58.111.109) + → wg-pk Nginx (HTTPS, SSL termination) + → WireGuard tunnel + → otivm-dev (10.110.0.18) + → Node.js app (port 3000) +``` + +--- + +## 3. DNS + +| Record | Type | Value | +|--------|------|-------| +| otium.civicus.us | A | 198.58.111.109 | + +No AAAA record. Traffic is proxied through wg-pk to a container +with no public IPv6. Consistent with shell.infra.civicus.us pattern. + +DNS is managed in BIND9 on the Linode DNS server. +Glue records are at Gandi. + +--- + +## 4. Container — CT 1105 + +| Property | Value | +|----------|-------| +| VMID | 1105 | +| Name | otivm-dev | +| Proxmox host | srv-a (10.0.0.11) | +| Template | debian-12-standard_12.12-1_amd64 | +| Memory | 512MB | +| Cores | 2 | +| Disk | 8GB (local-lvm) | +| LAN IP | 10.0.0.23/24 | +| Gateway | 10.0.0.1 | +| WireGuard IP | 10.110.0.18/32 | +| Unprivileged | yes | + +--- + +## 5. WireGuard + +This container is peer 10.110.0.18 on the TheRON WireGuard mesh. +The hub is wg-pk (198.58.111.109, mesh address 10.110.0.1). + +### /etc/wireguard/wg0.conf on otivm-dev + +```ini +[Interface] +Address = 10.110.0.18/32 +ListenPort = 51820 +PrivateKey = + +[Peer] +PublicKey = 1+Wb++fjXNbY0joOvj4AZvJgF6b125YOPSFsmNqVo3I= +AllowedIPs = 10.110.0.0/22 +Endpoint = 198.58.111.109:51820 +PersistentKeepalive = 25 +``` + +### Public key + +``` +GmJcIZguLxLjh8CNEITp5XUwb1xIDXBYOW/FHj47YUw= +``` + +### Peer stanza on wg-pk + +```ini +[Peer] +PublicKey = GmJcIZguLxLjh8CNEITp5XUwb1xIDXBYOW/FHj47YUw= +AllowedIPs = 10.110.0.18/32 +Endpoint = 73.36.246.43:51820 +``` + +### Service management + +```bash +systemctl status wg-quick@wg0 +systemctl restart wg-quick@wg0 +wg show +ping -c 3 10.110.0.1 # test mesh +``` + +--- + +## 6. Nginx on wg-pk + +File: /etc/nginx/sites-available/otium.civicus.us + +```nginx +server { + listen 80; + listen [::]:80; + server_name otium.civicus.us; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name otium.civicus.us; + + ssl_certificate /etc/letsencrypt/live/otium.civicus.us/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/otium.civicus.us/privkey.pem; + + location /.well-known/acme-challenge/ { + root /var/www/html; + } + + location / { + proxy_pass http://10.110.0.18:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } +} +``` + +--- + +## 7. SSL Certificate + +| Property | Value | +|----------|-------| +| Domain | otium.civicus.us | +| Authenticator | --nginx | +| Certificate path | /etc/letsencrypt/live/otium.civicus.us/ | +| Issued | April 2026 | +| Expires | July 2026 | +| Auto-renewal | certbot.timer (systemd, twice daily) | + +Test renewal: `certbot renew --dry-run` on wg-pk. + +⚠ RULE: Always issue SSL certificates FIRST with --nginx authenticator, +then add any basic auth after. Never reverse this order. + +--- + +## 8. Application Stack + +| Component | Value | +|-----------|-------| +| Runtime | Node.js v22.22.2 | +| npm | 10.9.7 | +| App user | otivm | +| App port | 3000 | +| PM2 service | pm2-otivm (systemd, enabled) | +| Python | 3.11.2 | +| Python venv | /home/otivm/venv | +| Claude Code | v2.1.119 | + +--- + +## 9. Users + +| User | Role | +|------|------| +| root | Container administration only | +| otivm | App user — all app activity, PM2, git, Claude Code | + +PM2 is always run as otivm. Never as root. +Python is always run inside /home/otivm/venv. Never outside. + +--- + +## 10. Webmin + +Webmin is installed on otivm-dev for file management and container access. +It is disabled by default and started manually when needed. + +```bash +# Aliases in /root/.bashrc +webmin-on # systemctl start webmin +webmin-off # systemctl stop webmin +``` + +Port 10000. Not exposed beyond the container network. +Auto-start is disabled — Webmin is off after every reboot. + +--- + +## 11. Gitea + +| Property | Value | +|----------|-------| +| Repo | https://gitea.barternetwork.us/TheRON/OTIVM | +| Branch | main | +| Push user | otivm@otivm-dev | +| Clone path | /home/otivm/OTIVM | +| License | BSD 2-Clause | + +Git credentials are stored in /home/otivm/.git-credentials (via credential.helper store). + +--- + +## 12. Backup + +Baseline backup taken immediately after provisioning, before any code deployed: + +``` +/var/lib/vz/dump/vzdump-lxc-1105-2026_04_25-04_10_53.tar.zst +``` + +Size: 551MB. Downloaded offline via Webmin. + +Backup command: +```bash +vzdump 1105 --storage local --compress zstd --mode stop +``` + +--- + +## 13. Quick Reference + +### Check everything is running + +```bash +# On otivm-dev +systemctl status wg-quick@wg0 +ping -c 3 10.110.0.1 + +# As otivm user +pm2 list +``` + +### Test the app endpoint + +```bash +curl -s http://localhost:3000/ +``` + +### Test through the full stack + +```bash +curl -s https://otium.civicus.us/ +``` + +--- + +*docs/infrastructure.md — OTIVM — April 2026* diff --git a/docs/provisioning.md b/docs/provisioning.md new file mode 100644 index 0000000..7b4b1eb --- /dev/null +++ b/docs/provisioning.md @@ -0,0 +1,285 @@ +# Container Provisioning — srv-a Ground Rules + +*Established: April 2026* + +This document records the correct procedure for provisioning new Debian 12 LXC +containers on srv-a (dev Proxmox, 10.0.0.11). Follow this exactly. +Every step was learned from direct experience — do not skip any of them. + +--- + +## 1. Create the Container + +```bash +pct create local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst \ + --hostname \ + --memory 512 \ + --cores 2 \ + --rootfs local-lvm:8 \ + --net0 name=eth0,bridge=vmbr0,gw=10.0.0.1,ip=10.0.0./24,type=veth \ + --nameserver 10.0.0.1 \ + --unprivileged 1 \ + --start 1 +``` + +Check available template name first: +```bash +pveam list local +``` + +--- + +## 2. IP Address Allocation + +| IP | Container | +|----|-----------| +| 10.0.0.10 | apt-cache (CT 1104) | +| 10.0.0.20 | tessera-pipeline (CT 1101) | +| 10.0.0.21 | tessera-store (CT 1102) | +| 10.0.0.22 | tessera-dev (CT 1103) | +| 10.0.0.23 | otivm-dev (CT 1105) | + +Next available: 10.0.0.24 + +--- + +## 3. WireGuard IP Allocation + +| IP | Node | +|----|------| +| 10.110.0.1 | wg-pk (Linode hub) | +| 10.110.0.2 | shell.infra.civicus.us | +| 10.110.0.4 | fw (CVSTOS push node) | +| 10.110.0.5 | OVH node | +| 10.110.0.6 | corpusdb.infra.civicus.us | +| 10.110.0.7–0.14 | Home lab containers | +| 10.110.0.15 | OVH node | +| 10.110.0.16 | OVH node | +| 10.110.0.17 | mcp.civicus.us | +| 10.110.0.18 | otivm-dev | + +Next available: 10.110.0.19 + +--- + +## 4. Set Root Password + +From srv-a (not from inside the container): +```bash +pct exec -- passwd root +``` + +--- + +## 5. Fix Locale + +⚠ Do NOT use `update-locale` — it fails in this environment. + +```bash +echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen +locale-gen +echo "LANG=en_US.UTF-8" > /etc/default/locale +export LANG=en_US.UTF-8 +``` + +--- + +## 6. Configure apt Proxy + +The apt-cache container (10.0.0.10) runs apt-cacher-ng on port 3142. +All new containers must be configured to use it. + +⚠ Two lines required — HTTP through cache, HTTPS direct: + +```bash +echo 'Acquire::http::Proxy "http://10.0.0.10:3142";' > /etc/apt/apt.conf.d/01proxy +echo 'Acquire::https::Proxy "DIRECT";' >> /etc/apt/apt.conf.d/01proxy +``` + +The HTTPS direct line is required because apt-cacher-ng cannot proxy +HTTPS tunnels. Without it, any HTTPS apt source (e.g. nodesource) will +fail with "403 CONNECT denied". + +--- + +## 7. Install Base Packages + +```bash +apt update && apt upgrade -y +apt install -y git python3 python3-venv python3-pip curl wget wireguard +``` + +--- + +## 8. Install Node.js v22 + +⚠ Do NOT use the nodesource setup script (`curl ... | bash`) — it +creates conflicting source files. Use the manual method: + +```bash +mkdir -p /etc/apt/keyrings +curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | \ + gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg +echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] \ + https://deb.nodesource.com/node_22.x nodistro main" \ + > /etc/apt/sources.list.d/nodesource.list +apt update && apt install -y nodejs +node --version # should be v22.x.x +npm --version +``` + +--- + +## 9. Install WireGuard + +```bash +apt install -y wireguard +wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key +chmod 600 /etc/wireguard/private.key +cat /etc/wireguard/public.key # give this to wg-pk +``` + +Create /etc/wireguard/wg0.conf: +```ini +[Interface] +Address = 10.110.0./32 +ListenPort = 51820 +PrivateKey = + +[Peer] +PublicKey = 1+Wb++fjXNbY0joOvj4AZvJgF6b125YOPSFsmNqVo3I= +AllowedIPs = 10.110.0.0/22 +Endpoint = 198.58.111.109:51820 +PersistentKeepalive = 25 +``` + +```bash +systemctl enable wg-quick@wg0 +systemctl start wg-quick@wg0 +ping -c 3 10.110.0.1 # confirm mesh +``` + +On wg-pk — add peer (live, no restart needed): +```bash +wg set wg0 peer allowed-ips 10.110.0./32 +# append to /etc/wireguard/wg0.conf as well +``` + +--- + +## 10. Create App User + +```bash +useradd -m -s /bin/bash +passwd +``` + +--- + +## 11. Python Venv + +Always as the app user, never as root: +```bash +su - +python3 -m venv /home//venv +echo 'source /home//venv/bin/activate' >> /home//.bashrc +``` + +⚠ Never run Python outside the venv. This is an absolute rule. + +--- + +## 12. Install PM2 + +As root: +```bash +npm install -g pm2 +``` + +Set up systemd service under app user: +```bash +env PATH=$PATH:/usr/bin pm2 startup systemd -u --hp /home/ +``` + +PM2 must always run as the app user. Never as root. + +--- + +## 13. Install Claude Code + +As the app user: +```bash +su - +mkdir -p ~/.npm-global +npm config set prefix '~/.npm-global' +echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc +source ~/.bashrc +npm install -g @anthropic-ai/claude-code +claude --version +``` + +Authenticate on first run: +```bash +cd ~/REPONAME +claude +# Select: Claude account with subscription +# Follow browser-based login +``` + +--- + +## 14. Install Webmin + +As root: +```bash +curl -o webmin-setup-repo.sh https://raw.githubusercontent.com/webmin/webmin/master/webmin-setup-repo.sh +sh webmin-setup-repo.sh +apt install -y webmin +systemctl disable webmin # do not autostart +``` + +Add aliases to /root/.bashrc: +```bash +echo "alias webmin-on='systemctl start webmin && echo Webmin started'" >> /root/.bashrc +echo "alias webmin-off='systemctl stop webmin && echo Webmin stopped'" >> /root/.bashrc +source /root/.bashrc +``` + +Webmin listens on port 10000. Start it only when needed. + +--- + +## 15. SSL Certificate Rule + +⚠ ABSOLUTE RULE for all nodes with Nginx: + +Issue the SSL certificate FIRST with --nginx authenticator. +Add any basic auth AFTER the certificate is issued. +Never reverse this order — basic auth blocks the ACME challenge. + +```bash +# 1. Create vhost with no auth +# 2. Dry run +certbot certonly --nginx -d --dry-run +# 3. Issue real certificate +certbot certonly --nginx -d +# 4. Add SSL stanzas to vhost +# 5. Add basic auth if needed +# 6. Reload nginx +``` + +--- + +## 16. Take Baseline Backup + +Before deploying any code, take a backup: +```bash +vzdump --storage local --compress zstd --mode stop +``` + +Download via Webmin → File Manager → /var/lib/vz/dump/ +Store offline. This is the recovery point. + +--- + +*docs/provisioning.md — TheRON srv-a — April 2026*