Skip to content

cunicopia-dev/claude-pool

Repository files navigation

claude-pool

License Runtime Claude Code TypeScript Zero Dependencies PRs Welcome

An open, self-hosted alternative to Cowork — run multiple concurrent Claude agent conversations with automatic lifecycle management, accessible from any interface you build.

What This Is

Cowork is Anthropic's desktop product that brings Claude Code's agentic architecture to knowledge work. It runs autonomous multi-step tasks, coordinates parallel workstreams, reads and writes your local files, executes code in a sandboxed VM, schedules recurring tasks, and organizes everything into persistent projects — all from the Claude Desktop app.

claude-pool and claude-socket give you a similar foundation, but self-hosted and extensible. You get the same multi-conversation agent runtime, but you control the interface, the communication layer, and what the agents can connect to.

graph LR
    B1[Your UI] <-->|WebSocket| CS1[claude-socket]
    B2[Mobile App] <-->|WebSocket| CS2[claude-socket]
    B3[Another Agent] <-->|WebSocket| CS3[claude-socket]

    subgraph "claude-pool (your machine)"
        CS1 <-->|MCP| S1[Conversation 1]
        CS2 <-->|MCP| S2[Conversation 2]
        CS3 <-->|MCP| S3[Conversation 3]
    end

    style B1 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
    style B2 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
    style B3 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
    style CS1 fill:#1a1a2e,stroke:#7c3aed,color:#e5e5e5
    style CS2 fill:#1a1a2e,stroke:#7c3aed,color:#e5e5e5
    style CS3 fill:#1a1a2e,stroke:#7c3aed,color:#e5e5e5
    style S1 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
    style S2 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
    style S3 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
Loading

claude-socket is the communication layer. It's a WebSocket bridge that lets anything connect to a Claude Code session — browsers, mobile apps, bots, other agents. The WebSocket is persistent and bidirectional, so it supports long-running conversations and can carry any kind of data, including messages from other agents.

claude-pool is the runtime layer. It manages multiple concurrent Claude Code agent processes, spinning them up on demand, idling them down when nobody's using them, and resuming them with full history when someone comes back. You're not locked into one conversation at a time, and you're not wasting resources on idle sessions.

Together they give you:

  • Multiple concurrent agent conversations — each with its own context, each accessible from whatever interface you want
  • Smart resource management — sessions spin up when needed, shut down when idle, and resume where they left off
  • Extensible communication — because it's WebSocket, not a proprietary UI, you control the interface. Build a web dashboard, connect a Discord bot, pipe in data from other systems, or have agents talk to each other
  • General-purpose agent hosting — Claude Code is the runtime, but the agents can be anything. A customer support bot, a devops assistant, a research agent, a home automation supervisor — claude-pool doesn't care what the agent does, it just manages the process. Bring your own CLAUDE.md, skills, MCP servers, and plugins to define what each agent is
  • ToS compliance — everything runs through Claude Code's official MCP channel and plugin system. No API key workarounds, no harness replacement. If you were using OpenClaw before Anthropic shut that down, this is how you get that flexibility back within the rules

How claude-pool Works

claude-pool wraps each Claude Code instance in a managed process with idle detection, crash recovery, and resume support. Think of it like a process pool for Claude Code sessions — serverless Claude on your own machine.

graph TD
    CP[claude-pool]
    CP --> S1[Session: junior]
    CP --> S2[Session: project-a]
    CP --> S3[Session: debug]

    S1 -->|PTY| C1[claude --name junior]
    S2 -->|PTY| C2[claude --name project-a]
    S3 -->|PTY| C3[claude --name debug]

    style CP fill:#1a1a2e,stroke:#7c3aed,color:#e5e5e5
    style S1 fill:#1a1a2e,stroke:#7c3aed,color:#e5e5e5
    style S2 fill:#1a1a2e,stroke:#7c3aed,color:#e5e5e5
    style S3 fill:#1a1a2e,stroke:#7c3aed,color:#e5e5e5
    style C1 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
    style C2 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
    style C3 fill:#1a1a2e,stroke:#06b6d4,color:#e5e5e5
Loading
  • Multiple concurrent sessions from one manager
  • Idle timeout — sessions automatically shut down after configurable inactivity
  • Keep warm — designate primary sessions that never idle out
  • Crash recovery — auto-restart crashed sessions (up to 3x in 5 minutes)
  • Resume — stopped sessions pick up where they left off via claude --resume
  • State persistence — session registry survives pool restarts
  • Management API — REST + WebSocket for programmatic control

Quick Start

git clone https://github.com/cunicopia-dev/claude-pool.git
cd claude-pool
bun install

Start the pool:

bun run start
claude-pool v0.1.0 | port 3200 | max 5 sessions

In another terminal, spawn a session:

bun run src/cli.ts spawn --name my-session

Check status:

bun run src/cli.ts status
claude-pool | status: ok | uptime: 45s | active: 1/5 | mem: 50MB

ID          Name                      Status      PID       Idle Time     Keep Warm
-----------------------------------------------------------------------------------
3e8e89b8    my-session                running     12345     10s           no

Docker

docker compose up -d

This mounts your Claude Code credentials (~/.claude/.credentials.json) read-only into the container. The pool manager, Bun, and Claude Code CLI all run inside the container. Session state persists in a Docker volume.

The container runs as a non-root user (claude refuses --dangerously-skip-permissions as root). Your credentials are mounted read-only — the container never writes to them.

To run without compose:

docker build -t claude-pool .
docker run -d \
  -v ~/.claude/.credentials.json:/home/claude-pool/.claude/.credentials.json:ro \
  -v ~/.claude/settings.json:/home/claude-pool/.claude/settings.json:ro \
  -v ./pool.config.json:/app/pool.config.json:ro \
  -p 3200:3200 \
  claude-pool

Configuration

Create a pool.config.json in the working directory or at ~/.config/claude-pool/config.json:

{
  "maxSessions": 5,
  "idleTimeoutMinutes": 30,
  "apiPort": 3200,
  "primarySession": {
    "name": "junior",
    "keepWarm": true,
    "channels": ["plugin:discord@claude-plugins-official"],
    "developmentChannels": ["plugin:web@my-plugin"],
    "pluginDirs": [],
    "workingDirectory": "/home/user",
    "dangerouslySkipPermissions": true
  },
  "defaults": {
    "channels": [],
    "developmentChannels": [],
    "pluginDirs": [],
    "dangerouslySkipPermissions": false,
    "workingDirectory": "/home/user"
  },
  "claudePath": "claude",
  "logLevel": "info"
}
Field Default Description
maxSessions 5 Maximum concurrent sessions
idleTimeoutMinutes 30 Kill sessions after this many minutes of inactivity
apiPort 3200 Management API port
primarySession Auto-spawned session on pool start
primarySession.keepWarm false Skip idle timeout for this session
defaults Default config applied to all new sessions
claudePath "claude" Path to the Claude Code binary
logLevel "info" Log verbosity: debug, info, warn, error

CLI

Command Description
claude-pool start Start the pool manager (default)
claude-pool status Show all sessions
claude-pool spawn --name <name> Create a new session
claude-pool kill <id> Kill a session
claude-pool resume <id> Resume a stopped session
claude-pool config Show resolved config

Flags: --config <path> (start/config), --port <port> (status/spawn/kill/resume)

Management API

REST API on the configured port (default 3200):

Method Path Description
GET /health Pool health — uptime, active session count, memory
GET /sessions List all sessions
POST /sessions Create a new session
GET /sessions/:id Get session details
DELETE /sessions/:id Kill a session
POST /sessions/:id/resume Resume a stopped session

WebSocket

Connect to /ws for real-time session status updates:

{"type": "sessions", "sessions": [...]}
{"type": "session_update", "session": {...}}
{"type": "session_removed", "id": "abc123"}

Session Lifecycle

stateDiagram-v2
    [*] --> starting: spawn
    starting --> running: process alive
    running --> stopping: kill / idle timeout
    running --> crashed: non-zero exit
    stopping --> stopped: process exited
    crashed --> starting: auto-restart (≤3x / 5min)
    crashed --> [*]: max restarts exceeded
    stopped --> starting: resume
    stopped --> [*]: removed
Loading
  1. Spawn — claude-pool launches claude wrapped in a PTY via script -qc
  2. Running — process alive, stdout/stderr monitored for activity timestamps
  3. Idle detection — every 60 seconds, sessions past the timeout are killed (unless keepWarm)
  4. Crash recovery — non-zero exits trigger auto-restart, up to 3 times in 5 minutes
  5. Resume — stopped sessions restart with claude --resume <session-id>, picking up full conversation history
  6. State persistence — the session registry is saved to pool.state.json so the pool can recover after its own restart

How It Works With Claude Code

Each session maps to one Claude Code process. claude-pool:

  • Captures Claude Code's native session ID from ~/.claude/sessions/<pid>.json
  • Uses --resume <session-id> to restore conversations
  • Passes through channels (--channels), development channels (--dangerously-load-development-channels), plugin directories (--plugin-dir), and permission flags
  • Wraps processes in a pseudo-TTY since Claude Code requires one for interactive mode

Running Tests

bun test

Requirements

License

Apache 2.0


Not affiliated with Anthropic. claude-pool is an independent open source project that builds on top of the Claude Code CLI.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors