Triple-C is a cross-platform desktop application that sandboxes Claude Code inside Docker containers. Each project can optionally enable full permissions mode (--dangerously-skip-permissions), giving Claude unrestricted access within the sandbox.
- Frontend: React 19 + TypeScript + Tailwind CSS v4 + Zustand state management
- Backend: Rust (Tauri v2 framework)
- Terminal: xterm.js with WebGL rendering
- Docker API: bollard (pure Rust Docker client)
┌─────────────────────────────────────────────────────┐
│ TopBar (terminal tabs + Docker/Image status) │
├────────────┬────────────────────────────────────────┤
│ Sidebar │ Main Content (terminal views) │
│ (25% w, │ │
│ responsive│ │
│ min/max) │ │
├────────────┴────────────────────────────────────────┤
│ StatusBar (project/terminal counts) │
└─────────────────────────────────────────────────────┘
- Create: New container created with bind mounts, env vars, and labels
- Start: Container started, entrypoint remaps UID/GID, sets up SSH, configures Docker group, sets up MCP servers, injects Claude Code settings
- Terminal:
docker execlaunches Claude Code (or bash shell) with a PTY - Stop: Container halted (filesystem persists in named volume); MCP containers stopped
- Restart: Existing container restarted; recreated if settings changed (detected via SHA-256 fingerprint)
- Reset: Container removed and recreated from scratch (named volume preserved)
| Target in Container | Source | Type | Notes |
|---|---|---|---|
/workspace |
Project directory | Bind | Read-write |
/home/claude/.claude |
triple-c-claude-config-{projectId} |
Named Volume | Persists across container recreation |
/tmp/.host-ssh |
SSH key directory | Bind | Read-only; entrypoint copies to ~/.ssh |
/home/claude/.aws |
AWS config directory | Bind | Read-only; for Bedrock auth |
/var/run/docker.sock |
Host Docker socket | Bind | If "Allow container spawning" is ON, or auto-enabled by stdio+Docker MCP servers |
Each project can independently use one of:
- Anthropic (OAuth): User runs
claude logininside the terminal on first use. Token persisted in the config volume across restarts and resets. - AWS Bedrock: Per-project AWS credentials (static keys, profile, or bearer token). SSO sessions are validated before launching Claude for Profile auth.
- Ollama: Connect to a local or remote Ollama server via
ANTHROPIC_BASE_URL(e.g.,http://host.docker.internal:11434). Requires a model ID, and the model must be pulled (or used via Ollama cloud) before starting the container. - OpenAI Compatible: Connect through any OpenAI API-compatible endpoint (LiteLLM, OpenRouter, vLLM, text-generation-inference, LocalAI, etc.) via
ANTHROPIC_BASE_URL+ANTHROPIC_AUTH_TOKEN. API key stored securely in OS keychain.
Note: Ollama and OpenAI Compatible support is best-effort. Claude Code is designed for Anthropic models, so some features (tool use, extended thinking, prompt caching, etc.) may not work as expected with non-Anthropic models behind these backends.
When "Allow container spawning" is enabled per-project, the host Docker socket is bind-mounted into the container. This allows Claude Code to create sibling containers (not nested Docker-in-Docker) that are visible to the host. The entrypoint detects the socket's GID and adds the claude user to the matching group.
If the Docker access setting is toggled after a container already exists, the container is automatically recreated on next start to apply the mount change. The named config volume (keyed by project ID) is preserved across recreation.
Triple-C supports Model Context Protocol (MCP) servers as a Beta feature. MCP servers extend Claude Code with external tools and data sources.
Modes: Each MCP server operates in one of four modes based on transport type and whether a Docker image is specified:
| Mode | Where It Runs | How It Communicates |
|---|---|---|
| Stdio + Manual | Inside the project container | Direct stdin/stdout (e.g., npx -y @mcp/server) |
| Stdio + Docker | Separate MCP container | docker exec -i <mcp-container> <command> from the project container |
| HTTP + Manual | External / user-provided | Connects to the URL you specify |
| HTTP + Docker | Separate MCP container | http://<mcp-container>:<port>/mcp via Docker DNS on a shared bridge network |
Key behaviors:
- Global library: MCP servers are defined globally in the MCP sidebar tab and stored in
mcp_servers.json - Per-project toggles: Each project enables/disables individual servers via checkboxes
- Auto-pull: Docker images for MCP servers are pulled automatically if not present when the project starts
- Docker networking: Docker-based MCP containers run on a per-project bridge network (
triple-c-net-{projectId}), reachable by container name — not localhost - Auto-detection: Config changes are detected via SHA-256 fingerprints and trigger automatic container recreation
- Config injection: MCP server configuration is written to
~/.claude.jsoninside the container via theMCP_SERVERS_JSONenvironment variable, merged by the entrypoint usingjq
Optional per-project integration with Flight Control — an AI-first development methodology bundled with Triple-C. When enabled, the bundled files are installed into the container, skills are installed, and workflow instructions are injected into CLAUDE.md.
Triple-C includes an optional web terminal server for accessing project terminals from tablets, phones, or other devices on the local network. When enabled in Settings, an axum HTTP+WebSocket server starts inside the Tauri process, serving a standalone xterm.js-based terminal UI.
- URL:
http://<LAN_IP>:7681?token=...(port configurable) - Authentication: Token-based (auto-generated, copyable from Settings)
- Protocol: JSON over WebSocket with base64-encoded terminal data
- Features: Project picker, multiple tabs (Claude + bash sessions), mobile-optimized input bar, scroll-to-bottom button
- Session cleanup: All terminal sessions are closed when the browser disconnects
The web terminal shares the existing ExecSessionManager via Arc-wrapped stores — same Docker exec sessions, different transport (WebSocket instead of Tauri IPC events).
Triple-C includes optional speech-to-text powered by Faster Whisper running in a separate Docker container. When enabled, a microphone button appears in the bottom-left corner of each terminal view.
- Hotkey:
Ctrl+Shift+Mto toggle recording - Models:
tiny,small, ormedium(configurable in Settings) - Port: Default
9876(configurable) - Language: Optional language hint for transcription
- Auto-start: When STT is enabled in Settings, the container starts automatically with the app — no need to manually start it after each restart
- On-demand fallback: If not auto-started, the container starts automatically when you first click the mic button
How it works: Audio is captured in the browser via the Web Audio API, encoded as WAV, and sent to the Faster Whisper container's /transcribe endpoint. The transcribed text is inserted directly into the active terminal. The STT container uses a named Docker volume (triple-c-stt-model-cache) to cache Whisper models across restarts.
The socket path is OS-aware:
- Linux/macOS:
/var/run/docker.sock - Windows:
//./pipe/docker_engine
Users can override this in Settings via the global docker_socket_path option.
| File | Purpose |
|---|---|
app/src/App.tsx |
Root layout (TopBar + Sidebar + Main + StatusBar) |
app/src/index.css |
Global CSS variables, dark theme, color-scheme: dark |
app/src/components/layout/TopBar.tsx |
Terminal tabs + Docker/Image status indicators |
app/src/components/layout/Sidebar.tsx |
Responsive sidebar (25% width, min 224px, max 320px) |
app/src/components/layout/StatusBar.tsx |
Running project/terminal counts |
app/src/components/projects/ProjectCard.tsx |
Project config, backend selector, action buttons |
app/src/components/projects/ClaudeCodeSettingsModal.tsx |
Claude Code CLI settings modal (TUI mode, effort, focus, caching) |
app/src/components/projects/ProjectList.tsx |
Project list in sidebar |
app/src/components/projects/FileManagerModal.tsx |
File browser modal (browse, download, upload) |
app/src/components/projects/ContainerProgressModal.tsx |
Real-time container operation progress |
app/src/components/mcp/McpPanel.tsx |
MCP server library (global configuration) |
app/src/components/mcp/McpServerCard.tsx |
Individual MCP server configuration card |
app/src/components/settings/SettingsPanel.tsx |
Docker, AWS, timezone, web terminal, and global settings |
app/src/components/settings/WebTerminalSettings.tsx |
Web terminal toggle, URL, token management |
app/src/components/settings/SttSettings.tsx |
STT settings panel (model, port, language, container controls) |
app/src/components/terminal/TerminalView.tsx |
xterm.js terminal with WebGL, URL detection, OSC 52 clipboard, image paste |
app/src/components/terminal/SttButton.tsx |
Mic button overlay with on-demand container start |
app/src/components/terminal/TerminalTabs.tsx |
Tab bar for multiple terminal sessions (claude + bash) |
app/src/hooks/useTerminal.ts |
Terminal session management (claude and bash modes) |
app/src/hooks/useFileManager.ts |
File manager operations (list, download, upload) |
app/src/hooks/useMcpServers.ts |
MCP server CRUD operations |
app/src/hooks/useSTT.ts |
Speech-to-text recording, transcription, and container management |
app/src-tauri/src/docker/container.rs |
Container creation, mounts, env vars, MCP injection, fingerprinting |
app/src-tauri/src/docker/exec.rs |
PTY exec sessions, file upload/download via tar |
app/src-tauri/src/docker/image.rs |
Image building/pulling |
app/src-tauri/src/docker/network.rs |
Per-project bridge networks for MCP containers |
app/src-tauri/src/commands/project_commands.rs |
Start/stop/rebuild Tauri command handlers |
app/src-tauri/src/commands/file_commands.rs |
File manager Tauri commands (list, download, upload) |
app/src-tauri/src/commands/mcp_commands.rs |
MCP server CRUD Tauri commands |
app/src-tauri/src/models/project.rs |
Project struct (backend, Docker access, Claude Code settings, MCP servers, Mission Control) |
app/src-tauri/src/models/mcp_server.rs |
MCP server struct (transport, Docker image, env vars) |
app/src-tauri/src/models/app_settings.rs |
Global settings (image source, Docker socket, AWS, Claude Code settings, web terminal, STT) |
app/src-tauri/src/web_terminal/server.rs |
Axum HTTP+WS server for remote terminal access |
app/src-tauri/src/web_terminal/ws_handler.rs |
WebSocket connection handler and session management |
app/src-tauri/src/web_terminal/terminal.html |
Embedded web UI (xterm.js, project picker, tabs) |
app/src-tauri/src/commands/stt_commands.rs |
STT start/stop/transcribe Tauri commands |
app/src-tauri/src/commands/web_terminal_commands.rs |
Web terminal start/stop/status Tauri commands |
app/src-tauri/src/storage/mcp_store.rs |
MCP server persistence (JSON with atomic writes) |
app/src-tauri/src/docker/stt.rs |
STT Docker container lifecycle (create, start, stop, build, pull) |
app/src/lib/wav.ts |
WAV audio encoding for STT transcription |
stt-container/Dockerfile |
Faster Whisper STT container image (Python 3.11 + FastAPI) |
stt-container/server.py |
STT HTTP server (POST /transcribe endpoint) |
container/Dockerfile |
Ubuntu 24.04 sandbox image with Claude Code + dev tools + clipboard/audio shims |
container/entrypoint.sh |
UID/GID remap, SSH setup, Docker group config, MCP injection, Claude Code settings injection, Mission Control setup |
container/osc52-clipboard |
Clipboard shim (xclip/xsel/pbcopy via OSC 52) |
container/audio-shim |
Audio capture shim (rec/arecord via FIFO) for voice mode |
- Uses Tailwind CSS v4 with the Vite plugin (
@tailwindcss/vite) - All colors use CSS custom properties defined in
index.css:root color-scheme: darkis set on:rootso native form controls (select dropdowns, scrollbars) render in dark mode- Do not add a global
* { padding: 0 }reset — Tailwind v4 uses CSS@layer, and unlayered CSS overrides all layered utilities. Tailwind's built-in Preflight handles resets.
Base: Ubuntu 24.04
Pre-installed tools: Claude Code, Node.js 22 LTS + pnpm, Python 3.12 + uv + ruff, Rust (stable), Docker CLI, git + gh, AWS CLI v2, ripgrep, openssh-client, build-essential
Shims: xclip/xsel/pbcopy (OSC 52 clipboard forwarding), rec/arecord (audio FIFO for voice mode)
Default user: claude (UID/GID 1000, remapped by entrypoint to match host)