Files
Triple-C/HOW-TO-USE.md
T
shadow-testandClaude Opus 5 4fdfed7955
Build App / compute-version (pull_request) Successful in 4s
Build App / build-macos (pull_request) Successful in 2m28s
Build App / build-windows (pull_request) Successful in 5m13s
Build Container / build-container (pull_request) Successful in 13m11s
Build App / build-linux (pull_request) Successful in 6m53s
Build App / create-tag (pull_request) Skipped
Build App / sync-to-github (pull_request) Skipped
Bake the browser's runtime libraries into the base image
`npx playwright install chromium` downloaded ~150 MB of browser that then
died with "error while loading shared libraries: libglib-2.0.so.0" —
verified, not inferred, against the current image. The image shipped none
of Chromium's shared libraries, which is why `apt install
google-chrome-stable` looked like the cure: apt was quietly installing the
same set as Chrome's own dependencies.

Installing them at runtime instead converges on the worst possible state.
The libraries land in the container's writable layer, so they are re-paid
after every Reset and *lost* on base-image migration, which replays apt
from a manifest. The browsers ride in ~/.cache/ms-playwright, inside the
home volume, and survive both — leaving a 400 MB browser present with its
libraries gone. So the libraries are baked and the browsers are not: each
half now lives where it already persists.

The layer runs `npx --yes playwright@latest install-deps chromium` rather
than a hand-written apt list. Ubuntu 24.04's 64-bit-time_t transition
renamed a swathe of these packages (libasound2t64, libatk1.0-0t64,
libglib2.0-0t64, …) and a new Chromium dependency would drift straight back
into the launch failure this exists to prevent; letting Playwright name its
own dependencies is self-maintaining. It sits immediately after Node — npx
is its only prerequisite — and well above the shim COPYs, so editing a shim
does not re-run it.

The `--dry-run` that follows is a build-time assertion, not decoration: on a
platform Playwright has no list for, `install-deps` prints a warning and
returns having installed **nothing, with exit status 0**. Without the
assertion that ships a broken image behind a clean build log.

Measured, on a build of this file with the layer applied over an otherwise
identical image: +99 packages, +334 MiB unpacked and +119 MiB compressed
(2950 → 3284 MiB, 759 → 878 MiB). Two thirds of that is not reachable by
trimming — libgbm1, which Chromium needs, pulls mesa-libgallium, which
pulls libllvm20. A chromium-only apt list measures 247 MiB against
install-deps' 341 MiB; the ~94 MiB difference is xvfb and the CJK/emoji
fonts, kept because the base ships no fonts at all and every page this
feature exists to display would otherwise render as tofu.

Verified on real builds, both architectures: a `--platform linux/arm64`
build of this file installs the same 99 packages and passes the same
assertion. On the new amd64 image, `playwright install chromium` with no
`--with-deps` and no `install-deps` launches headless Chromium 151.0.7922.34
and loads a page; on the old image the identical script fails on
libglib-2.0.so.0.

`install.rs` no longer runs `install-deps` unconditionally — that would be a
minutes-long apt run for nothing on a current image. It asks
`install-deps --dry-run` first and skips the install when everything is
present, saying which of the two happened on the progress stream. The check
is Playwright's rather than a probe of our own for library names, so check
and fix cannot disagree about what the dependency set is. Note that
`--dry-run` exits 0 both when everything is installed and when Playwright
has no list for the platform, so the verdict is read from its output.

Containers on older images stay the normal case until people migrate, and
they still work: on such an image the simulation cannot even resolve the
package names (the index is cleaned in every base image), which reports as
"couldn't tell" and installs — the right answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSP2KNPhuWKQ4DL5TZEn3k
2026-08-10 10:57:21 -07:00

67 KiB
Raw Blame History

How to Use Triple-C

Triple-C (Claude-Code-Container) is a desktop application that runs Claude Code inside isolated Docker containers. Each project gets its own sandboxed environment with bind-mounted directories, so Claude only has access to the files you explicitly provide.


Table of Contents


Prerequisites

Docker

Triple-C requires a running Docker daemon. Install one of the following:

Platform Option Link
Windows Docker Desktop https://docs.docker.com/desktop/install/windows-install/
macOS Docker Desktop https://docs.docker.com/desktop/install/mac-install/
Linux Docker Engine https://docs.docker.com/engine/install/
Linux Docker Desktop (alternative) https://docs.docker.com/desktop/install/linux/

After installation, verify Docker is running:

docker info

Windows note: Docker Desktop must be running before launching Triple-C. The app communicates with Docker through the named pipe at //./pipe/docker_engine.

Linux note: Your user must have permission to access the Docker socket (/var/run/docker.sock). Either add your user to the docker group (sudo usermod -aG docker $USER, then log out and back in) or run Docker in rootless mode.

Claude Code Account

You need access to Claude Code through one of:

  • Anthropic account — Sign up at https://claude.ai and use claude login (OAuth) inside the terminal
  • AWS Bedrock — An AWS account with Bedrock access and Claude models enabled
  • Ollama — A local or remote Ollama server (best-effort support)
  • llama.cpp — A local or remote llama-server (best-effort support)
  • OpenAI Compatible — A gateway that implements the Anthropic Messages API, such as LiteLLM (best-effort support). A server that only speaks OpenAI's /v1/chat/completions will not work — see OpenAI Compatible Configuration.

First Launch

1. Get the Container Image

When you first open Triple-C, go to the Settings tab in the sidebar. Under Docker, you'll see:

  • Docker Status — Should show "Connected" (green). If it shows "Not Available", make sure Docker is running.
  • Image Status — Will show "Not Found" on first launch.

Choose an Image Source:

Source Description When to Use
Registry Pulls the pre-built image from ghcr.io Fastest setup — recommended for most users
Local Build Builds the image locally from the embedded Dockerfile If you can't reach the registry, or want a custom build
Custom Use any Docker image you specify Advanced — bring your own sandbox image

Click Pull Image (for Registry/Custom) or Build Image (for Local Build). A progress log will stream below the button. When complete, the status changes to "Ready" (green).

2. Create Your First Project

Switch to the Projects tab in the sidebar and click + Add.

  1. Project Name — Give it a meaningful name (e.g., "my-web-app").
  2. Folders — Click Browse to select a directory on your host machine. This directory will be mounted into the container at /workspace/<folder-name>. You can add multiple folders with the + button at the bottom of the folder list.
  3. Click Add Project.

3. Start the Container

Click the project in the sidebar. Its Project Home opens as a tab in the main area. Click Start in the Project Home header (or use the play control that appears when you hover the sidebar row).

Progress is reported inline — the sidebar row and the Project Home header show messages like "Creating container…" and "Starting container…" while the status moves from Stopped () to Starting () to Running (). Nothing blocks the rest of the app; if something fails you get a toast with the full detail behind a Details disclosure.

4. Open a Terminal

Click Open Claude Terminal in the Project Home header, or press Ctrl+T. A new tab appears in the main tab strip and an xterm.js terminal loads.

Claude Code launches automatically. The project's permission mode decides how much it asks before acting — the default is to prompt before each tool call. See Permission Modes.

5. Authenticate

Anthropic — shared token (recommended):

Run claude setup-token once from Settings → Claude Authentication in the sidebar, and every Anthropic-backend project uses that token without its own login. See Shared Claude Authentication.

Anthropic — per-container OAuth:

  1. Type claude login or /login in the terminal.
  2. Claude prints an OAuth URL. Triple-C detects long URLs and shows a clickable toast at the top of the terminal — click Open to open it in your browser.
  3. Complete the login in your browser. The token is saved and persists across container stops, starts and recreations. A Reset deletes it — see below.

If the login hangs after the browser step, the callback could not reach the container. Enable the Auth Bridge for that project.

AWS Bedrock:

  1. Stop the container first (most settings can only be changed while stopped).
  2. Open the project's Config tab and, under Model, set Backend to Bedrock.
  3. Fill in your AWS credentials in the same section (see AWS Bedrock Configuration below).
  4. Start the container again.

Ollama:

  1. Stop the container first (most settings can only be changed while stopped).
  2. Open the project's Config tab and, under Model, set Backend to Ollama.
  3. Set the base URL of your Ollama server (defaults to http://host.docker.internal:11434 for a local instance). Set the Model to the model you want to use (required).
  4. Make sure the model has been pulled in Ollama (e.g., ollama pull qwen3.5:27b) or used via Ollama cloud before starting.
  5. Start the container again.

llama.cpp:

  1. Stop the container first (most settings can only be changed while stopped).
  2. Open the project's Config tab and, under Model, set Backend to llama.cpp.
  3. Set the base URL of your llama-server (defaults to http://host.docker.internal:8080, llama-server's default port). Set the Model to the model it is serving.
  4. Start the container again.

OpenAI Compatible:

  1. Stop the container first (most settings can only be changed while stopped).
  2. Open the project's Config tab and, under Model, set Backend to OpenAI Compatible.
  3. Set the base URL of your gateway (defaults to http://host.docker.internal:4000, LiteLLM's default port). Optionally set an API key and model.
  4. Start the container again.

The Interface

┌──────────────────────────────────────────────────────────────────────┐
│ [⌂ my-app] [▣ my-app ask] [▣ my-app (bash)]   Docker ● Image ● ?     │
├─────────────┬────────────────────────────────────────────────────────┤
│  Sidebar    │  ┌──────────────────────────────────────────────────┐  │
│             │  │ my-app   ● Running · up 2h 5m                    │  │
│  Projects   │  │  [Open Claude Terminal] [Shell] [Files]          │  │
│  Settings   │  │  [Stop] [⋯]                                      │  │
│             │  ├──────────────────────────────────────────────────┤  │
│  ● my-app   │  │ Overview · Sessions · Automation · Config · Files│  │
│  ○ other    │  ├──────────────────────────────────────────────────┤  │
│             │  │                                                  │  │
│             │  │        (Project Home, or a terminal view)        │  │
│             │  │                                                  │  │
│             │  └──────────────────────────────────────────────────┘  │
├─────────────┴────────────────────────────────────────────────────────┤
│  2 project(s) · 1 running · 2 terminal(s)          Jump to Current ↓ │
└──────────────────────────────────────────────────────────────────────┘
  • Tab strip (top) — One strip holds every open tab, in the order you opened them. There are two kinds: Project Home tabs ( glyph, project name, status glyph) and terminal tabs ( glyph, plus a small badge showing the permission mode the terminal was launched with — plan, ask, edits or bypass). Bash shell tabs show a "(bash)" suffix. Right-click a terminal tab to rename it, jump to its project home, or close it; double-click to rename inline. There is no separate terminal tab bar and no "+" button — tabs appear when you open a project or a terminal.
  • Status indicators (top right) — Docker connection and container image availability. Each pairs a coloured dot with a word, so status is never conveyed by colour alone. The ? button opens the built-in help.
  • Sidebar — Toggle between the Projects list and the Settings panel. It collapses to a narrow icon rail with the chevron button, and remembers that choice.
  • Main area — Shows the active tab: a Project Home view or an xterm.js terminal. With no tabs open you get a welcome screen with Docker/image/project readiness checks.
  • StatusBar — Counts of total projects, running containers and open terminal sessions; the Jump to Current ↓ button when a terminal is scrolled up; and the microphone button when speech-to-text is enabled.

Project Home

Clicking a project in the sidebar opens Project Home in the main area. The sidebar row is only for selecting a project and for two quick controls that appear on hover — start/stop, and open a Claude terminal. Everything else about a project lives in Project Home.

The header shows the project name, its status, how long the container has been up, and the action buttons. Below that are five tabs:

Tab What it's for
Overview The permission mode control, a summary of the backend and sandbox settings, capability tiles, recent sessions and scheduled tasks
Sessions Past Claude Code conversations stored on this project's config volume, each with a Resume button
Automation The scheduled tasks running inside this container — see Automation & Scheduled Tasks
Config All per-project configuration — see Project Configuration
Files Browse, download and upload files inside the container

Sessions

Claude Code records each conversation on the project's config volume. The Sessions tab lists them with a name or summary, the session id, its working directory, its age, size and message count. Refresh re-reads the list.

Resume opens a new shell tab and runs claude --resume <session-id> for you, with the project's current permission-mode flags applied. The Overview tab shows the four most recent sessions with the same Resume action.

Sessions can only be read while the container is running, and they are stored on the config volume — so a Reset deletes them.

Capability Tiles

The Overview tab shows read-only counts of what Claude Code has available inside this container:

Tile What is counted
Skills Directories under .claude/skills/ that contain a SKILL.md
Agents .md files under .claude/agents/
Commands .md files under .claude/commands/
Hooks Hook handlers configured in .claude/settings.json / settings.local.json
Plugins Installed and enabled Claude Code plugins
MCP servers Servers in ~/.claude.json and in any .mcp.json under /workspace

Both user scope (/home/claude/.claude) and project scope (/workspace/<folder>/.claude) are included, and each tile opens a list of what it found.

Triple-C does not edit any of this. Claude Code owns skills, agents, commands, hooks, plugins and MCP servers, and it has good built-in tooling for them. The tiles are a window, not an editor: Manage in terminal opens a terminal in the container so you can use /agents, /hooks, /plugins, /mcp and friends directly.

The counts are only available while the container is running.


Project Management

Project Status

Each project shows a status glyph paired with a word, so it is readable without relying on colour:

Glyph Status Meaning
Stopped Container is not running
Starting / Stopping Container is transitioning (the glyph pulses)
Running Container is active, ready for terminals
Error Something went wrong (check the toast for detail)

While a container is starting or stopping, the status line is replaced by the live progress message.

Project Actions

Most actions live in the Project Home header; two live behind the overflow menu next to it. The sidebar row carries only the two hover controls.

Action Where When Available What It Does
Start Project Home header; sidebar hover control Stopped Creates (if needed) and starts the container
Stop Project Home header; sidebar hover control Running Stops the container but preserves its state
Force stop Project Home header Starting / Stopping Interrupts a transition that is stuck
Open Claude Terminal Project Home header; sidebar hover control; Ctrl+T Running Opens a new Claude Code terminal tab
Shell Project Home header Running Opens a bash login shell tab in the container (no Claude Code)
Files Project Home header, and the Files tab Running Switches to the Files tab to browse, download and upload files
Config The Config tab Always Per-project configuration (most fields need the container stopped)
Back up container overflow menu A container exists Saves a .tar.gz archive of the container to a location you choose
Reset container… overflow menu Stopped or Error Destroys the container, snapshot image and both volumes, then recreates from the base image (wipes ~/.claude) — asks first
Remove project… overflow menu Always Deletes the project, its container, its volumes and its stored credentials — asks first

Both destructive actions confirm before acting, and the Reset dialog spells out what you lose: your claude login, anything installed inside the container, and every saved session transcript. Your mounted project folders live on the host and are not touched.

The backup archive includes the Claude config volume, which may contain API keys. Keep it private.

Renaming a Project

Rename a project in its Config tab, under Workspace → Project name. Press Enter to save and leave the field, or Escape to revert. (Double-clicking a terminal tab renames that tab — that is a different thing.)

Container Lifecycle

Containers use a stop/start model. When you stop a container, everything inside it is preserved — installed packages, modified files, downloaded tools. Starting it again resumes where you left off.

Reset container removes the container, its snapshot image and both of its named volumes (triple-c-home-<project-id> and triple-c-claude-config-<project-id>), then creates a fresh one from the clean base image. This is destructive: ~/.claude and ~/.claude.json go with the volumes, so your per-container OAuth login, any skills or agents you installed, your session transcripts and your scheduled tasks are all lost.

What Reset keeps: your host folders (they are bind mounts and are never touched), the project's configuration in Triple-C, and anything stored in your OS keychain — including the shared Claude authentication token. If the project uses that shared token, it re-authenticates by itself after a Reset; if it relies on claude login, you will need to log in again.

Apart from Remove project…, Reset is the only action that deletes the volumes. Stopping and starting preserves them, and so does the automatic container recreation that happens when you change a setting that affects the container — in both cases your Claude Code configuration survives.

Remove project… deletes everything Reset does, plus the project record itself and its stored credentials.

Container Progress Feedback

When starting, stopping, or resetting a container, progress is shown inline on the project row and in the Project Home header (e.g., "Creating container...", "Starting container..."), so the rest of the app stays usable. If an error occurs it is raised as a toast with the full detail behind a Details disclosure. There is no blocking progress modal.


Permission Modes

Every project has a permission mode that decides how much Claude Code does without asking. It is a segmented control on the Overview tab (and again under Config → Runtime), and it replaces the old Full Permissions on/off switch.

Mode What Claude does What Triple-C passes to claude
Plan Proposes a plan and makes no changes --permission-mode plan
Default Asks before each tool call (nothing — Claude Code's own default)
Accept Edits Auto-approves file edits; other tools still prompt --permission-mode acceptEdits
Bypass Auto-approves every tool call --dangerously-skip-permissions

New projects start in Default. Projects created before permission modes existed keep behaving the way they did: one that had Full Permissions on becomes Bypass, one that had it off becomes Default.

CAUTION: In Bypass, Claude can execute any command inside the container without asking. The container sandbox limits the blast radius, but think carefully — especially if the container has Docker socket access or reaches services on your network. The Overview tab tells you whether the in-container sandbox is also on.

When a change takes effect

  • Terminals — the mode is applied when a terminal is opened, so it affects terminals you open from then on. A Claude session that is already running keeps the permissions it started with; close the tab and open a new terminal to change it. The badge on each terminal tab shows the mode that terminal was launched with (plan, ask, edits, bypass).
  • Resumed sessions — a session resumed from the Sessions tab uses the project's current mode.
  • Scheduled tasks — these now honour the permission mode too (they previously always ran with --dangerously-skip-permissions). The mode reaches them through the container's environment, which can only change when the container is recreated, so stop and start the project for a mode change to reach the scheduler.

Scheduled tasks run headless (claude -p) and cannot answer a permission prompt. In any mode other than Bypass, a task may simply stop early when Claude Code asks for approval. Its run log records which mode it used.


Project Configuration

Open a project's Config tab in Project Home. Configuration is grouped into four sections — Workspace, Model, Access and Runtime — plus Claude instructions and Claude Code settings.

Changes save automatically when a field loses focus, and a Saved / Saving… / Failed indicator in the corner tells you what happened. Most settings can only be changed when the container is stopped; a warning chip appears at the top of the tab if it is running. (The project name and the permission mode can be changed at any time.)

Mounted Folders

Each project mounts one or more host directories into the container. The mount appears at /workspace/<mount-name> inside the container.

  • Click Browse to change the host path
  • Edit the mount name to control where it appears inside /workspace/
  • Click + Add folder to add more, or Remove to drop one (the last remaining folder cannot be removed)
  • Mount names must be unique and use only letters, numbers, dashes, underscores, and dots

SSH Keys

Specify the path to your SSH key directory (typically ~/.ssh). Keys are mounted read-only and copied into the container with correct permissions. This enables git clone via SSH inside the container.

Git Configuration

  • Git Name / Email — Sets git config user.name and user.email inside the container.
  • Git HTTPS Token — A personal access token (e.g., from GitHub) for HTTPS git operations. Stored securely in your OS keychain — never written to disk in plaintext.

Allow Container Spawning

When enabled, the host Docker socket is mounted into the container so Claude Code can create sibling containers (e.g., for running databases, test environments). This is off by default for security.

Toggling this requires stopping and restarting the container to take effect.

Mission Control

Toggle Mission Control to integrate Flight Control — an AI-first development methodology bundled with Triple-C — into the project. When enabled:

  • The bundled Flight Control files are installed into the container
  • Flight Control skills are installed to Claude Code's skill directory (~/.claude/skills/)
  • Project instructions are appended with Flight Control workflow guidance
  • The files are symlinked at /workspace/mission-control

Available skills include /mission, /flight, /leg, /agentic-workflow, /flight-debrief, /mission-debrief, /daily-briefing, and /init-project.

This setting can only be changed when the container is stopped. Toggling it triggers a container recreation on the next start.

Permission Mode

The Runtime section repeats the permission mode control from the Overview tab — see Permission Modes for what each mode does and when a change takes effect.

Sandbox Mode

Toggles Claude Code's in-container bubblewrap isolation. The Overview tab shows the current state next to the permission mode, because the two together decide how contained a Bypass-mode session really is.

Environment Variables

Add key-value pairs under Access → Environment variables; they are injected into the container. Per-project variables override global variables with the same key.

Reserved prefixes (ANTHROPIC_, AWS_, GIT_, HOST_, TRIPLE_C_) are filtered out to prevent conflicts, along with the exact names Triple-C manages itself: CLAUDE_INSTRUCTIONS, CLAUDE_CODE_SETTINGS_JSON, CLAUDE_CODE_OAUTH_TOKEN, MISSION_CONTROL_ENABLED, TRIPLE_C_PERMISSION_MODE and MCP_SERVERS_JSON. Other CLAUDE_CODE_* variables are allowed, so you can set Claude Code feature flags directly (e.g., CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1).

Port Mappings

Under Access → Port mappings, map host ports to container ports. This is useful when Claude Code starts a web server or other service inside the container and you want to access it from your host browser.

Each mapping specifies:

  • Host Port — The port on your machine (1-65535)
  • Container Port — The port inside the container (1-65535)
  • Protocol — TCP (default) or UDP

Claude Instructions

The Claude instructions editor at the bottom of the Config tab holds per-project instructions for Claude Code. These are written to ~/.claude/CLAUDE.md inside the container and provide project-specific context. If you also have global instructions (in Settings), the global instructions come first, followed by the per-project instructions.

Claude Code Settings

The Claude Code settings editor, also at the bottom of the Config tab, configures Claude Code CLI behavior for this project. These settings control how Claude Code operates inside the container:

Setting What It Does
TUI Mode Set to Fullscreen for flicker-free alt-screen rendering (uses CLAUDE_CODE_NO_FLICKER=1)
Effort Level Controls reasoning depth: Low (fast, less thorough), Medium, High (deep reasoning)
Focus Mode Collapses tool output to one-line summaries, showing only the prompt and final response
Thinking Summaries Shows Claude's thinking process as summaries during responses
Session Recap Provides context when returning to a session after being away
Auto-Scroll Disabled Disables auto-scroll when in fullscreen TUI mode
Env Scrub Strips credentials from subprocess environments for security
Prompt Caching (1h) Enables 1-hour prompt cache TTL instead of the default 5 minutes

Per-project settings override global defaults set in Settings. If all settings are at their defaults, no configuration is injected.

These settings map to Claude Code environment variables and ~/.claude/settings.json entries. Changes require stopping and restarting the container to take effect.

MCP Servers

Triple-C no longer manages MCP servers itself. Configure them with Claude Code's own tooling from a terminal inside the container:

  • claude mcp add — register a server
  • claude mcp list — show configured servers
  • claude mcp remove — delete a server
  • /mcp — slash command inside a Claude Code session for MCP status and authentication
  • A project-level .mcp.json in /workspace — checked into your repo and shared with anyone who opens the project

Your MCP configuration persists across container stop/start because ~/.claude.json and ~/.claude live on named Docker volumes. A Reset wipes them, so you would need to re-add your servers afterwards.


Shared Claude Authentication

Instead of running claude login separately in every container, you can authenticate once and share the result across projects. Claude Code's claude setup-token mints a long-lived token (roughly a year) that Triple-C stores in your OS keychain and injects into containers as CLAUDE_CODE_OAUTH_TOKEN.

This lives in the sidebar under Settings → Claude Authentication.

Signing in

  1. Start at least one project whose container is running — the flow borrows that container as a place to run the CLI. The token it produces is global, not tied to that project.
  2. Start the sign-in. Triple-C runs claude setup-token inside the container and streams its output.
  3. Claude Code prints an authorization URL. Open it, sign in, and Anthropic's page gives you a code to copy — this flow finishes on an Anthropic-hosted page, not a local callback.
  4. Paste the code back into Triple-C. The token is captured and written straight to the keychain.

Only one sign-in can run at a time, and the whole flow times out after 15 minutes. A long-lived token requires a Claude subscription; without one, setup-token finishes without printing a token and nothing is stored.

How the token is used

  • It is injected only into projects whose backend is Anthropic — it means nothing to Bedrock, Ollama, llama.cpp or an OpenAI-compatible gateway.
  • Each project can opt out under Config → Model ("Use the shared Claude token"). Projects are opted in by default, so a single sign-in covers your whole fleet; opt a project out if you want it pinned to its own claude login identity.
  • CLAUDE_CODE_OAUTH_TOKEN is reserved — you cannot set it yourself as a custom environment variable, because a hand-set value would silently outrank the stored token.
  • A container picks the token up when it is next started: acquiring, re-acquiring, revoking or opting out changes an internal marker that triggers a container recreation on the next start. Restart your Anthropic-backend containers after signing in.

Revoking

Revoking deletes the token from your keychain. Containers keep the value they were given until each is next started, at which point the same recreation clears the variable.

The token is never shown in the app, never written to a log, and never sent to the frontend. While setup-token is running, its output is filtered so anything resembling an sk-ant- secret is masked before it reaches the screen — including a secret split across two chunks of output.


Opening URLs in Your Browser (URL Relay)

There is no browser inside the container and no screen to put one on. Any tool that tries to open a web page therefore fails, usually with something unhelpful like "Couldn't find a suitable web browser!". The URL relay fixes that: when a command inside the container asks for a browser, the URL is handed to your browser on the host.

Nothing is displayed or forwarded from the container — only the URL travels.

It is always on and needs no configuration.

What you see

A small bar appears at the top of the terminal reading "Container asked to open a URL", with the URL and an Open button. Click Open and the page loads in your normal browser, signed in as you. The prompt disappears on its own after 30 seconds if you ignore it.

Triple-C asks rather than opening pages by itself. The container is sandboxed code — some of it written by Claude a minute ago — and silently making your logged-in browser visit a URL it chose is not something to hand over automatically. One click keeps that decision yours.

Which commands benefit

Anything that opens a browser to authenticate or to show you a page:

Command What it wanted a browser for
gh auth login GitHub device / OAuth login
aws sso login AWS IAM Identity Center login
gcloud auth login Google Cloud login
az login Azure login
vercel login, netlify login, fly auth login, heroku login, wrangler login Vendor CLI logins
npm login, supabase login, doctl auth init Token / device flows
xdg-open <url> in any script Opening a page directly
python3 -m webbrowser <url> Anything using Python's webbrowser module

Under the hood the container provides a stand-in browser at /usr/local/bin/triple-c-open, installed under all the names tools look for — xdg-open, sensible-browser, www-browser, x-www-browser, gnome-open, gvfs-open, kde-open, open — and as the $BROWSER environment variable, which most of the CLIs above consult first. You can also call triple-c-open <url> yourself.

It works even when the command is run by Claude Code rather than typed by you: the relay talks to the terminal directly, not through the command's output, so being nested inside a tool call does not break it.

When no terminal is attached

The relay rides on the terminal session. If nothing is attached to the container, there is nothing to relay through:

  • Scheduled tasks (Automation tab) run from cron with no terminal at all.
  • A shell you opened with your own docker exec, outside Triple-C.

In those cases the relay does not hang or wait. It prints the URL in plain text and returns immediately:

triple-c-open: no Triple-C terminal attached — cannot reach the host browser.
triple-c-open: open this URL manually:
https://github.com/login/device?user_code=WXYZ-1234

For a scheduled task that text lands in the task log (Project Home → Automation → Logs), so you can still finish the login yourself afterwards. Practically speaking: don't expect an unattended scheduled task to complete an interactive browser login. Authenticate once from a terminal session — the credentials persist in the project's config volume — and let the scheduled runs use them.

Security

Requests coming out of the container are treated as untrusted input, because that is what they are:

  • Only http:// and https:// are ever opened. file://, javascript:, data: and every custom protocol handler your OS has registered are rejected outright. A container that could make the host open arbitrary URI schemes would have a way out of the sandbox.
  • URLs with embedded credentials (https://github.com@evil.example/) are rejected — they misrepresent which site you are about to visit.
  • Control characters, whitespace and oversized payloads are rejected before parsing, so the relay cannot be used to smuggle terminal escape sequences into the UI.
  • The URL is shown to you in its normalized form: what the prompt displays is exactly what opens.
  • Prompts are rate-limited (a handful per ten seconds, with repeats of the same URL collapsed), so a runaway loop in the container cannot bury the interface.

The relay only asks. Nothing opens without your click.


Browser Logins Inside the Container (Auth Bridge)

Some CLIs log you in by opening a browser and waiting for the browser to call back to a temporary web server they started on localhost. claude login, aws sso login and Concourse's fly login all work this way. When the CLI runs inside a container, that localhost is the container's — the browser on your host calls back into nothing and the login hangs forever.

The Auth Bridge fixes this. It is opt-in per project and off by default.

What it does

  • Every couple of seconds it looks inside the container for programs listening on the container's loopback address, and binds the same port number on your host. That is the whole trick: the redirect URL the login provider was handed resolves correctly on both sides.
  • Connections are carried into the container over the Docker API, which keeps working on Docker Desktop where container IP addresses are not reachable from the host.
  • It follows whichever address family the container program actually used. This matters in practice: Node resolves localhost to IPv6 first on Linux, so claude login frequently listens on ::1 and nothing else.
  • Ports you have already configured as port mappings are left alone. If a host port is already taken, the bridge reports a conflict and leaves it alone rather than fighting for it — it will retry on a later pass.
  • The bridge is entirely host-side, so turning it on or off never recreates the container. It stops by itself when the container stops.

Security

The host side binds loopback only127.0.0.1 and [::1], never a wildcard address. Nothing on your network can reach a bridged port. Within your own machine, though, a bridged port is reachable by any local process for as long as the in-container listener exists, and the services behind it are unauthenticated: they bound loopback precisely because they expected to be reachable from nowhere else. Only container programs that bound loopback are bridged; anything listening on all interfaces is deliberately ignored (publishing those is what port mappings are for).

Leave it off unless you need it, and it will not be running.


AWS Bedrock Configuration

To use Claude via AWS Bedrock instead of Anthropic's API, set Backend to Bedrock under Config → Model.

Authentication Methods

Method Fields Use Case
Static keys Access Key ID, Secret Access Key, Session Token (optional) Direct credentials — simplest setup
Named profile AWS Profile name Uses ~/.aws/config and ~/.aws/credentials on the host
Bearer token Bearer Token Temporary bearer token authentication

With Named profile, the SSO session is validated before Claude Code launches, so an expired session is caught at the start of a terminal rather than mid-task.

Additional Bedrock Settings

  • AWS Region — Required. The region where your Bedrock models are deployed (e.g., us-east-1).
  • Model ID — Optional. Override the default Claude model (e.g., anthropic.claude-sonnet-4-20250514-v1:0).
  • Service tier — Optional. Selects a Bedrock service tier.

Global AWS Defaults

In Settings > AWS Configuration, you can set defaults that apply to all Bedrock projects:

  • AWS Config Path — Path to your ~/.aws directory. Click Detect to auto-find it.
  • Default Profile — Select from profiles found in your AWS config.
  • Default Region — Fallback region for projects that don't specify one.

Per-project settings always override these global defaults.


Ollama Configuration

To use Claude Code with a local or remote Ollama server, set Backend to Ollama under Config → Model.

Settings

  • Base URL — The URL of your Ollama server. Defaults to http://host.docker.internal:11434, which reaches a locally running Ollama instance from inside the container. For a remote server, use its IP or hostname (e.g., http://192.168.1.100:11434).
  • Model IDRequired. The model to use (e.g., qwen3.5:27b). The model must be pulled in Ollama before use — run ollama pull <model> or use it via Ollama cloud so it is available when the container starts.
  • Background model — Optional. See Model Aliases and Background Calls. Leave blank to reuse the Model ID above.

Global defaults for all three live under Settings → Backends → Ollama Configuration and are used whenever the matching per-project field is blank.

How It Works

Ollama natively implements the Anthropic Messages API at POST /v1/messages, which is the only thing Claude Code ever sends. Triple-C sets ANTHROPIC_BASE_URL to point Claude Code at your Ollama server instead of Anthropic's API. The ANTHROPIC_AUTH_TOKEN is set to ollama (required by Claude Code but not used for actual authentication). The ANTHROPIC_DEFAULT_*_MODEL aliases are pinned to your model — see Model Aliases and Background Calls.

Note: Ollama 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.

Important: The model must already be available in Ollama before starting the container. If using a local Ollama instance, pull the model first with ollama pull <model-name>. If using Ollama's cloud service, ensure the model has been used at least once so it is cached.


llama.cpp Configuration

To use Claude Code with a local or remote llama-server (from llama.cpp), set Backend to llama.cpp under Config → Model.

llama-server implements the Anthropic Messages API natively — POST /v1/messages and POST /v1/messages/count_tokens — so Claude Code talks to it directly, with no translation layer in between.

Settings

  • Base URL — The URL of your llama-server. Defaults to http://host.docker.internal:8080; 8080 is llama-server's own default port (--port PORT | port to listen (default: 8080)). For a remote server, use its IP or hostname.
  • Model ID — The model llama-server is serving. A llama-server process serves one model, so this is mostly the id Claude Code reports — but it is also what the model aliases are pinned to, so setting it matters.
  • Background model — Optional. See Model Aliases and Background Calls. Leave blank to reuse the Model ID above.

Global defaults for all three live under Settings → Backends → llama.cpp Configuration and are used whenever the matching per-project field is blank.

Starting llama-server

llama-server -m /path/to/model.gguf --port 8080 --host 0.0.0.0

--host 0.0.0.0 matters: llama-server binds 127.0.0.1 by default, which the container cannot reach through host.docker.internal.

How It Works

Triple-C sets ANTHROPIC_BASE_URL to your llama-server, and ANTHROPIC_AUTH_TOKEN to the placeholder llama.cpp. llama-server only checks the Authorization header when it was started with --api-key (default: none), so the value is ignored in the usual case — but Claude Code requires some credential to be present, so one is always sent.

Note: llama.cpp 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.


OpenAI Compatible Configuration

To route Claude Code through a gateway, set Backend to OpenAI Compatible under Config → Model.

The name is misleading, and the distinction matters. Claude Code only ever sends POST /v1/messages?beta=true in Anthropic Messages format to ANTHROPIC_BASE_URL. It never calls OpenAI's /v1/chat/completions. So this backend requires an endpoint that implements the Anthropic Messages APILiteLLM does, and works. A server that exposes only an OpenAI-compatible API (plain vLLM, text-generation-inference, LocalAI, OpenRouter, …) will not work here; put an Anthropic-shaped gateway such as LiteLLM in front of it. For Ollama and llama.cpp, use their own backends — both implement /v1/messages natively.

(The backend name is kept as-is so existing projects keep working.)

Settings

  • Base URL — The URL of your gateway. Defaults to http://host.docker.internal:4000, LiteLLM's default port (adjust to match your server's address and port).
  • API Key — Optional. The API key for your endpoint, if authentication is required. Stored securely in your OS keychain.
  • Model ID — Optional. Override the model to use.
  • Background model — Optional. See Model Aliases and Background Calls. Leave blank to reuse the Model ID above.

Global defaults for the base URL, model and background model live under Settings → Backends → OpenAI Compatible Configuration.

How It Works

Triple-C sets ANTHROPIC_BASE_URL to point Claude Code at your gateway. If an API key is provided, it is set as ANTHROPIC_AUTH_TOKEN.

Note: 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 when routing to non-Anthropic models through the endpoint.


Model Aliases and Background Calls

Claude Code has four model aliases — opus, sonnet, haiku and fable. Left alone they resolve to Anthropic's model IDs. A local server has never heard of those IDs, so every call that goes through an alias fails, usually with no visible error.

The one that bites hardest is haiku: ANTHROPIC_DEFAULT_HAIKU_MODEL is documented as "Model ID that the haiku alias resolves to, also used for background functionality" — conversation titles, summaries, and other out-of-band work. If it is wrong, those quietly stop happening.

So for every backend that points at a custom endpoint — Ollama, llama.cpp and OpenAI Compatible — Triple-C sets all four:

Variable Value
ANTHROPIC_DEFAULT_OPUS_MODEL your configured Model ID
ANTHROPIC_DEFAULT_SONNET_MODEL your configured Model ID
ANTHROPIC_DEFAULT_HAIKU_MODEL your Background model, or the Model ID if that is blank
ANTHROPIC_DEFAULT_FABLE_MODEL your configured Model ID

Leaving Background model blank is the right default. A local server almost always serves one model, and pointing every alias at it is what makes background work succeed.

Set Background model only if you serve a second, smaller model you would rather spend on titles and summaries. It moves the Haiku alias alone; the other three still follow Model ID. It is available per-project (Config → Model) and globally (Settings → Backends), with the usual per-project-overrides-global rule.

Notes:

  • These variables are not set for the Anthropic or Bedrock backends. Those reach servers that genuinely host the Anthropic model IDs, so Claude Code's own defaults are correct.
  • All four names are reserved — you cannot set them yourself as custom environment variables.
  • Changing a model or a Background model recreates the container on the next start, because environment variables can only change at creation time.
  • ANTHROPIC_SMALL_FAST_MODEL, the deprecated predecessor of the Haiku variable, is not used.

Settings

Access global settings via the Settings tab in the sidebar. The panel is a set of collapsible sections: General, Claude Authentication, Backends, Container, Git / SSH, Tools and Updates.

Claude Authentication

Acquire or revoke the shared Claude authentication token — see Shared Claude Authentication.

Docker Settings

  • Docker Status — Connection status to the Docker daemon.
  • Image Source — Where to get the sandbox container image (Registry, Local Build, or Custom).
  • Pull / Build Image — Download or build the image. Progress streams in real time.
  • Refresh — Re-check Docker and image status.

Container Timezone

Set the timezone for all containers (IANA format, e.g., America/New_York, Europe/London, UTC). Auto-detected from your host on first launch. This affects scheduled task timing inside containers.

Global Claude Instructions

Instructions applied to all projects. Written to ~/.claude/CLAUDE.md in every container, before any per-project instructions.

Global Environment Variables

Environment variables applied to all project containers. Per-project variables with the same key take precedence.

Default SSH Key Directory

Path to your SSH key directory (typically ~/.ssh). This is mounted into all containers that don't have a per-project SSH path set. Per-project SSH paths take precedence.

Default Git Name / Email

Sets git user.name and git user.email inside all containers. Per-project Git Name / Email settings take precedence. This is useful so you don't have to set the same name and email on every project.

Claude Code Settings (Global Defaults)

Default Claude Code CLI settings applied to all projects. See Claude Code Settings in the Project Configuration section for a description of each setting. Per-project settings override these global defaults.

Web Terminal

Enable remote access to your project terminals from any device on the local network (tablets, phones, other computers).

  • Toggle — Click ON/OFF to start or stop the web terminal server.
  • URL — When running, shows the full URL including the access token. Click Copy URL to copy it to your clipboard, then open it in a browser on your tablet or phone.
  • Token — An access token is auto-generated on first enable. Click Copy to copy the token, or Regenerate to create a new one (this disconnects existing web sessions).
  • Port — Defaults to 7681. Configurable in settings.json if needed.

The web terminal server auto-starts on app launch if it was previously enabled, and stops when the app closes.

Updates

  • Current Version — The installed version of Triple-C.
  • Auto-check — Toggle automatic update checks (every 24 hours).
  • Check now — Manually check for updates.

When an update is available, a pulsing Update button appears in the top bar. Click it to see release notes and download links.


Web Terminal (Remote Access)

The web terminal lets you access your running project terminals from a tablet, phone, or any other device on the local network — no app installation required, just a web browser.

Setup

  1. Go to Settings in the sidebar.
  2. Find the Web Terminal section and click the toggle to ON.
  3. A URL appears (e.g., http://192.168.1.100:7681?token=...). Click Copy URL.
  4. Open the URL in a browser on your tablet or other device.

Using the Web Terminal

The web terminal UI mirrors the desktop app's terminal experience:

  • Project picker — Select a running project from the dropdown at the top.
  • Claude / Bash buttons — Open a new Claude Code or bash session for the selected project.
  • Tab bar — Switch between multiple open sessions. Click the x on a tab to close it.
  • Input bar — A text input at the bottom optimized for mobile/tablet keyboards. Characters are sent immediately without waiting for autocomplete. Helper buttons for Enter, Tab, and ^C (Ctrl+C) are provided for keys that are awkward on virtual keyboards.
  • Scroll to bottom — A floating arrow button appears when you scroll up, letting you jump back to the latest output.

Security

  • Access requires a token in the URL query string. Without the correct token, connections are rejected.
  • The token is auto-generated (32 bytes, base64url-encoded) and can be regenerated at any time from Settings.
  • The server only listens on port 7681 (configurable) — make sure this port is not exposed to the public internet.
  • All sessions opened from a browser tab are automatically cleaned up when the tab is closed or the WebSocket disconnects.

Tips

  • Bookmark the URL on your tablet for quick access.
  • The web terminal works best in landscape orientation on tablets.
  • If the connection drops (e.g., Wi-Fi interruption), the web terminal auto-reconnects after 2 seconds.
  • Regenerating the token invalidates all existing browser sessions — you'll need to update bookmarks with the new URL.

Terminal Features

Multiple Sessions

You can open multiple terminal sessions (even for the same project). Each session gets its own tab in the main tab strip, alongside any open Project Home tabs. Click a tab to switch, or click the × on a tab to close it. Tabs show the project name (or a custom session name if you set one), with a "(bash)" suffix for shell sessions and a badge for the permission mode the session was launched with.

Right-click a terminal tab for Rename tab, Reset name, Open project home and Close tab; double-click it to rename inline.

Bash Shell Sessions

In addition to Claude Code terminals, you can open a plain bash login shell in any running container by clicking the Shell button. This is useful for manual inspection, package installation, debugging, or running commands that don't need Claude Code.

URL Detection

When Claude Code prints a long URL (e.g., during claude login), Triple-C detects it and shows a toast notification at the top of the terminal with an Open button. Clicking it opens the URL in your default browser. The toast auto-dismisses after 30 seconds.

Shorter URLs in terminal output are also clickable directly.

Copying and Pasting

Use Ctrl+Shift+C (or Cmd+C on macOS) to copy selected text from the terminal, and Ctrl+Shift+V (or Cmd+V on macOS) to paste. This follows standard terminal emulator conventions since Ctrl+C is reserved for sending SIGINT.

Clipboard Support (OSC 52)

Programs inside the container can copy text to your host clipboard. When a container program uses xclip, xsel, or pbcopy, the text is transparently forwarded to your host clipboard via OSC 52 escape sequences. No additional configuration is required — this works out of the box.

Image Paste

You can paste images from your clipboard into the terminal (Ctrl+V / Cmd+V). The image is uploaded to the container as /tmp/clipboard_<timestamp>.png and the file path is injected into the terminal input so Claude Code can reference it. A toast notification confirms the upload.

Jump to Current

When you scroll up in the terminal to review previous output, a Jump to Current button appears in the bottom-right corner. Click it to scroll back to the latest output.

Files

The Files tab of Project Home browses inside a running container. You can:

  • Browse the container filesystem, starting at /workspace, with breadcrumb navigation
  • Download any file to your host machine via the Download button on each file entry
  • Upload file from your host into the current container directory
  • Refresh the directory listing at any time

The listing shows file names, sizes, and modification dates.

Terminal Rendering

The terminal uses WebGL for hardware-accelerated rendering of the active tab. Inactive tabs fall back to canvas rendering to conserve GPU resources. The terminal automatically resizes when you resize the window.


Automation & Scheduled Tasks

Each container can run Claude Code on a schedule — recurring or one-time — through a small scheduler called triple-c-scheduler that lives inside the image. Tasks run as separate, headless Claude Code invocations (claude -p "<your prompt>") driven by cron.

The Automation tab in Project Home is the place to watch and control them; tasks are created from inside the container with the triple-c-scheduler CLI.

The Automation Tab

With the container running, the Automation tab lists every task the scheduler knows about. For each one you get its name, whether it is recurring or one-time, its cron expression or scheduled time, and when it last ran. For each task you can:

Control What it does
Toggle Enable or disable the task without deleting it
Run now Trigger the task immediately, outside its schedule
Log Show the tail of that task's run log (last 200 lines)
Remove Delete the task, after a confirmation

Refresh re-reads everything from the container.

When tasks finish they leave notifications. If any are waiting, a panel appears at the top of the tab listing each one with its task name, whether it succeeded or failed, how long ago it ran and a summary. Clear all dismisses them. The Overview tab also shows a notification count and the next few scheduled tasks.

Permission mode

Scheduled runs use the project's permission mode — they no longer always run with --dangerously-skip-permissions. Because the mode travels into the container as an environment variable, stop and start the project after changing it for the scheduler to see the change. Remember that a headless run cannot answer a permission prompt, so in any mode other than Bypass a task may stop early when Claude Code asks for approval; the run log records the mode that was used.

Creating Tasks (In the Container)

There is no "add task" form in the app. Create tasks from a terminal in the container — either type the commands yourself in a Shell session, or just ask Claude to do it.

Create a Recurring Task

triple-c-scheduler add --name "daily-review" --schedule "0 9 * * *" --prompt "Review open issues and summarize"

Create a One-Time Task

triple-c-scheduler add --name "migrate-db" --at "2026-03-05 14:00" --prompt "Run database migrations"

One-time tasks automatically remove themselves after execution.

Manage Tasks From the CLI

The CLI still works, and does the same things the Automation tab does:

triple-c-scheduler list                    # List all tasks
triple-c-scheduler enable --id abc123      # Enable a task
triple-c-scheduler disable --id abc123     # Disable a task
triple-c-scheduler remove --id abc123      # Delete a task
triple-c-scheduler run --id abc123         # Trigger a task immediately
triple-c-scheduler logs --id abc123        # View logs for a task
triple-c-scheduler logs --tail 20          # View last 20 log entries (all tasks)
triple-c-scheduler notifications           # View completion notifications
triple-c-scheduler notifications --clear   # Clear notifications

Cron Schedule Format

Standard 5-field cron: minute hour day-of-month month day-of-week

Example Meaning
*/30 * * * * Every 30 minutes
0 9 * * 1-5 9:00 AM on weekdays
0 */2 * * * Every 2 hours
0 0 1 * * Midnight on the 1st of each month

Working Directory

By default, tasks run in /workspace. Use --working-dir to specify a different directory:

triple-c-scheduler add --name "test" --schedule "0 */6 * * *" --prompt "Run tests" --working-dir /workspace/my-project

Scheduled tasks live on the project's config volume, so a Reset deletes them along with everything else on that volume.


Keyboard Shortcuts

Application

Shortcut Action
Ctrl+T Open a new Claude terminal for the current project (nothing happens unless its container is running)
Ctrl+Shift+W Close the active tab
Ctrl+Tab Switch to the next tab
Ctrl+Shift+Tab Switch to the previous tab
Ctrl+1Ctrl+9 Jump to the first through ninth tab

Why Ctrl+Shift+W and not Ctrl+W? Ctrl+W is readline's kill-word — it deletes the word before the cursor, and it is used constantly in the terminal this app is built around. Binding it to "close tab" would make the shell unusable, so Triple-C deliberately leaves Ctrl+W alone.

In the Terminal

Shortcut Action
Ctrl+Shift+C Copy the selection, with trailing whitespace trimmed
Ctrl+Shift+Alt+C Copy the selection exactly as-is
Ctrl+Shift+V Paste
Ctrl+V Paste an image from the clipboard into the container
Ctrl+Shift+M Toggle speech-to-text recording (when enabled)

Everything else goes straight through to the program running in the container.


What's Inside the Container

The sandbox container (Ubuntu 24.04) comes pre-installed with:

Tool Version Purpose
Claude Code Latest AI coding assistant (the tool being sandboxed)
Node.js 22 LTS JavaScript/TypeScript development
pnpm Latest Fast Node.js package manager
Python 3.12 Python development
uv Latest Fast Python package manager
ruff Latest Python linter/formatter
Rust Stable Rust development (via rustup)
Docker CLI Latest Container management (when spawning is enabled)
git Latest Version control
GitHub CLI (gh) Latest GitHub integration
AWS CLI v2 AWS services and Bedrock
ripgrep Latest Fast code search
build-essential C/C++ compiler toolchain
openssh-client SSH for git and remote access

The container also includes clipboard shims (xclip, xsel, pbcopy) that forward copy operations to the host via OSC 52, a browser shim (triple-c-open, installed as xdg-open, sensible-browser, www-browser, x-www-browser and $BROWSER) that relays URLs to your host browser — see Opening URLs in Your Browser — and an audio shim (rec, arecord) for future voice mode support.

It also ships the system libraries a browser needs to run (libnss3, libgbm1, libatk*, libasound2t64, libcups2t64, libpango, libdrm2, fonts, and the rest of the set Playwright asks for). So npx playwright install chromium gives you a browser that actually starts. Before these were baked in, that download succeeded and the browser then died with "Host system is missing dependencies: libnss3.so", which is why sudo apt install google-chrome-stable looked like the cure — apt was quietly installing the same libraries as Chrome's own dependencies.

The browsers themselves are not pre-installed — they are hundreds of megabytes and tied to the Playwright version you use. Install one with the Browser tab's setup buttons, or npx playwright install chromium in a terminal. They land in ~/.cache/ms-playwright, which is on the home volume, so a browser survives container recreation and base-image migration and is only lost on a project Reset.

If your project's container was created from an older base image, it won't have the libraries — the Browser tab's install action detects that and installs them for you first, and says so while it does. That install lives in the container's writable layer, so it is undone by a Reset and by a base-image migration; migrating the project onto the current base image is what picks the libraries up for good.

You can install additional tools at runtime with sudo apt install, pip install, npm install -g, etc. Installed packages persist across container stops (but not across resets).


Claude Code Tips

These features are built into Claude Code and work inside Triple-C containers with no extra configuration:

Feature How to Use
Focus Mode Run /focus or press Ctrl+O in the terminal to toggle collapsed tool output
Session Recap Run /recap to get a summary of what happened in the current session
Session Color Run /color red (or any color) to color-code your terminal prompt bar
Recurring Tasks Run /loop 5m check the deploy to repeat a prompt every 5 minutes
Interactive Lessons Run /powerup to learn Claude Code features with animated demos
Team Onboarding Run /team-onboarding to generate a teammate ramp-up guide
Bedrock Setup Select "3rd-party platform" on the login screen for an interactive Bedrock setup wizard
Vertex AI Setup Select "3rd-party platform" on the login screen for an interactive Vertex AI setup wizard
MCP Elicitation MCP servers can now request structured user input mid-task — works automatically

Troubleshooting

Docker is "Not Available"

  • Is Docker running? Start Docker Desktop or the Docker daemon (sudo systemctl start docker).
  • Permissions? On Linux, ensure your user is in the docker group or the socket is accessible.
  • Custom socket path? If your Docker socket is not at the default location, set it in Settings. The app expects /var/run/docker.sock on Linux/macOS or //./pipe/docker_engine on Windows.

Image is "Not Found"

  • Click Pull Image or Build Image in Settings > Docker.
  • If pulling fails, check your network connection and whether you can reach the registry.
  • Try switching to Local Build as an alternative.

Container Won't Start

  • Check that the Docker image is "Ready" in Settings.
  • Verify that the mounted folder paths exist on your host.
  • Read the error toast — the full message is behind its Details disclosure.

OAuth Login URL Not Opening

  • Triple-C detects long URLs printed by claude login and shows a toast with an Open button.
  • If the toast doesn't appear, try scrolling up in the terminal — the URL may have already been printed.
  • You can also manually copy the URL from the terminal output and paste it into your browser.

"Couldn't find a suitable web browser" / a Command Won't Open a Page

The URL relay should catch this. If a command still complains, check from a terminal session in that project:

echo "$BROWSER"                       # /usr/local/bin/triple-c-open
triple-c-open https://example.com/    # should raise the prompt in the terminal

If $BROWSER is empty or triple-c-open is missing, the container is running an older image. Rebuild it (Project Home → Reset, or pull/build the image again from Settings) — the relay is part of the container image, not something the app can inject into a running container.

If you see "no Triple-C terminal attached", the command is running somewhere with no terminal — a scheduled task, or a shell you opened with your own docker exec. The URL is printed instead; copy it into your browser. See When no terminal is attached.

If the prompt says the URL was refused, the command asked for a scheme the relay will not open on your machine (anything that isn't http/https).

A Browser Login Never Completes

You opened the URL, signed in successfully, and the CLI in the terminal is still waiting. The callback from your browser is landing on your host's localhost while the CLI is listening on the container's. Enable the Auth Bridge for that project and try again.

For Claude specifically, the simpler answer is usually Shared Claude Authentication, which finishes on an Anthropic-hosted page and needs no callback at all.

A Scheduled Task Stopped Part-Way Through

Scheduled tasks run headless and cannot answer a permission prompt. If the project is not in Bypass mode, a task will stop when Claude Code asks for approval. Check the task's Log in the Automation tab — it records the permission mode the run used.

A Permission Mode Change Didn't Apply

  • In a terminal: the mode is set when the terminal opens. Close the tab and open a new one.
  • For scheduled tasks: the mode reaches the scheduler through the container's environment. Stop the project and start it again.

File Permission Issues

  • Triple-C automatically remaps the container user's UID/GID to match your host user, so files created inside the container should have the correct ownership on your host.
  • If you see permission errors, try resetting the container: stop it, then choose Reset container from the menu in the Project Home header. Note that this wipes ~/.claude.

Settings Won't Save

  • Most project settings can only be changed when the container is stopped. Stop the container first, make your changes, then start it again.
  • Some changes (like toggling Docker access, Mission Control, or changing mounted folders) trigger an automatic container recreation on the next start.

"Failed to install Anthropic marketplace" Error

If Claude Code shows "Failed to install Anthropic marketplace - Will retry on next startup" repeatedly, the marketplace metadata in ~/.claude.json may be corrupted. To fix this, open a Shell session in the project and run:

cp ~/.claude.json ~/.claude.json.bak && jq 'with_entries(select(.key | startswith("officialMarketplace") | not))' ~/.claude.json.bak > ~/.claude.json

This backs up your config and removes the corrupted marketplace entries. Claude Code will re-download them cleanly on the next startup.