A self-hosted web dashboard for spawning and managing Claude Code remote-control
bridges into any project directory on a remote host — then attach to them from
claude.ai/code or the Claude mobile app. No SSH session required.
Anthropic's first-party tooling assumes terminal access on the host to spawn a
bridge in a given project directory. Clauster fills that gap: a browser-based dispatcher of
claude remote-control instances on a remote machine (NAS, homelab box). You pick
a project, start a bridge, and attach to it from claude.ai/code or the mobile app
— no SSH session required.
Status: pre-1.0, in active development. Loopback-only by default; password and reverse-proxy auth are available for networked deployments (see Auth & networking). No telemetry, ever.
Everything below is implemented and shipping. Items marked (opt-in) are gated
behind a config flag and off by default — the flag is named inline so you can find
it in clauster.yml.example.
- Project discovery — one card per directory under
projects_root, with git /CLAUDE.md/ trust badges. - Bridge lifecycle — start / stop / resume bridges; live status
(Starting / Running / Stopped / Crashed / Error). A bridge that launches but
never registers an environment is reported honestly as
Errorafter a grace window, not a phantomRunning. - Spawn controls — pick the spawn mode (same-dir / worktree / session),
permission mode, and resume mode (standard / pty true-resume, POSIX) per launch;
claude.resume_modeis the pre-selected default.bypassPermissionsis double-gated: a per-project config ceiling (projects.<name>.allow_bypass_permissions) and a type-the-project-name confirm in the UI. - Open session in Claude — a deep link to the primary session plus a scannable QR code that opens it in the Claude app (claude.ai/code or mobile), attached to the running bridge.
- External session surfacing — sessions you started from a terminal or Desktop (not via Clauster) are discovered and shown with a distinct indicator.
- Create / clone projects — make a new project or clone a git URL, with SSRF guards, transport lockdown, a size cap, and a "code runs on start" warning for cloned repos. Clones stream live progress over a WebSocket and never auto-spawn (they land discovered-but-stopped).
- Live log tail — the bridge debug log streamed over a WebSocket, ANSI-stripped
and ID-redacted (
env_/session_/cse_IDs, bare UUIDs, and secret-shaped tokens — API keys, bearer headers). Redaction is hybrid by default (verbatim on disk, redacted over the wire);logs.redact_session_urlredacts on disk too. - CLAUDE.md editor — view/edit a project's
CLAUDE.mdfrom the dashboard (size-capped, lost-update-guarded, trust-gated, audit-logged). - Per-project cost badge — approximate USD + token totals rolled up from a
project's session transcripts. Token counts are exact (read from the transcript
usage); the dollar figure is a ballpark — a hand-maintained USD price table (usage.py, as of 2026-05) that drifts as pricing changes, with unpriced models counting as 0. Hide it withusage.show_cost: false(privacy / screen-share).
- Workspace trust — starting a bridge in an untrusted directory prompts a just-in-time "trust the files in this folder?" confirm (an explicit checkbox) that writes the Claude workspace-trust flag and then spawns; trusted directories show a green shield by the project name and start with no prompt.
- Auto-enable remote control — before the first spawn, Clauster marks remote
control as acknowledged in the runtime user's
~/.claude.jsonso a detached-stdin bridge isn't stuck on the one-time interactive "Enable Remote Control?" prompt. On by default (claude.auto_enable_remote_control); set false to manage it yourself.
- Conversation recap on restart (opt-in) —
claude remote-controlrestarts into a fresh, empty context, so a restarted bridge "forgets" the prior conversation. Withclaude.resume_recapenabled, Clauster installs aSessionStarthook in the runtime user's Claude settings that recaps the most recent prior transcript for that directory back into the new session. - Native true-resume / "PTY mode" (opt-in, POSIX) —
claude.resume_mode: ptyruns theclaude --remote-controlflag form under a PTY keeper sidecar, which genuinely restores prior conversation context on Resume (--continue) rather than recapping it. The keeper outlives a Clauster restart and is stopped by signal. Single-session (vs. the default multi-session server). Backend shipped; the dashboard mode indicator + cross-restart UI rediscovery are in progress — see Roadmap. - Ghost-environment reaper — find and archive/delete the server-side bridge
environments that outlive their bridge and clutter the claude.ai/code "New session"
selector. The CLI (
clauster reap-environments) is always available; the dashboard UI is opt-in (reaper.ui_enabled) because it exposes a destructive first-party API in the browser. Archive is reversible; force-delete requires typingDELETE.
uv sync --extra dev
cp clauster.yml.example clauster.yml # edit projects_root
uv run clausterThen open http://127.0.0.1:7621. claude must be on your PATH (Clauster spawns
it; it isn't vendored).
With the server running (above) and claude on your PATH, spawning your first
bridge is a handful of clicks — no terminal needed once it's started:
- Point Clauster at your code. Set
projects_rootinclauster.ymlto a directory whose subfolders are projects (e.g.~/code); each child directory becomes a card. - Sanity-check the host (optional).
clauster doctorconfirmsclaudeis found and new enough and thatprojects_root/ the state dir are usable — fix any ✗ before spawning. - Open the dashboard at http://127.0.0.1:7621. You'll see one card per project.
- Start a bridge. On a project's card, click Start. Clauster launches
claude remote-controlin that directory and the card flips to Running with a live status badge. (Pick a spawn / permission mode first if you like — the defaults are safe.) - Attach from anywhere. Use the card's Open session in Claude link — or scan its
QR code — to pick the bridge up in
claude.ai/codeor the Claude mobile app. No SSH session. - Stop or resume. Stop signals the bridge; Resume relaunches it (with
claude.resume_recaporresume_mode: ptyit can carry the prior conversation forward — see Opt-in extras). A resumable bridge also offers Start new session for a deliberate fresh start.
Exposing this beyond loopback (e.g. on your LAN)? Read Auth & networking first — a non-loopback bind requires authentication.
Multi-arch images (linux/amd64, linux/arm64) are published to GHCR on each release.
The image binds 0.0.0.0, so it requires enforced auth to start. First generate a
password hash — this runs clauster inside the image, so you don't need it on the host:
docker run --rm -it ghcr.io/schubydoo/clauster:latest clauster hash-passwordCopy the printed $argon2id$… hash, then start the server with auth enabled:
docker run -d --name clauster \
-p 7621:7621 \
-e PUID=1000 -e PGID=1000 \
-e CLAUSTER_AUTH_ENABLED=true \
-e CLAUSTER_AUTH_PASSWORD_REQUIRED=true \
-e 'CLAUSTER_AUTH_PASSWORD_HASH=$argon2id$v=19$...' \
-v /path/to/config:/config \
-v /path/to/projects:/projects \
ghcr.io/schubydoo/clauster:latest- The image binds
0.0.0.0, so it won't start without enforced auth — setCLAUSTER_AUTH_ENABLED=trueandCLAUSTER_AUTH_PASSWORD_REQUIRED=trueand aCLAUSTER_AUTH_PASSWORD_HASH(or configure reverse-proxy trust in/config/clauster.yml), or the container exits on start. Single-quote the hash env value — the argon2 hash contains$that your shell would otherwise expand. /configholdsclauster.yml+ state;/projectsis yourprojects_root.PUID/PGIDremap the runtime user to own bind-mounts.claudeis not baked in — tell Clauster where it is one of two ways: mount the binary somewhere on the containerPATH(the defaultclaude.binary: claudeis resolved viaPATH), or setCLAUSTER_CLAUDE_BINARY=/abs/path/to/claude(a.k.a.claude.binary) to an absolute path you've mounted anywhere. Either way, also mount the runtime user's~/.claudecredentials — or build a derived image that installsclaude.- Logs are JSON by default (
CLAUSTER_LOG_FORMAT); health is at/healthz. Images are cosign-signed with build provenance + SBOM attestations.
A ready-to-edit compose.yaml is included:
# 1. generate a password hash (runs inside the image)
docker compose run --rm clauster clauster hash-password
# 2. export it single-quoted, then edit the projects/claude volumes in compose.yaml
export CLAUSTER_AUTH_PASSWORD_HASH='$argon2id$v=19$...'
# 3. start (the image's HEALTHCHECK is inherited)
docker compose up -dLoopback (127.0.0.1) needs no auth. Binding to a non-loopback address is refused
unless authentication is actually enforced — set auth.enabled: true (the master
switch) together with either password login (auth.password_required + a hash from
clauster hash-password) or reverse-proxy trust (peer-IP allowlist + HMAC header) —
or, to opt out on a trusted LAN, auth.allow_unauthenticated_network. Sessions
are signed cookies with server-side revocation ("log out everywhere"); WebSocket
connections are authenticated before accept and origin-checked.
All settings live in clauster.yml — see
clauster.yml.example for the full, commented schema. Any
scalar key is overridable by an environment variable of the form
CLAUSTER_<UPPER_SNAKE_PATH>. The schema is additive-only — old configs always
validate against newer versions.
| Common flag | Default | What it does |
|---|---|---|
host / port |
127.0.0.1 / 7621 |
bind address (non-loopback needs auth) |
projects_root |
— | directory whose children become project cards |
auth.enabled |
false |
master auth switch — must be on for password / proxy auth to apply |
auth.password_required |
false |
require login (clauster hash-password for the hash) |
claude.resume_recap |
false |
recap the prior transcript into a restarted bridge |
claude.resume_mode |
standard |
pty = native true-resume on Resume (POSIX); default for new bridges only — a bridge keeps the mode it launched with |
reaper.ui_enabled |
false |
expose the ghost-environment reaper in the dashboard |
usage.show_cost |
true |
show the per-project cost badge; false hides it (and skips the usage fetch) for privacy |
logs.redact_session_url |
false |
redact the session URL on disk too, not just over WS |
clauster run # start the server (default)
clauster hash-password # generate an argon2id hash for auth
clauster doctor # diagnose config / environment
clauster backup | restore | migrate
clauster install-service {systemd|launchd|windows}
clauster reap-environments # reap ghost bridge environments (dry-run by default)
clauster usage <transcript> # token + approximate cost for a session transcript
Planned work, roughly in priority order — the public-facing companion to the in-repo
scratch/TODO.md.
- PTY mode — finish the slice — the backend for native true-resume ships today
(
claude.resume_mode: pty); next is the dashboard mode indicator and cross-restart UI rediscovery of PTY bridges (the keeper process already survives a restart). - Public API — promote the existing
/api/*routes to a documented, versioned, auth-gated contract (OpenAPI surface, API tokens distinct from the session cookie) so third parties can build their own dashboards. - Session naming — predictable/branded session display names instead of the random adjective-noun defaults; list active/resumable sessions in the UI.
- v0.3 — multi-user — per-user accounts (OIDC via Authentik / Pocket-ID /
Keycloak / Zitadel), a real persistence layer (SQLAlchemy + Alembic), and GDPR
controller tooling (
clauster user export/delete). - v0.3 — operability — crash notifications (Apprise / webhooks), a
/metricsPrometheus endpoint, a homepage-dashboard widget endpoint, and i18n string extraction. - Wiki — a proper docs site (setup, deployment recipes, config reference, security model) beyond this README.
Python 3.11+ · FastAPI · Alpine.js + Jinja2 + Tabler · uv · pydantic. Developed
and CI-gated on Linux; macOS / Windows are in the test matrix. Apache-2.0 licensed.



