Self-hosted web interface for Codex, OpenCode, Mistral Vibe, and Claude Code CLIs. Plum Code WebUI gives each provider the same browser workspace: streaming chat, tool approvals, file and git panes, provider analytics, shared agents/skills/plugins, preview tooling, and built-in MCP servers inside one Docker deployment.
Default provider: Codex. Anthropic is restricting
claude -p/ introducing a credit system, so Codex is now the primary CLI. Claude stays available as a legacy option.
Sessions dashboard - create or resume projects, filter by provider, and start Codex/OpenCode/Vibe/Claude sessions from one place.
Codex chat - streaming output, tool execution, provider/model controls, YOLO mode, usage limits, and mobile-aware layout.
Analytics - unified token volume, API-equivalent spend, cache efficiency, provider mix, pricing health, and per-model breakdown.
Extensions - manage MCP servers, shared agents, skills, plugins, and provider-specific auth/config from the settings UI.
Responsive chat with the same provider-aware UI on phone-sized viewports.
- Provider-aware streaming responses over WebSocket
- Multi-session management with history, starring, groups, provider badges, and running-state indicators
- Per-session provider, model, reasoning, service-tier, web-search, and permission-mode controls
- Image attachments plus inline image generation/editing through ComfyUI MCP
- LaTeX/Math rendering with KaTeX
- Interactive choice prompts and permission approvals
- Context & token popover with live progress bar
- Usage limit bar for providers that expose quotas
- Todo and subagent lifecycle rendering when the active CLI emits them
- Shared
/agents,/skills,/subagents, and slash-command discovery
- Context Popover: Inline progress bar showing context window usage (green -> yellow -> red), click to see full token breakdown (input/output/cache read/cache write), cost, and model
- Tool-Log Panel: Full tool execution timeline with filter buttons (All, Read, Write, Bash, Web, Agent), duration tracking per tool, live timers for running tools, expandable input/output details
- Compaction Boundary Cards: Visual separators in chat when context is compacted, with expandable summary text
- Preview Tooling: Start local dev servers, inspect output, and keep process logs redacted before they are returned to the UI/API
- Codex (OpenAI) - default provider, per-turn
codex execprocess model with auto-respawn, chunk-level streaming, transcript-prefix resume, and model discovery from~/.codex/models_cache.json - OpenCode - server-backed HTTP/SSE provider routing for GLM (
z-ai/glm-*), Kimi, Anthropic/OpenAI/Gemini routes, and 75+ other LLMs - Mistral Vibe - Mistral Medium 3.5 / Devstral coding models, argv-based prompt execution, per-session
VIBE_HOME, and--continueresume - Claude Code (Anthropic) - legacy persistent stream-json provider
- Per-session provider selection; switching providers restarts the underlying CLI cleanly
- Dedicated auth routes:
/auth/codex,/auth/opencode,/auth/vibe,/auth/claude - Independent CLI instances + persisted auth per provider (
~/.codex,~/.local/share/opencode,~/.vibe,~/.claude) - Admin/helper LLM calls, such as commit message generation, route through the same Codex-first provider preference
- Unified
usage_historyledger for all providers - Token volume, request count, cache efficiency, pricing coverage, and API-equivalent spend
- Per-model pricing with explicit unpriced-model handling
- Provider grouping shared between backend analytics and frontend charts
- Codex usage limits from the ChatGPT Codex usage endpoint when OAuth tokens are available
- File Tree Browser with lazy loading and git status
- Monaco Code Editor with syntax highlighting
- Create, edit, delete, and rename files
- Three view modes: Simple, Compact, Detailed
- Full Git Panel (staging, commits, diffs, history)
- Visual branch management (create, publish, delete)
- Commit history with diff viewer
- AI-powered commit message generation
- Pull/Fetch with remote status (ahead/behind)
- Create new repositories
- Clone repositories (with repo browser)
- Push to GitHub with remote management
- Token-authenticated operations
- Built-in commands:
/help,/clear,/model,/status,/cost,/compact - User commands from
~/.claude/commands/*.md - Project commands from
{project}/.claude/commands/*.md - Autocomplete dropdown when typing
/
- Project Auto-Discovery from
~/.claude/projects - Working directory navigation
- Session starring and filtering
- PTY Reconnect with 30-minute buffer
- comfyui-images - text-to-image and image-edit tools backed by ComfyUI workflows, rendered inline in chat
- android-builder - ~25 tools for building, installing, launching, and testing Android apps via the
android-app-creatorbackend (project lifecycle, build, ADB, emulator, on-device testing) - godot - create, inspect, validate, script, and export Godot projects through a local MCP bridge
- blender - create, inspect, export, and render 3D assets through Blender Python in background mode
- Settings can also list and test global MCP servers from the provider config, such as audio or project-specific bridges
- Admin pages: user list, role management, audit log
AUTH_ALLOWED_EMAILSenv-var allowlist (gates both OAuth and basic-auth)- First-login admin bootstrap via
SEED_ADMIN_EMAIL
- Shared agents from
~/.claude/agents - Shared skills from
~/.claude/skills, including design-system skill packs - Plugin management for user and marketplace plugins
- Codex plugin browser/install flow for OpenAI-curated plugins
- Auto-sync of external skill packs and provider links for OpenCode/Vibe where supported
- Progressive Web App (PWA)
- Bottom tab navigation
- Swipe gestures for panel navigation
- Responsive design
- Native Android client (
packages/android)
- Tabbed settings interface
- Theme configuration
- Per-provider API key / OAuth management (Codex, OpenCode, Mistral Vibe, Claude, GitHub, Google)
- MCP Server management with connection testing
- Mistral API key storage encrypted with
ENCRYPTION_KEY - ComfyUI URL testing and persistence
- Memory viewer for session context
- Express.js - HTTP server
- Socket.IO - Real-time communication
- SQLite (better-sqlite3) - Database
- node-pty - interactive CLI process management
- simple-git - Git operations
- @octokit/rest - GitHub API
- React 18 - UI framework
- Vite - Build tool with code splitting
- Radix UI - Accessible components
- Tailwind CSS - Styling
- Zustand - State management
- TanStack Query - Data fetching
- Monaco Editor - Code editing
- KaTeX - Math rendering
- TypeScript - Type safety across all packages
git clone https://github.com/zwaetschge/plum-code-webui.git
cd plum-code-webui
./scripts/install.shThe installer walks you through:
- Prereq check - docker, docker compose plugin, openssl, daemon connectivity.
- Interactive
.env- public URL, port, allowlisted login emails, host paths for data/config/workspace. Auto-generatesSESSION_SECRET+JWT_SECRET. docker compose build+up -d- first run takes a few minutes.- Health wait - polls
/health(or the container's healthcheck) for up to 2 min. - Optional Codex login - runs
codex logininside the container because Codex is the default provider. Other providers can be authenticated later from Settings or their dedicated/auth/<provider>route. Can be skipped with--skip-login.
Re-run any time to reconfigure (existing .env values are preserved unless you pass --reset). Non-interactive mode (--non-interactive) takes all defaults and is useful for CI bootstraps.
Requirements: Docker 24+, Docker Compose plugin, openssl. The container ships its own Node plus the codex, opencode, vibe, and claude CLIs.
The repo's docker-compose.yml is intentionally portable: no Traefik labels, no absolute paths, no external networks. Site-specific overrides go into docker-compose.override.yml (gitignored), which Compose auto-merges. A copy-paste starting point lives at docker-compose.override.yml.example and shows how to add Traefik labels, absolute host paths (for example /mnt/user/appdata/... on Unraid), group_add for the docker socket GID, and the repair-bot rebuild sidecar.
cp .env.example .env # then edit: SESSION_SECRET, JWT_SECRET, AUTH_ALLOWED_EMAILS, FRONTEND_URL
docker compose up -d --buildAccess the WebUI at the FRONTEND_URL you configured (default http://localhost:4545).
pnpm install # workspace deps
pnpm dev # backend (3006) + frontend (5173) in parallel
# or:
./scripts/start-webui.sh # generates ephemeral secrets, kills stale PIDs, tails logsPrerequisites: Node 20+, pnpm 9+, and whichever provider CLIs you want to run locally (codex, opencode, vibe, claude).
pnpm build
pnpm startFor deployed Docker instances, use the repair-bot sidecar instead of recreating the main container from inside itself:
bash scripts/plum-rebuild.shThe script writes data/rebuild-trigger.json, waits for repair-bot to rebuild and recreate the main container from outside, then runs a sanity check against http://localhost:${WEBUI_PORT:-4545}/. Use --no-cache, --no-wait, or --timeout=N when needed.
Full schema in packages/backend/src/config.ts (zod-validated, fails fast on startup). scripts/install.sh writes the common values into .env; the rest are optional deployment or provider overrides.
| Variable | Description | Required |
|---|---|---|
SESSION_SECRET / JWT_SECRET |
Express session and JWT signing secrets (min 32 chars each). Installer auto-generates. | Yes |
AUTH_ALLOWED_EMAILS |
Comma-separated email allowlist enforced for both OAuth and basic-auth. Empty = no allowlist, only safe behind a private network or SSO proxy. | Recommended |
SEED_ADMIN_EMAIL |
First user with this email gets role=admin on first login. Defaults to the first allowlist entry when unset. |
No |
FRONTEND_URL / CORS_ALLOWED_ORIGINS / TRUST_PROXY |
Public URL, extra CORS origins, and Express proxy trust. TRUST_PROXY=true is unsafe without a trusted reverse proxy in front. |
No |
WEBUI_PORT / WEBUI_SHM_SIZE / TZ |
Host port (default 4545), Chromium shared memory (default 1gb), and timezone. |
No |
DATA_DIR / CONFIG_DIR / WORKSPACE_DIR / ALLOWED_BASE_PATHS |
Host paths bind-mounted to persistent data, provider homes, and workspaces. | No |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET / GITHUB_CALLBACK_URL |
Optional GitHub OAuth. Callback: ${FRONTEND_URL}/auth/github/callback. |
No |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET / GOOGLE_CALLBACK_URL |
Optional Google OAuth. Callback: ${FRONTEND_URL}/auth/google/callback. |
No |
ENCRYPTION_KEY |
Enables encrypted storage for provider API keys, including the Mistral key managed in Settings. | Recommended |
CLI_PROVIDER_<PROVIDER>_MODELS |
Override model menus for CODEX, OPENCODE, VIBE, or CLAUDE; empty means auto-discover or use provider fallback. |
No |
CLI_PROVIDER_<PROVIDER>_DEFAULT_MODEL |
Override defaults such as Codex gpt-5.5, OpenCode z-ai/glm-5.1, Vibe mistral-vibe-cli-latest, or Claude sonnet. |
No |
CLI_PROVIDER_OPENCODE_DEFAULT_AGENT / CLI_PROVIDER_OPENCODE_STYLE_PROMPT |
OpenCode WebUI primary agent and Codex-like communication style. Defaults to build; set style prompt to 0 or false to disable the injected reminder. |
No |
MISTRAL_API_KEY |
Enables Mistral Vibe when no user-specific key is stored in Settings. | No |
ADMIN_LLM_PROVIDER |
Pin admin/helper calls to codex, opencode, vibe, or claude. Default preference is Codex first. |
No |
CODEX_WEBUI_SANDBOX_MODE / CODEX_WEBUI_APPROVAL_POLICY |
Codex Docker defaults. The image defaults to danger-full-access / never because Codex's Landlock workspace-write sandbox is unreliable inside Docker. |
No |
CODEX_USAGE_URL / CODEX_USER_AGENT |
Override the ChatGPT Codex usage endpoint or User-Agent used for /api/usage/limits?provider=codex. |
No |
COMFYUI_URL |
Fallback ComfyUI base URL. Settings can override it without restart. | No |
OPENAI_API_KEY |
Exposes OpenAI API billing to CLI sessions and scripts/openai-image.sh when no key is stored in app settings. |
No |
GODOT_BIN / GODOT_TIMEOUT_MS |
Optional Godot 4 binary and timeout for the built-in Godot MCP. Scaffolding works without it; validation/export require it. | No |
BLENDER_BIN / BLENDER_TIMEOUT_MS |
Blender binary and timeout for the built-in Blender MCP. The runtime image defaults to blender-headless. |
No |
PREVIEW_HOSTNAME |
Optional hostname used by the dev-server preview proxy. | No |
CHROME_BIN / PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH / PUPPETEER_EXECUTABLE_PATH |
System Chromium wrapper exposed to CLI sessions (default: /usr/local/bin/plum-chromium). |
No |
The backend spawns each provider as a child process and bridges its stream over Socket.IO:
# Codex - default, single-shot per turn; WebUI respawns after turn.completed
codex exec --json --skip-git-repo-check --cd /workspace/my-project ...
# OpenCode - server-backed model routing for GLM, Kimi, Anthropic, OpenAI, etc.
opencode run --format json --model z-ai/glm-5.1 ...
# Mistral Vibe - argv prompt, isolated VIBE_HOME per WebUI session
vibe --output streaming --trust --workdir /workspace/my-project -p "..."
# Claude Code - legacy persistent stream-json provider
claude --print --verbose --output-format stream-json --input-format stream-json \
--include-partial-messages --dangerously-skip-permissionsAll CLIs ship inside the container; their auth/state directories (~/.codex, ~/.opencode, ~/.vibe, ~/.claude) survive rebuilds via the ${CONFIG_DIR} bind mount. OpenCode is symlinked into its expected ~/.config/opencode and ~/.local/share/opencode paths. The runtime image also includes system Chromium, Chromedriver, fonts, and Xvfb; sessions inherit CHROME_BIN=/usr/local/bin/plum-chromium plus Playwright/Puppeteer executable-path env vars for headless browser checks.
packages/
├── backend/ # Express + Socket.IO server
│ ├── src/
│ │ ├── routes/ # REST API endpoints (~30 modules)
│ │ ├── services/
│ │ │ └── claude/ # Legacy folder name; owns Codex / OpenCode / Vibe / Claude lifecycle
│ │ ├── auth/ # Passport (GitHub, Google) + basic-auth + allowlist
│ │ ├── middleware/ # CSP, rate limiting, error handling
│ │ └── db/ # SQLite (better-sqlite3) + migrations
├── frontend/ # React 18 + Vite SPA
│ ├── src/
│ │ ├── components/
│ │ │ ├── chat/ # Messages, tools, compaction cards, subagents
│ │ │ ├── session/ # Controls, tool log
│ │ │ └── ui/ # Radix-based primitives
│ │ ├── pages/ # Sessions, admin, settings
│ │ ├── stores/ # Zustand stores
│ │ ├── services/ # API + Socket.IO client
│ │ └── hooks/
├── shared/ # Shared TypeScript types
├── desktop/ # Desktop shell wrapper
├── android/ # Native Android client
└── scripts/
└── mcp-servers/ # comfyui.mjs, android-builder.mjs, godot.mjs, blender.mjs (stdio MCP)
GET /api/sessions- List all sessionsPOST /api/sessions- Create new sessionGET /api/sessions/:id- Get session detailsPUT /api/sessions/:id- Update session metadataPATCH /api/sessions/:id/star- Toggle starPATCH /api/sessions/:id/provider- Switch provider for a sessionPATCH /api/sessions/:id/mode- Switch permission modeGET /api/sessions/:id/messages- Load session messagesPOST /api/sessions/:id/rewind- Rewind to an earlier message
GET /api/files?path=- List directory contentsGET /api/files/content?path=- Read file contentPOST /api/files- Create filePUT /api/files- Update fileDELETE /api/files?path=- Delete file
GET /api/git/status?path=- Get git statusPOST /api/git/stage- Stage filesPOST /api/git/commit- Create commitPOST /api/git/pull- Pull from remotePOST /api/git/push- Push to remotePOST /api/git/branch/create- Create branchPOST /api/git/generate-commit-message- AI commit message
GET /api/github/repos- List user reposPOST /api/github/repos- Create repoPOST /api/github/clone- Clone repoPOST /api/github/push- Push to GitHub
GET /api/commands- List available commandsPOST /api/commands/execute- Execute command
GET /api/cli-providers- List configured CLI providersGET /api/cli-providers/available- Check provider availability/auth stateGET /api/cli-providers/diagnostics- Run provider diagnosticsGET /api/cli-providers/:id/models- List models for one providerGET /api/usage/limits?provider=codex- Fetch provider quota/limit data where supportedGET /api/analytics/summary- Usage summary and pricing totalsGET /api/analytics/timeline- Usage timeline for charts
GET /api/settings/PUT /api/settings- User settingsGET/PUT/DELETE /api/settings/mistral-key- Mistral Vibe key managementGET/PUT /api/settings/integrations- OpenAI/ComfyUI integration settingsGET/POST/PUT/DELETE /api/mcp- MCP server management and testingGET/PUT /api/comfyui/settings- ComfyUI endpoint configurationPOST /api/comfyui/generate- Submit an image generation jobGET/POST/PUT/DELETE /api/claude-config/agents- Shared agent filesGET/POST/PUT/DELETE /api/claude-config/skills- Shared skill packsGET/POST/PUT/DELETE /api/claude-config/plugins- User and marketplace pluginsGET /api/codex/plugins,POST /api/codex/plugins/install,POST /api/codex/plugins/:id- Codex plugin listing, install, and enable/disable flow
session:send- Send a message to the session's active providersession:subscribe- Subscribe to session updatessession:interrupt- Interrupt the active CLI processsession:reconnect- Reconnect with buffer replay
session:output- Streaming text deltassession:message- Complete persisted messagesession:thinking- Thinking indicator (boolean)session:tool_use- Tool lifecycle (started / completed / error) with duration trackingsession:todos- Todo list updatessession:usage- Token usage datasession:compact- Context compaction eventssession:agent- Subagent (Task tool) lifecycle eventssession:mode- Permission mode changessession:status- Session state changes
- Fork the repository
- Create a feature branch
- Make your changes
- Run
pnpm typecheckandpnpm lint - Submit a pull request
MIT License - see LICENSE for details.
- OpenAI for Codex
- OpenCode for multi-provider CLI routing
- Mistral AI for Mistral Vibe and Devstral models
- Anthropic for Claude and Claude Code
