Book402 runs on minimal infrastructure — a single VPS handles everything.

System Architecture

graph TD subgraph Internet U[Users / Agents] DNS[book402.com
docs.book402.com] end subgraph QuimServer["QuimServer (Hetzner CX23)"] subgraph Nginx["Nginx (port 80)"] N1["/ → React SPA"] N2["/graph, /books, /search → API proxy"] N3["docs.book402.com → MkDocs"] end subgraph App["Application Layer"] API["Book402 API
(Node.js, PM2, port 3002)"] DOC["MkDocs Static Files
(/opt/book402-docs)"] end subgraph Data["Data Layer"] DB["SQLite DB
(3,807 books, 23,145 chunks)"] end subgraph Infra["Infrastructure"] FAC["x402 Facilitator
(Docker, port 8402)"] SWAP["2GB Swap"] end end subgraph Blockchain BASE["Base L2
(USDC settlements)"] end U --> DNS --> Nginx N1 --> DOC N2 --> API API --> DB API --> FAC FAC --> BASE

Server Specs

Spec Value
Provider Hetzner
Plan CX23
Cost €2.99/month
CPU 2 vCPU (Intel Xeon Skylake)
RAM 4 GB + 2 GB swap
Disk 38 GB SSD
OS Ubuntu 24.04 LTS
Location Helsinki, Finland

Services

Service Port Manager Purpose
Nginx 80 systemd Reverse proxy + static files
Book402 API 3002 PM2 REST API + x402 middleware
x402 Facilitator 8402 Docker Payment verification & settlement

Network

graph LR subgraph External A[book402.com] -->|DNS| IP[135.181.111.17] B[docs.book402.com] -->|DNS| IP end subgraph QuimServer["135.181.111.17"] IP --> NGX[Nginx :80] NGX -->|"book402.com /"| SPA[React SPA] NGX -->|"book402.com /api/*"| API[Node.js :3002] NGX -->|"docs.book402.com"| DOCS[MkDocs static] API -->|localhost| FAC[Facilitator :8402] end subgraph Blockchain FAC -->|RPC| BASE[mainnet.base.org] end

Firewall

UFW rules (deny by default):

22/tcp     ALLOW   (SSH)
80/tcp     ALLOW   (HTTP)
443/tcp    ALLOW   (HTTPS — future)

All internal services (API 3002, Facilitator 8402) bind to 127.0.0.1 only.

Wallet

Field Value
Address 0x6B1925e4a1f779797eF51A18A4694B59FFb60Aba
Network Base (Chain ID 8453)
Asset USDC
Purpose Receive x402 payments, sign facilitator settlements

Security

The private key is stored in /opt/x402-facilitator/config.json (chmod 600, root only). The API server does NOT have access to the private key — only the facilitator container does.

Deployment

# API updates
cd /opt/feralgrind
git pull
cd tools && npx tsc -p tsconfig.json
pm2 restart feralgrind-api

# Landing page updates
cd ~/projects/book402.com
npm run build
rsync -avz --delete dist/ quimserver:/opt/book402-landing/

# Docs updates
cd ~/projects/book402.com/docs
mkdocs build
rsync -avz --delete site/ quimserver:/opt/book402-docs/