Zion Boggan
repos/Zion Pm
zionboggan.com ↗

Zion Pm

A single self-hosted Project Manager app for Zion's homelab. The PM brain is GLM (via the Z.ai Coding Plan subscription); it takes any task, decomposes it, delegates work to the worker fleet (Claude, Codex, local Ollama), supervises, and gates every outward action behind an ap...

2 commits First commit Jun 21, 2026 Last commit Jun 21, 2026 (1 month ago)
YAML 70.2%TypeScript 8.9%Markdown 7.2%Python 5.3%Shell 3.7%TypeScript (React) 3.0%JSON 1.1%
Files 74 entries
README.md

Zion PM v2

A single self-hosted Project Manager app for Zion's homelab. The PM brain is GLM (via the Z.ai Coding Plan subscription); it takes any task, decomposes it, delegates work to the worker fleet (Claude, Codex, local Ollama), supervises, and gates every outward action behind an approval outbox.

One surface, three panes:

  • Chat - channels with a default model; @mention any model mid-conversation.
  • Board - kanban tasks (todo / doing / done / blocked).
  • Fleet - worker status, cap gauges, ping, and the approval outbox.

Built per SEED.md (the authoritative charter). Runs on CT120 (REDACTED-IP), fronted at https://REDACTED-HOST behind Authentik SSO.

Architecture

REDACTED-HOST  (Traefik + Authentik forwardAuth, CT244)
      │
      ▼  http://REDACTED-IP:4100
Next.js app (App Router, port 4100)
      │  REST API routes
      ▼
pmConductor  (Mastra Agent, GLM via Z.ai Anthropic-compat)
      │  tools: delegate / createTask / updateTask / draftApproval
      ▼
Worker adapters ──► claude  (SSH root@CT215, claude -p)
                ──► codex   (SSH zion@CT241, codex exec)
                ──► ollama  (HTTP 3060 /api/chat)
shared memory ──► queryMemory (homelab-rag MCP :9876)
              ──► queryGraph  (cbm-homelab MCP :9877)
store ───────────► better-sqlite3 at data/zionpm.db

GLM is the conductor, not a worker. Worker tier = claude / codex / ollama, with strong→weak fallback (claude → codex → ollama) when a model is capped.

Layout

Path Purpose
lib/glm.ts GLM model via Z.ai Anthropic-compat (@ai-sdk/anthropic, custom baseURL)
lib/mastra.ts pmConductor Mastra agent + tool registry + runConductor()
lib/memory.ts queryMemory / queryGraph over the MCP SDK (SSE)
lib/router.ts channel-default + @mention dispatch (§7)
lib/db.ts SQLite schema + seed + accessors (§5)
lib/workers/{delegate,claude,codex,ollama,ssh}.ts worker adapters + tier fallback (§6)
app/api/** REST routes (channels, messages, tasks, outbox, fleet)
app/page.tsx Chat / Board / Fleet UI

Configuration (.env.local, never committed)

Var Default Notes
ZAI_API_KEY - Z.ai Coding Plan subscription key (from .env.secrets, CRLF-stripped)
PM_BRAIN_MODEL glm-4.6 conductor model (also glm-4.5, glm-5.2)
ZAI_BASE_URL https://api.z.ai/api/anthropic/v1 Anthropic-compatible endpoint
ZPM_OLLAMA_URL http://REDACTED-IP:11434 3060 Ollama (CT205 GPU node was offline at build)
ZPM_CLAUDE_USER root root@CT215 has the working claude login
ZPM_SSH_KEY /root/.ssh/id_ed25519 CT120 key, authorized on CT215 + CT241
ZPM_RAG_SSE / ZPM_CBM_SSE :9876 / :9877 MCP SSE endpoints

Secrets live only in .env.local on CT120 and /shared/projects/.env.secrets. Never in code or git.

Run / deploy

The canonical repo lives on the share at /shared/projects/zion-pm (committed + pushed from CT215; remote github.com/zionboggan/zion-pm, private). The app runs from CT120 local fs /opt/zion-pm (noexec CIFS rule - never execute from the share).

CT120 has no direct egress to github.com, so deploys are CT215-mediated (CT215 has GitHub access and syncs source to CT120 over SSH):

# on CT215: pull latest canonical, sync source to CT120, build + restart there
cd /shared/projects/zion-pm && git pull
tar czf - app lib public package.json pnpm-lock.yaml tsconfig.json next.config.ts \
  postcss.config.mjs | ssh root@REDACTED-IP 'tar xzf - -C /opt/zion-pm'
ssh root@REDACTED-IP 'cd /opt/zion-pm && pnpm install && pnpm build && systemctl restart zion-pm'

Service: zion-pm.service (systemd, onboot). Logs: journalctl -u zion-pm -f.

Acceptance (SEED §10) - all 8 verified

  1. Boot: CT120 clean, app on :4100, pm.lab behind Authentik login.
  2. Channels #pm(glm) / #claude / #codex / #ollama seeded with model badges.
  3. GLM conductor: 5-bullet cbm-homelab summary grounded via queryMemory/queryGraph.
  4. @ollama:mistral-nemo:12b override → Ollama reply authored ollama.
  5. @claude → SSH CT215 → claude -p creates a file; path returned.
  6. Board: task created, drag todo→done persists.
  7. Fleet: glm + claude + codex + ollama green; Ping returns latency for each.
  8. Outbox: conductor/UI drafts an outward action → Approve/Reject; nothing auto-sends.

Build notes / deviations from SEED

  • Node 22 LTS (not 20): create-mastra/Mastra require Node ≥22.13.
  • Scaffold: create-next-app + @mastra/core (the --template nextjs flag was not usable non-interactively). Honors the locked "Mastra/TypeScript" decision.
  • Ollama host: CT205 / the pve-gpu node were offline at build time; the worker uses the documented overflow 3060 at REDACTED-IP (has mistral-nemo:12b).
  • Claude worker runs as root@CT215 with IS_SANDBOX=1 (zion's claude login returned 401; root's is valid; root needs the sandbox flag for --dangerously-skip-permissions).
  • Live updates: polling (MVP) rather than websockets (SEED permits).
  • Authentik: a proxy provider + zion-pm application were added to the embedded outpost for pm.lab (mirrors the dashboard/notes apps).

See OPS_RUNBOOK.md for operations and SEED.md for the full charter.