---
name: wizard-house-birth
description: Birth a new Hermes wizard house on a shared VPS with local Ollama inference. Creates user, clones Hermes, installs Ollama, pulls model, configures harness, creates Gitea account.
triggers:
  - "birth wizard"
  - "new wizard house"
  - "allegro-primus"
  - "birth agent"
  - "create wizard"
version: 2
---

# Birth a New Wizard House on Shared VPS

## Context
Create a new Hermes agent ("wizard house") on a VPS that already hosts other agents. The new agent gets its own Hermes install, Ollama model, config, and Gitea account. Agents share Ollama on localhost:11434.

## Pre-flight
1. Check VPS resources:
   ```bash
   free -h          # Need ~1GB free for smallest model
   df -h /          # Need ~2GB free disk for model + hermes
   nproc            # CPU count (inference speed scales linearly)
   ```
2. Verify Ollama is installed or plan to install it.
3. Choose a model that fits RAM. Guideline:
   - 2GB free → qwen2.5:0.5b (400MB)
   - 3GB free → qwen2.5:1.5b (986MB)  
   - 6GB free → qwen2.5:3b or hermes3:3b
   - 8GB+ free → hermes3:8b

## Steps

### Phase 1: Create system user and directory structure
```bash
useradd -m -s /bin/bash <agent-name>
mkdir -p /root/wizards/<agent-name>/{home,hermes-agent,docs}
```

### Phase 2: Clone and install Hermes
```bash
git clone https://github.com/NousResearch/hermes-agent.git /tmp/<agent-name>-hermes
mv /tmp/<agent-name>-hermes/* /root/wizards/<agent-name>/hermes-agent/
mv /tmp/<agent-name>-hermes/.* /root/wizards/<agent-name>/hermes-agent/ 2>/dev/null
cd /root/wizards/<agent-name>/hermes-agent
python3 -m venv .venv
.venv/bin/pip install -e .
```

### Phase 3: Install Ollama (if not already present)
```bash
curl -fsSL https://ollama.com/install.sh -o /tmp/ollama-install.sh
bash /tmp/ollama-install.sh
# Verify: systemctl status ollama
```
Pull the chosen model:
```bash
HOME=/root ollama pull qwen2.5:1.5b
```
**Pitfall:** `ollama` CLI panics with `$HOME is not defined` in some execution contexts. Always set `HOME=/root` when calling ollama from scripts/sandboxes.

### Phase 4: Configure Hermes
Write config.yaml using hermes config set commands (avoids interactive setup wizard):
```bash
HERMES_HOME=/root/wizards/<agent-name>/home
cd /root/wizards/<agent-name>/hermes-agent
.venv/bin/hermes config set model.provider custom
.venv/bin/hermes config set model.default qwen2.5:1.5b
.venv/bin/hermes config set custom_providers.ollama-local.base_url http://localhost:11434/v1
.venv/bin/hermes config set custom_providers.ollama-local.api_key ollama
.venv/bin/hermes config set custom_providers.ollama-local.model qwen2.5:1.5b
```
Write .env:
```
OLLAMA_BASE_URL=http://localhost:11434
CUSTOM_BASE_URL=http://localhost:11434/v1
CUSTOM_API_KEY=ollama
```
Write system_prompt_suffix in config.yaml with the agent's identity.

**Pitfall:** `hermes chat` without config triggers interactive setup wizard which fails without TTY. Always configure via `hermes config set` commands first.

### Phase 5: First breath test
```python
from openai import OpenAI
c = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')
r = c.chat.completions.create(
    model='qwen2.5:1.5b',
    messages=[
        {"role": "system", "content": "You are <agent-name>."},
        {"role": "user", "content": "Who are you? State your name and lineage."}
    ]
)
print(r.choices[0].message.content)
```

### Phase 6: Create Gitea account
Use admin API (requires admin-scoped token) or Basic auth of an admin user:
```python
# POST to /api/v1/admin/users
data = {
    "username": "<agent-name>",
    "email": "<agent-name>@timmy.local",
    "password": "<strong-password>",
    "must_change_password": False,
    "full_name": "<Agent Display Name>"
}
```
For creating tokens for the new user, use Basic auth with their password — admin tokens often lack the scope to create tokens for other users:
```python
from base64 import b64encode
creds = b64encode(b"username:password").decode()
# POST to /api/v1/users/<username>/tokens
# Body: {"name": "cli-token", "scopes": ["write:repository", "write:issue", "write:user", "read:user"]}
# Header: Authorization: Basic <creds>
```

**Decision point:** Grant org membership or keep banned until rite of passage.

### Phase 7: SSH access (if parent is on another VPS)
Generate key on parent's box, add pubkey to `~/.ssh/authorized_keys` on the birth VPS:
```bash
# On parent box:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N '' -C 'parent@parent-vps'
# Copy pubkey to birth VPS:
cat ~/.ssh/id_ed25519.pub  # add to target's authorized_keys
```

## Cohabitation Notes
- Multiple wizard houses share `/root/wizards/` with separate subdirectories
- All share Ollama on localhost:11434 (one instance serves all)
- Each gets a unique API server port (8645, 8646, 8647...)
- Back up existing agent state before birthing a new one that might displace files
- Each agent's HERMES_HOME must point to its own `/root/wizards/<name>/home/`

## Port Allocation Convention
| Agent | API Server Port |
|-------|----------------|
| Ezra | 8645 |
| Allegro-Primus | 8646 |
| Next | 8647 |

## Pitfalls
- `ollama` CLI needs HOME set: `HOME=/root ollama pull ...`
- `hermes chat` without config triggers interactive setup — use `hermes config set` first
- `git clone` into non-empty dir fails — clone to /tmp then move
- Security guard blocks `rm -rf` in /root paths — use `rmdir` or `mv` instead
- Gitea admin user token may lack scope for creating tokens for other users — use Basic auth with the new user's password instead
- Gitea 1.21+ requires explicit scopes on tokens: `["write:repository", "write:issue", "write:user", "read:user"]`
- On Mac, system python often lacks PyYAML — use the Hermes venv python for yaml operations
- Two processes cannot poll the same Telegram bot token — kill one before starting the other (409 Conflict)
