Add infrastructure documentation and project setup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
121
CLAUDE.md
Normal file
121
CLAUDE.md
Normal file
@@ -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 <files>
|
||||||
|
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*
|
||||||
61
README.md
61
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.*
|
||||||
|
|||||||
271
docs/infrastructure.md
Normal file
271
docs/infrastructure.md
Normal file
@@ -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 = <stored on node>
|
||||||
|
|
||||||
|
[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*
|
||||||
285
docs/provisioning.md
Normal file
285
docs/provisioning.md
Normal file
@@ -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 <VMID> local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst \
|
||||||
|
--hostname <name> \
|
||||||
|
--memory 512 \
|
||||||
|
--cores 2 \
|
||||||
|
--rootfs local-lvm:8 \
|
||||||
|
--net0 name=eth0,bridge=vmbr0,gw=10.0.0.1,ip=10.0.0.<X>/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 <VMID> -- 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.<X>/32
|
||||||
|
ListenPort = 51820
|
||||||
|
PrivateKey = <contents of /etc/wireguard/private.key>
|
||||||
|
|
||||||
|
[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 <pubkey> allowed-ips 10.110.0.<X>/32
|
||||||
|
# append to /etc/wireguard/wg0.conf as well
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Create App User
|
||||||
|
|
||||||
|
```bash
|
||||||
|
useradd -m -s /bin/bash <appuser>
|
||||||
|
passwd <appuser>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Python Venv
|
||||||
|
|
||||||
|
Always as the app user, never as root:
|
||||||
|
```bash
|
||||||
|
su - <appuser>
|
||||||
|
python3 -m venv /home/<appuser>/venv
|
||||||
|
echo 'source /home/<appuser>/venv/bin/activate' >> /home/<appuser>/.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 <appuser> --hp /home/<appuser>
|
||||||
|
```
|
||||||
|
|
||||||
|
PM2 must always run as the app user. Never as root.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Install Claude Code
|
||||||
|
|
||||||
|
As the app user:
|
||||||
|
```bash
|
||||||
|
su - <appuser>
|
||||||
|
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 <domain> --dry-run
|
||||||
|
# 3. Issue real certificate
|
||||||
|
certbot certonly --nginx -d <domain>
|
||||||
|
# 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 <VMID> --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*
|
||||||
Reference in New Issue
Block a user