A self-hostable web UI and admin console for Claude Code.
ClaudeDeck turns the Claude Code CLI into a multi-user chat service you can run on your own server. Your users open a browser, pick an agent, and chat — each message is a Claude Code run on the backend, streamed back token by token. Administrators configure everything from a web panel: which model to use, which MCP servers each agent can reach, the agent's system instructions, and who's allowed in.
It started life as the reusable core of an internal "AI analyst" product and was extracted into a general-purpose framework: bring your own agent persona and MCP tools, and you have a production-shaped chat app around Claude Code without writing the plumbing.
Chat — streaming markdown replies, a searchable session sidebar, and an agent-produced artifact card (view / copy / download):
Structured questions — an agent can ask clickable single / multi / free-text questions instead of plain prose; the answers come back as its next message:
Admin console — manage agents (per-agent model, enable/disable), MCP servers, instructions and users, all from the browser:
The Claude Code CLI is great in a terminal, but turning it into a shared web service means
solving the same problems every time: streaming claude -p over a WebSocket, persisting
conversations, resuming sessions, letting non-technical users talk to it, and giving admins a
safe way to change the model / tools / prompt without redeploying. ClaudeDeck is that layer.
- Streaming chat over WebSocket — token-by-token, with conversation history persisted in
PostgreSQL and
--resumecontinuity across messages. - Multiple agents. Each agent is a Claude Code workspace (its own
CLAUDE.md, rule files, MCP servers and model). Users pick which agent to talk to; admins create and edit them from the panel. - Per-agent MCP management. Add/remove/enable HTTP & SSE MCP servers from the admin UI and check their live connection status — no redeploy. (stdio/command servers are intentionally rejected via the UI to avoid turning the panel into remote code execution.)
- Live instruction editing. Edit an agent's
CLAUDE.mdand.claude/rules/*.mdfrom the browser; changes apply to the next message. - Model selection. A global default model plus per-agent overrides, all editable in the panel.
- Structured output (optional). Agents can emit two marker blocks that the UI renders specially: questions (clickable single/multi/text forms) and a final artifact (a markdown document shown as a view/copy/download card). See Structured output.
- Local user accounts with an admin role, managed in the panel. Passwords are scrypt-hashed.
- Admin console: settings, agents, instructions, MCP servers, users, and Claude CLI status/version.
- Dockerized:
docker compose upbrings up the app, PostgreSQL, and the pinned Claude CLI.
Browser (chat / admin)
│ HTTP + WebSocket
▼
Node.js + Express backend ──spawn──> claude -p (Claude Code CLI)
│ │
├── PostgreSQL 16 ├── agents/<key>/CLAUDE.md + .claude/rules/*.md
│ users / sessions / messages └── agents/<key>/.mcp.json (MCP servers)
│ settings / agents
└── express-session (cookie)
- Each chat message spawns
claude -p <message> --output-format stream-json --include-partial-messages --resume <id> --model <model>with the agent's workspace as the working directory. The backend parses the stream, filters out marker blocks, and forwards visible text to the browser. - The CLI version is pinned in
backend/Dockerfilebecause the stream-json output contract is version-sensitive.
claudedeck/
├── backend/ # Node.js + TypeScript (Express, ws, pg)
│ ├── src/
│ │ ├── index.ts # server, session cookie, WS, bootstrap, graceful shutdown
│ │ ├── auth/ # local-auth (login), password (scrypt), access (requireAuth/Admin)
│ │ ├── claude/ # session-manager (spawn CLI), marker-parser, mcp-admin
│ │ ├── config/ # settings, agents, agents-logic (pure)
│ │ ├── db/migrate.ts # idempotent SQL migration runner
│ │ ├── ws/ # chat-handler, expiration cron
│ │ └── api/ # routes (sessions/agents), admin (settings/agents/mcp/users)
│ └── test/ # node:test unit tests
├── frontend/ # static HTML/CSS/vanilla JS (chat, admin, login)
├── agents/
│ ├── _base/ # template copied when you create a new agent
│ └── default/ # the built-in "Assistant" agent
├── db/migrations/ # 001_init.sql (base schema)
├── docker-compose.yml
└── .env.example
Requirements: Docker + Docker Compose, and a Claude subscription/API access for the CLI.
git clone https://github.com/RaZLeN/claudedeck.git
cd claudedeck
cp .env.example .env
# Edit .env: set SESSION_SECRET (random 32+ bytes) and ADMIN_PASSWORD.
# The container runs as uid 1000 and writes agent config into ./agents from the admin panel:
sudo chown -R 1000:1000 ./agents
docker compose up -d --buildThen authenticate the Claude CLI once (its auth is stored in a named volume and survives rebuilds):
docker compose exec -it app claude loginOpen http://localhost:3000, log in with the admin credentials from .env, and start chatting.
Check the Claude CLI tab in the admin panel to confirm the CLI is authenticated.
On first start (empty users table) ClaudeDeck creates an admin from ADMIN_USERNAME /
ADMIN_PASSWORD (defaults admin / admin, with a warning). Change the password in the
admin panel or set ADMIN_PASSWORD before exposing the service.
Most things are configured in two places: environment variables (infra) and the admin panel
(runtime). Environment variables live in .env (see .env.example for the full list):
| Variable | Purpose | Default |
|---|---|---|
PORT |
HTTP/WS port | 3000 |
SESSION_SECRET |
Cookie signing secret (32+ bytes). Required in production. | change-me |
COOKIE_SECURE |
Set true when served over HTTPS |
false |
APP_TITLE |
Branding shown in the UI (also editable in the panel) | ClaudeDeck |
ADMIN_USERNAME / ADMIN_PASSWORD |
First admin, created once | admin / admin |
CLAUDE_MODEL |
Default model when an agent has no override | sonnet |
CLAUDE_MAX_SESSIONS |
Max concurrent CLI processes | 5 |
CLAUDE_MAX_TURNS |
Max CLI turns per message | 100 |
CLAUDE_SESSION_EXPIRY_HOURS |
Idle hours before a session is marked expired | 24 |
POSTGRES_* |
Database connection | see .env.example |
Open /admin as an administrator:
- Settings — the default model (
opus/sonnet/haiku, or an exact model id) and the app title. - Agents — create, edit, enable/disable, and order the agents users can pick. Each agent has
a key (immutable), a label, and an optional per-agent model override. Creating an agent copies
the
agents/_basetemplate intoagents/<key>/. - Instructions — edit the selected agent's
CLAUDE.mdand.claude/rules/*.mdfiles. Changes take effect on the next message (each message is a fresh CLI process reading these files). - MCP servers — manage the selected agent's HTTP/SSE MCP servers and check their live status.
Both
.mcp.json(definitions) and.claude/settings.json(enablement + tool pre-approval) are written together so the servers actually work in non-interactiveclaude -p. - Users — create/edit/delete local accounts, toggle admin and disabled flags, reset passwords. The last active administrator is protected from removal.
- Claude CLI — whether the CLI is authenticated, its version, and a read-only update check.
An agent is just a directory under agents/. The default agent is a good example:
agents/default/
├── CLAUDE.md # persona + instructions (the agent's system context)
├── .claude/
│ ├── rules/structured-output.md # extra rule files referenced from CLAUDE.md
│ └── settings.json # enabled MCP servers + tool allow-list (managed by the panel)
└── .mcp.json # MCP server definitions (managed by the panel)
To add an agent, either create it in the admin panel (recommended — it scaffolds from
_base) or copy agents/_base to agents/<key>/, edit CLAUDE.md, and add a row in the
admin Agents tab with the same key.
Agents can opt into two marker blocks (each marker on its own line). They're filtered out of the visible chat text and rendered specially. Everything else streams as normal markdown.
Questions — present clickable choices:
<<<QUESTIONS>>>
{ "questions": [
{ "id": "env", "text": "Which environment?", "type": "single", "options": ["dev","staging","prod"] },
{ "id": "notes", "text": "Anything else?", "type": "text" }
] }
<<<QUESTIONS_END>>>
type is single (radio), multi (checkboxes) or text. The user's answers come back as the
agent's next message.
Artifact — a final document the user can keep:
<<<ARTIFACT>>>
# Title
...markdown...
<<<ARTIFACT_END>>>
Rendered as a card with view / copy / download, and saved with the session. These are documented
for the agent in agents/default/.claude/rules/structured-output.md; reuse or drop them per agent.
- Authentication is local username/password with scrypt-hashed passwords and a signed,
HTTP-only session cookie. Put the app behind HTTPS (
COOKIE_SECURE=true) in production. - MCP via the panel is restricted to http/sse —
stdio/commandservers are rejected so the admin UI can't be used to run arbitrary commands on the host. - Workspace paths are sandboxed under
agents/; instruction/MCP file paths are whitelisted and traversal-guarded. - The container runs as a non-root user (
node, uid 1000). - No external calls from the UI — the frontend has no build step and vendors its two
rendering libraries (
marked,DOMPurify) underfrontend/js/vendor/, so nothing is loaded from a CDN. It works fully offline / air-gapped once the CLI is authenticated. - This is a single shared Claude Code identity (the CLI login). All agents use it; per-agent isolation is at the workspace/MCP/instruction level, not separate Claude credentials.
The CLI version is pinned in backend/Dockerfile (npm install -g @anthropic-ai/claude-code@<version>)
because the stream-json contract this app parses can change between versions. To upgrade: bump the
version in the Dockerfile, docker compose build app && docker compose up -d app, then sanity-check
streaming. The Claude CLI admin tab shows the current vs. latest version but never auto-updates.
cd backend
npm install
npm run build # tsc → dist/
npm test # node:test unit tests
npm run dev # tsx watch (needs a reachable PostgreSQL + a logged-in claude CLI on PATH)You'll need a PostgreSQL instance (point POSTGRES_* at it), the claude CLI on your PATH
and authenticated, and CLAUDE_AGENTS_DIR pointing at the repo's agents/ directory. The
backend applies migrations and bootstraps the default agent + admin on startup.
MIT.
Not affiliated with Anthropic. "Claude" and "Claude Code" are trademarks of Anthropic. ClaudeDeck drives the Claude Code CLI; you bring your own Claude access.




