Skip to main content
AI & Automation

AI Workbench & Forge Relay

AI coding workbench with 4 CLI tools, smart dispatcher, xterm.js terminals, Forge Relay API, and OpenClaw orchestration. Primary on Izar-Host CT 126, fallback on Tarn-Host VM 160.

February 26, 2026 Updated March 1, 2026

AI Workbench & Forge Relay

The Workbench is the admin coding environment at /admin/workbench. It provides browser-based access to 4 AI coding CLIs, a smart dispatcher that routes tasks based on subscription capacity, and OpenClaw agentic orchestration.

Architecture

Browser (xterm.js)
    │ WebSocket (wss://workbench-term.Arcturus-Prime.com)

CF Tunnel (Altair-Link) ──── cloudflared

ttyd (7681) ──── forge-attach.sh ──── tmux session

Forge Relay v2 (7682) ──── tmux send-keys / capture-pane
    │ (https://workbench-relay.Arcturus-Prime.com)

Astro API (/api/admin/forge) ──── relayFetch()

OpenClaw orchestration ──── 8 Forge tools ──── agentic loop

Hosting

Primary: CT 126 workbench-Izar-Host on Izar-Host (10.42.0.126)

  • Alpine 3.23, 512 MB RAM, 2 cores
  • On the local Milky Way — always reachable from Altair-Link (CF tunnel host)
  • Services: ttyd (7681), Forge Relay (7682)
  • OpenRC init scripts: workbench-ttyd, workbench-relay

Fallback: VM 160 on Tarn-Host (192.168.20.160)

  • Full workbench with dispatcher, AI tool CLIs, git worktrees
  • On the remote Andromeda — requires inter-site routing (can be flaky)
  • To switch: update tunnel config on Altair-Link, restart cloudflared
ComponentPurpose
ttyd (7681)WebSocket terminal — bridges browser xterm.js to tmux via forge-attach.sh
Forge Relay v2 (7682)Hono.js REST API wrapping tmux programmatic control (Bearer auth)
Dispatcher (8096)FastAPI smart router, usage tracking (SQLite), git worktree management (Tarn-Host only)
tmux5 standard sessions (forge-*) + dynamic sessions (wb-*)

Cloudflare Tunnels

HostnameCurrent TargetPurpose
workbench-term.Arcturus-Prime.comhttp://10.42.0.126:7681 (Izar-Host)Browser WebSocket terminal
workbench-relay.Arcturus-Prime.comhttp://10.42.0.126:7682 (Izar-Host)Forge Relay API

Tunnel runs on Altair-Link (10.42.0.199), config: /home/argonaut/.cloudflared/config.yml

Switching Between Izar-Host and Tarn-Host

Edit /home/argonaut/.cloudflared/config.yml on Altair-Link:

# Izar-Host (primary — local network, reliable)
- hostname: workbench-term.Arcturus-Prime.com
  service: http://10.42.0.126:7681
- hostname: workbench-relay.Arcturus-Prime.com
  service: http://10.42.0.126:7682

# Tarn-Host (fallback — remote network, can be unreachable)
# service: http://192.168.20.160:7681
# service: http://192.168.20.160:7682

Then restart: ssh [email protected] "sudo systemctl restart cloudflared"

AI Coding CLIs

ToolBinaryVersionAuthRate Limit
Claude Codeclaude2.1.63OAuth (copied from workstation)~900 msgs/5hr (Max) or ~45/5hr (Pro)
Codex CLIcodex0.106.0Needs codex login~150 msgs/5hr (Plus)
OpenCodeopencode1.2.15BYOK (API keys)Unlimited
Kilo Codekilocode7.0.33BYOK (API keys)Unlimited

tmux Sessions

SessionPurpose
forge-shellGeneral bash shell
forge-claudeClaude Code CLI
forge-codexCodex CLI
forge-opencodeOpenCode CLI
forge-kilocodeKilo Code CLI

Sessions auto-created by init-sessions.sh at ttyd startup and healthcheck. Dynamic wb-* sessions created by the dispatcher for automated tasks.

Workbench Modes

Terminal Mode

5 xterm.js terminals in browser tabs, each connecting to a tmux session via ttyd WebSocket. Lazy-connect on tab switch (only the active tab opens a WebSocket). Reconnect button for dropped connections.

WebSocket URL pattern:

  • Local dev: ws://10.42.0.126:7681/ws?arg=forge-shell
  • Production: wss://workbench-term.Arcturus-Prime.com/ws?arg=forge-shell

Agent Mode (Forge Dispatch)

Dispatch form sends tasks to AI tools. The buildCliCommand() function maps tool names to CLI invocations:

  • claude "instruction" (interactive) or claude --print "instruction" (headless)
  • codex --approval-mode suggest "instruction" (interactive)
  • opencode run "instruction" (headless) or opencode (interactive TUI)
  • kilocode run "instruction" (headless) or kilocode (interactive TUI)

Session grid shows status of tmux sessions. Activity log tracks dispatched commands.

Chat Mode

Streaming chat via the unified-chat endpoint. Conversation management stored in localStorage.

Smart Dispatcher (port 8096)

When tool: "auto", the dispatcher scores tools:

score = (remaining_capacity / rate_limit) * (1 / priority)
PriorityToolStrengths
1 (highest)Claude CodeArchitecture, refactoring, complex, debugging
2Codex CLIQuick fixes, tests, simple features
3OpenCodeMulti-model, batch
4 (fallback)Kilo CodeAutomation, batch

Subscription tools first, BYOK fallback. Usage tracked in SQLite with 5-hour rolling windows.

API Routes

/api/admin/forge (forge.ts)

Proxies to Forge Relay via FORGE_RELAY_URL (through Cloudflare tunnel in production).

ActionDescription
dispatchSend CLI command to a tool’s tmux session
sendSend raw keystrokes to a session
interruptSend Ctrl+C to a session
captureRead terminal output from a session
sessionsList all tmux sessions

/api/admin/forge-git (forge-git.ts)

ActionDescription
create-branchCreate branch via Gitea API
create-prCreate pull request via Gitea API
commit-via-relayStage + commit via relay
push-via-relayPush to remote via relay
pipelineFull auto: branch + push + PR

/api/admin/openclaw (openclaw.ts)

OpenClaw agentic orchestration with 8 Forge tools (dispatch, status, capture, interrupt, git branch/commit/push/pr). Creates a ForgeExecutor that calls Gitea and Forge Relay APIs directly.

Dispatcher API (port 8096)

MethodPathPurpose
POST/api/taskSubmit task (repo, prompt, tool, commit, push, create_pr, timeout)
GET/api/task/{id}Task status + output
GET/api/toolsTool capacity + availability
GET/api/reposList cloned repos
POST/api/reposClone new repo from Gitea

Environment Variables

VariableLocationPurpose
FORGE_RELAY_URLCF Pages env, local .envhttps://workbench-relay.Arcturus-Prime.com
FORGE_RELAY_SECRETCF Pages env, relay OpenRC serviceBearer token for relay auth
WORKBENCH_API_TOKENVM dispatcher service, OpenClaw .envDispatcher API auth
GITEA_API_TOKENCF Pages envGitea API for git operations
OPENCLAW_API_TOKENCF Pages envOpenClaw gateway auth
GITEA_TOKENVM dispatcher serviceGit push auth

Files

VM 160 (/opt/workbench/)

FilePurpose
forge-relay/server.jsHono.js REST API for tmux control (v2)
dispatcher/main.pyFastAPI smart router, usage tracking, git worktrees
forge-attach.shttyd session attachment (reads ?arg= URL param)
init-sessions.shCreates forge-* tmux sessions if missing
healthcheck.shAuto-heal script (root cron every 2min)
status.shDiagnostic dashboard (wb status)
repos/*.gitBare git clones (auto-fetch every 5min)

Arcturus-Prime Astro

FilePurpose
src/pages/admin/workbench.astroFull workbench UI (5000+ lines)
src/pages/api/admin/forge.tsForge API route + relayFetch() proxy
src/pages/api/admin/forge-git.tsGit pipeline API route
src/pages/api/admin/openclaw.tsOpenClaw orchestration API
src/lib/forge-client.tsBrowser-side Forge API client
src/lib/tool-router.tsHeuristic tool selection engine
src/config/modules/workbench.tsModule manifest

CT 126 workbench-Izar-Host (/opt/workbench/)

FilePurpose
forge-attach.shtmux session attachment script for ttyd
init-sessions.shCreates the 5 standard forge-* sessions
forge-relay/server.jsHono.js REST API (285 lines)
forge-relay/package.jsonDependencies: hono, @hono/node-server

OpenRC services: /etc/init.d/workbench-ttyd, /etc/init.d/workbench-relay Logs: /var/log/workbench/ttyd.log, /var/log/workbench/relay.log

Source Repository

~/Development/Arcturus-Prime-workbench/ → Gitea: Arcturus-Prime/Arcturus-Prime-workbench

Troubleshooting

Terminal stuck on “Connecting to forge-shell…”

Quick checks (in order):

  1. Is the container running?

    ssh [email protected] "pct exec 126 -- rc-service workbench-ttyd status"
  2. Are tmux sessions alive?

    ssh [email protected] "pct exec 126 -- tmux ls"

    If no sessions: ssh [email protected] "pct exec 126 -- bash /opt/workbench/init-sessions.sh"

  3. Can Altair-Link reach the container?

    ssh [email protected] "curl -s -o /dev/null -w '%{http_code}\n' http://10.42.0.126:7681/"

    Should return 200.

  4. Is the CF tunnel routing correctly?

    curl -s -o /dev/null -w '%{http_code}\n' "https://workbench-term.Arcturus-Prime.com/"

    Should return 200. If 502, the tunnel can’t reach the container.

  5. Does the WebSocket connect?

    cd ~/Development/Arcturus-Prime && node -e "
    const ws = new (require('ws'))('wss://workbench-term.Arcturus-Prime.com/ws?arg=forge-shell', {handshakeTimeout:5000});
    ws.on('open', () => { console.log('OK'); ws.close(); process.exit(0); });
    ws.on('error', (e) => { console.log('FAIL:', e.message); process.exit(1); });
    setTimeout(() => { console.log('TIMEOUT'); process.exit(1); }, 6000);
    "
  6. Browser-specific checks:

    • Hard refresh: Ctrl+Shift+R (bypass cache after CF Pages deploy)
    • Disable ad blocker for Arcturus-Prime.com (can block WebSocket subdomains)
    • Check browser console for WebSocket errors (filter “ws” or “websocket”)
    • Try incognito/private window

Restart services on CT 126

ssh [email protected] "pct exec 126 -- rc-service workbench-ttyd restart"
ssh [email protected] "pct exec 126 -- rc-service workbench-relay restart"

Restart cloudflared

ssh [email protected] "sudo systemctl restart cloudflared"

Known issues

IssueCauseFix
CORS error in console from checkTermHost()ttyd doesn’t send CORS headersCode uses no-cors fetch — opaque response is expected. Console error is cosmetic if using old cached code. Hard refresh.
ERR_BLOCKED_BY_CLIENTAd blocker blocking a CF-hashed JS chunkDisable ad blocker or add exception for Arcturus-Prime.com
WebSocket works from CLI but not browserBrowser cache serving old code before CORS fixHard refresh (Ctrl+Shift+R), wait for CF Pages deploy to complete
502 from CF tunnelContainer down or tunnel misconfiguredCheck steps 1-4 above

Connection flow (browser)

1. checkTermHost() — no-cors fetch to https://workbench-term.Arcturus-Prime.com/
   - Opaque response (status 0) = reachable
   - Throws (AbortError/TypeError) = unreachable → show error overlay
2. connectTerminal(tab) — new WebSocket('wss://workbench-term.Arcturus-Prime.com/ws?arg=forge-shell')
   - 8-second connection timeout
   - On open: send resize, show terminal
   - On message: ttyd binary protocol (type byte + payload)
   - On close/error: show reconnect overlay, reset reachability cache
workbenchforgeclaude-codecodexopencodekilocodetmuxttydterminalIzar-Hostct126Tarn-Hostvm160