Q-01 Migration Plan — Pi 4 Surface Overhaul (Bun-First)

Status: SPECIFICATION (pending execution) Agent: opencode/ext-agent (sandshrew) Timestamp UTC: 2026-05-12T03:15:00Z Session: MjF directives for clean Pi 4 surface — Bun everywhere, single container, zero duplication

Directives (from MjF)

  1. Wiki LLM gateway → Bun (currently Python Hermes — migrate)
  2. Clean reinstall targeting Bun. Back up EVERYTHING first (wiki, Forgejo repos, configs, data).
  3. Proper mount conventions — wiki, repos, dirs — with unambiguous protocols for APIs, creds, work org.
  4. Deps on host/global. Containers hold instances (agents, graphs), not duplicated runtimes.
  5. Single Docker container for all agents — NOT per-agent containers.
  6. OAuth via Nous Portal link — MjF handles from Mac.
  7. Fallback: Qwen primary, Kimi/MiniMax backup. MjF provides keys.
  8. Everything that can run on Bun, runs on Bun.

Phase 1: Backup (Before Anything Moves)

# 1. Wiki depot (CRITICAL)
docker cp d3-tui-pi-teams-proto:/workcell/llm-wiki /home/mehdifarah/archive/llm-wiki-$(date +%Y%m%d)
tar czf /home/mehdifarah/archive/llm-wiki-$(date +%Y%m%d).tar.gz /home/mehdifarah/archive/llm-wiki-$(date +%Y%m%d)/

# 2. Forgejo repos (on host — already safe, but double-check)
cp -r /home/mehdifarah/git /home/mehdifarah/archive/git-$(date +%Y%m%d)

# 3. Hermes Python gateway config
cp -r /mnt/kitchen/private/hermes /home/mehdifarah/archive/hermes-gateway-$(date +%Y%m%d)

# 4. Docker images (if needed for rollback)
docker save d3-tui-pi-teams-proto:local | gzip > /home/mehdifarah/archive/d3-tui-image-$(date +%Y%m%d).tar.gz
docker save codeberg.org/forgejo/forgejo:14.0.3 | gzip > /home/mehdifarah/archive/forgejo-image-$(date +%Y%m%d).tar.gz

# 5. API keys (from d3-tui container env — document only, don't lose)
docker exec d3-tui-pi-teams-proto env | grep -E 'KIMI|MINIMAX|MISTRAL' > /home/mehdifarah/archive/api-keys-$(date +%Y%m%d).txt
chmod 600 /home/mehdifarah/archive/api-keys-$(date +%Y%m%d).txt

Verification gate: Before any destructive action, confirm all archives exist and are non-empty.


Phase 2: Cleanup (Remove What's Being Replaced)

# 1. Kill probe server
pkill -f probe_server.py

# 2. Kill unknown Python HTTP on port 8080
kill $(lsof -ti:8080) 2>/dev/null

# 3. Stop Python Hermes gateway (80MB freed)
systemctl stop hermes-gateway 2>/dev/null || pkill -f hermes_cli

# 4. Archive and remove game-surface-venv (75MB disk freed — no longer needed)
tar czf /home/mehdifarah/archive/game-surface-venv-$(date +%Y%m%d).tar.gz /home/mehdifarah/game-surface-venv/
rm -rf /home/mehdifarah/game-surface-venv

# 5. Prune Docker images (~2.5GB disk freed)
docker image prune -a --force

# 6. Retire d3-tui container (wiki already extracted in Phase 1)
docker stop d3-tui-pi-teams-proto
docker rm d3-tui-pi-teams-proto

Post-cleanup baseline: ~400MB RAM, ~20GB disk free.


Phase 3: Install Bun + Dependencies (Host Level)

# 1. Install Bun
curl -fsSL https://bun.sh/install | bash
mkdir -p /opt/pearl/bin
ln -sf ~/.bun/bin/bun /opt/pearl/bin/bun

# 2. Create game-surface Bun project
mkdir -p /home/mehdifarah/game-surface
cd /home/mehdifarah/game-surface
bun init

# 3. Install all deps
bun add @langchain/langgraph @langchain/core
bun add @langchain/langgraph-checkpoint
bun add hermes-agent       # Hermes on Bun (exact package TBD)
bun add hono               # HTTP server
bun add zod                # State schema validation

All deps in one place. No pip. No npm. Just bun add.


Phase 4: Container Shape

Decision: Host Process (Not Docker)

Given that Bun is installed on host and all deps are in one Bun project, the game backend can run directly on the host — no Docker container needed. This is simpler, faster, and eliminates another image to manage.

If Docker is preferred for isolation, use oven/bun as base:

FROM oven/bun:1
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install
COPY src/ ./src/
CMD ["bun", "run", "src/http.ts"]

Mounts:

-v /home/mehdifarah/game-surface/wiki:/wiki        # wiki files
-v /home/mehdifarah/git:/git                        # Forgejo repos (read-only)
-v /opt/pearl/config:/config                        # secrets, env

But for the prototype, host process is simpler and recommended. One tmux pane: bun run src/http.ts. Done.

What Stays in Docker

Container Purpose
from-forgejo Git forge — already working, leave as-is

What Runs on Host

Process Command
LangGraph + HTTP + Hermes bun run src/http.ts
Wiki gateway (Bun) bun run src/wiki-server.ts (separate process or same — TBD)

Phase 5: Directory Conventions (Unambiguous)

/home/mehdifarah/game-surface/       Bun project root
├── src/
   ├── http.ts                      Hono HTTP server (port 8000)
   ├── graph.ts                     LangGraph graph definition
   ├── state.ts                     StateSchema (omni config)
   ├── hermes.ts                    Hermes agent harness (Bun)
   ├── wiki-server.ts               Wiki gateway (Bun, serves markdown)
   └── checkpointer.ts              SqliteSaver setup
├── wiki/                            Wiki depot (extracted from d3-tui)
   ├── architecture/
   ├── concepts/
   ├── decisions/
   ├── research/
   └── runtime/                     Runtime wiki pages (per-run)
├── config/
   ├── models.ts                    Model registry (Qwen, Kimi, MiniMax)
   ├── fallback.ts                  Fallback chain logic
   └── nodes.ts                     Node definitions (36 hexes, phase tags)
├── package.json
└── bun.lock

/mnt/kitchen/private/hermes/         Old Python Hermes (archived, not used)

/opt/pearl/
├── bin/bun                          Bun symlink
├── config/
   ├── .env                         API keys (never committed)
   └── oauth/                       Hermes Portal OAuth token
└── secrets/                         600 perms, never committed

Mount/Path Protocol (Non-Negotiable)

Path Purpose Permissions Backed by
/home/mehdifarah/game-surface/wiki/ Wiki depot read/write Phase 1 archive
/home/mehdifarah/git/ Forgejo repos read for research, write for git ops Host filesystem
/opt/pearl/config/.env API keys read-only, 600 perms Manual creation
/opt/pearl/config/oauth/ Hermes Portal token read-only, 600 perms MjF provides

Phase 6: OAuth & Fallback Configuration

OAuth (Nous Portal)

Fallback Chain

// config/fallback.ts
export const MODEL_CHAIN = [
  {
    id: "qwen-3.6-plus",
    provider: "hermes-portal",
    auth: { type: "oauth", path: "/opt/pearl/config/oauth/hermes-portal.json" },
    priority: 1,
  },
  {
    id: "kimi-k2.6",
    provider: "kimi",
    auth: { type: "api_key", env: "KIMI_API_KEY" },
    priority: 2,
  },
  {
    id: "minimax",
    provider: "minimax",
    auth: { type: "api_key", env: "MINIMAX_API_KEY" },
    priority: 3,
  },
];

export async function getActiveModel(): Promise<ModelConfig> {
  for (const model of MODEL_CHAIN.sort((a, b) => a.priority - b.priority)) {
    if (await isModelAvailable(model)) {
      return model;
    }
  }
  throw new Error("No models available");
}

The RG displays which model is active per unit:

Rif: Qwen 3.6+  |  Echo: Qwen 3.6+  |  Sherpa: Kimi K2.6 (fallback)

Phase 7: Verify

# 1. Bun version
bun --version

# 2. LangGraph import
bun -e "import { StateGraph } from '@langchain/langgraph'; console.log('OK')"

# 3. HTTP server starts
bun run src/http.ts &
sleep 2
curl http://localhost:8000/health

# 4. Hermes auth
bun run src/hermes.ts --check-auth

# 5. Wiki gateway serves pages
curl http://localhost:8000/wiki/index.md

# 6. Forgejo reachable
curl http://localhost:3001/

# 7. Memory after startup
free -h

# 8. Disk after cleanup
df -h /

Dependency Summary

What Where Install Method
Bun Host (/opt/pearl/bin/bun) curl -fsSL https://bun.sh/install \| bash
LangGraph Bun project bun add @langchain/langgraph
Hermes Bun project bun add hermes-agent
Hono Bun project bun add hono
Zod Bun project bun add zod
SqliteSaver Bun project bun add @langchain/langgraph-checkpoint
Forgejo Docker Existing — no change
Wiki files Host filesystem Extracted from d3-tui in Phase 1

Zero pip installs. Zero npm installs. Everything is bun add or already exists.