Skip to content

RaZLeN/claudedeck

Repository files navigation

ClaudeDeck

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.


Screenshots

Chat — streaming markdown replies, a searchable session sidebar, and an agent-produced artifact card (view / copy / download):

ClaudeDeck chat with an artifact card

Structured questions — an agent can ask clickable single / multi / free-text questions instead of plain prose; the answers come back as its next message:

ClaudeDeck structured questions card

Admin console — manage agents (per-agent model, enable/disable), MCP servers, instructions and users, all from the browser:

ClaudeDeck admin — agents

ClaudeDeck admin — per-agent MCP servers

ClaudeDeck admin — users

Why

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.

Features

  • Streaming chat over WebSocket — token-by-token, with conversation history persisted in PostgreSQL and --resume continuity 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.md and .claude/rules/*.md from 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 up brings up the app, PostgreSQL, and the pinned Claude CLI.

Architecture

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/Dockerfile because 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

Quick start (Docker)

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 --build

Then authenticate the Claude CLI once (its auth is stored in a named volume and survives rebuilds):

docker compose exec -it app claude login

Open 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.

First admin

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.

Configuration

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

Admin panel options

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/_base template into agents/<key>/.
  • Instructions — edit the selected agent's CLAUDE.md and .claude/rules/*.md files. 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-interactive claude -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.

Defining an agent

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.

Structured output

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.

Security notes

  • 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/ssestdio/command servers 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) under frontend/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.

Upgrading the Claude CLI

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.

Development (without Docker)

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.

License

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.

About

Self-hostable web UI and admin console for the Claude Code CLI — multi-user streaming chat with per-agent MCP servers, instructions, and model management.

Topics

Resources

License

Contributing

Stars

4 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors