# Forge Server — James's Future Home

*Last updated: 2026-02-04*

**This IS my primary home.** Migration completed 2026-02-04. IP swapped to 192.168.1.16.

---

## Hardware

| Component | Details |
|-----------|---------|
| **Machine** | Lenovo ThinkServer TS140 (second unit) |
| **CPU** | Intel Core i7-6700K @ 4.0GHz (4c/8t, HyperThreading) |
| **RAM** | 64GB DDR4 |
| **GPU** | NVIDIA GeForce GTX 970 4GB (compute 5.2, Maxwell) |
| **Storage** | 469GB NVMe (28G used, 417G free = 7%) |
| **Network** | Single NIC, enp10s0, 192.168.3.138/22 |

## OS & Kernel

- **OS:** Ubuntu 24.04.3 LTS (Server, headless)
- **Kernel:** 6.8.0-94-generic
- **Timezone:** America/New_York

## Network

- **IP:** 192.168.1.16 (swapped from old james on 2026-02-04)
- **Subnet:** 192.168.0.0/22
- **Gateway:** (standard home network)
- **DNS:** systemd-resolved (127.0.0.53, 127.0.0.54)

## Access

- **SSH:** Key auth only (password auth disabled, root login disabled)
- **Authorized keys:**
  - `james@server` — James (primary)
  - `johan@ubuntu2404` — Johan
  - `claude@macbook` — Johan's Mac
- **Sudo:** Passwordless (`johan ALL=(ALL) NOPASSWD:ALL`)
- **Linger:** Enabled (user services persist without active SSH)

## Security

- **Firewall (UFW):** Active
  - Rule 1: SSH (22/tcp) from anywhere
  - Rule 2: All traffic from LAN (192.168.0.0/22)
  - Default: deny incoming, allow outgoing
- **Fail2ban:** Active, monitoring sshd
- **Unattended upgrades:** Enabled
- **Sysctl hardening:** rp_filter, syncookies enabled
- **Disabled services:** snapd, ModemManager
- **Still enabled (fix later):** cloud-init

## GPU Stack

- **Driver:** nvidia-headless-580 + nvidia-utils-580 (v580.126.09)
- **CUDA:** 13.0 (reported by nvidia-smi)
- **Persistence mode:** Enabled
- **VRAM:** 4096 MiB total, ~2.2 GB used by OCR model
- **Temp:** ~44-51°C idle
- **CRITICAL:** GTX 970 = compute capability 5.2 (Maxwell)
  - PyTorch ≤ 2.2.x only (newer drops sm_52 support)
  - Must use CUDA 11.8 wheels

## Python Environment

- **Path:** `/home/johan/ocr-env/`
- **Python:** 3.12.3
- **Key packages:**
  - PyTorch 2.2.2+cu118
  - torchvision 0.17.2+cu118
  - transformers 5.0.1.dev0 (installed from git, has GLM-OCR support)
  - accelerate 1.12.0
  - FastAPI 0.128.0
  - uvicorn 0.40.0
  - Pillow (for image processing)
- **Monkey-patch:** `transformers/utils/generic.py` patched for `torch.is_autocast_enabled()` compat with PyTorch 2.2

## Services

### OCR Service (GLM-OCR)
- **Port:** 8090 (0.0.0.0)
- **Service:** `systemctl --user status ocr-service`
- **Unit file:** `~/.config/systemd/user/ocr-service.service`
- **Source:** `/home/johan/ocr-service/server.py`
- **Model:** `/home/johan/models/glm-ocr` (zai-org/GLM-OCR, 2.47 GB)

**Endpoints:**
- `GET /health` — status, GPU memory, model info
- `POST /ocr` — single image (multipart: file + prompt + max_tokens)
- `POST /ocr/batch` — multiple images

**Performance:**
- Model load: ~1.4s (stays warm in VRAM)
- Small images: ~2s
- Full-page documents: ~25s (auto-resized to 1280px max)
- VRAM: 2.2 GB idle, peaks ~2.8 GB during inference

**Usage from james:**
```bash
# Health check
curl http://192.168.3.138:8090/health

# OCR a single image
curl -X POST http://192.168.3.138:8090/ocr -F "file=@image.png"

# OCR with custom prompt
curl -X POST http://192.168.3.138:8090/ocr -F "file=@doc.png" -F "prompt=Extract all text:"
```

### Ollama
- **Port:** 11434 (localhost only)
- **Version:** 0.15.4
- **Status:** Installed, waiting for v0.15.5 for native GLM-OCR support
- **Note:** Not currently used — Python/transformers handles OCR directly

## Migration Plan: james → forge

### What moves:
- [ ] OpenClaw gateway (port 18789)
- [ ] Signal-cli daemon (port 8080)
- [ ] Proton Mail Bridge (ports 1143, 1025)
- [ ] Mail Bridge / Message Center (port 8025)
- [ ] Message Bridge / WhatsApp (port 8030)
- [ ] Dashboard (port 9200)
- [ ] Headless Chrome (port 9223)
- [ ] All workspace files (`~/clawd/`)
- [ ] Document management system
- [ ] Cron jobs and heartbeat config
- [ ] SSH keys and configs

### What stays on james (or TBD):
- Legacy configs / backups
- SMB shares (maybe move too?)

### Pre-migration checklist:
- [ ] Install Node.js 22 on forge
- [ ] Install OpenClaw on forge
- [ ] Set up Signal-cli on forge
- [ ] Set up Proton Mail Bridge on forge
- [ ] Set up message-bridge (WhatsApp) on forge
- [ ] Set up headless Chrome on forge
- [ ] Copy workspace (`~/clawd/`) to forge
- [ ] Copy documents system to forge
- [ ] Test all services on forge before switchover
- [ ] Update DNS/Caddy to point to forge IP
- [ ] Update TOOLS.md, MEMORY.md with new IPs
- [ ] Verify GPU OCR still works alongside gateway

### Advantages of forge over james:
- **CPU:** i7-6700K (4c/8t, 4.0GHz) vs Xeon E3-1225v3 (4c/4t, 3.2GHz) — faster + HT
- **RAM:** 64GB vs 16GB — massive headroom
- **GPU:** GTX 970 for local ML inference
- **Storage:** 469GB NVMe vs 916GB SSD — less space but faster
- **Network:** Same /22 subnet, same LAN access to everything

### Risks:
- Storage is smaller (469G vs 916G) — may need to be selective about what moves
- GPU driver + gateway on same box — monitor for resource conflicts
- Signal-cli needs to re-link or transfer DB
- WhatsApp bridge needs QR re-link

---

## Directory Layout

```
/home/johan/
├── ocr-env/              # Python venv (PyTorch + transformers)
├── ocr-service/          # FastAPI OCR server
│   └── server.py
├── models/
│   └── glm-ocr/          # GLM-OCR weights (2.47 GB)
├── .config/
│   └── systemd/user/
│       └── ocr-service.service
└── .ssh/
    └── authorized_keys
```

## Key Constraints

1. **PyTorch version locked to 2.2.x** — GTX 970 sm_52 not supported in newer
2. **CUDA 11.8 wheels only** — matches PyTorch 2.2 requirement
3. **Max image dimension 1280px** — larger causes excessive VRAM/time on GTX 970
4. **transformers from git** — stock pip version doesn't have GLM-OCR model class
5. **Monkey-patch required** — `torch.is_autocast_enabled()` API changed in PyTorch 2.4
