Skip to main content

Self-hosted web UI for spawning and managing Claude Code remote-control bridges on a remote host.

Project description

Clauster

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.

CI Lint codecov Reviewed by CodeRabbit OpenSSF Scorecard OpenSSF Best Practices

PyPI Python versions License: Apache-2.0 GHCR Ruff pre-commit

Clauster dashboard

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.

Dashboard, light theme
Dark / light — theme toggle persists across reloads
Create or clone a project
Create or clone — SSRF-guarded, cloned code runs only on Start
Password login
Password login — for non-loopback / networked deploys
Every action is reactive — cards insert, badges flip, and clone progress
streams without a full-page reload. Self-hosted assets; no CDN, no trackers.

Features

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.

Projects & bridges

  • 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 Error after a grace window, not a phantom Running.
  • Spawn controls — pick the spawn mode (same-dir / worktree / session), permission mode, and resume mode (standard / pty true-resume, POSIX) per launch; claude.resume_mode is the pre-selected default. bypassPermissions is 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).

Visibility & editing

  • 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_url redacts on disk too.
  • CLAUDE.md editor — view/edit a project's CLAUDE.md from 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 with usage.show_cost: false (privacy / screen-share).

Safety

  • 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.json so 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.

Opt-in extras

  • Conversation recap on restart (opt-in)claude remote-control restarts into a fresh, empty context, so a restarted bridge "forgets" the prior conversation. With claude.resume_recap enabled, Clauster installs a SessionStart hook 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: pty runs the claude --remote-control flag 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 typing DELETE.

Quick start (dev)

uv sync --extra dev
cp clauster.yml.example clauster.yml    # edit projects_root
uv run clauster

Then open http://127.0.0.1:7621. claude must be on your PATH (Clauster spawns it; it isn't vendored).

First bridge in 60 seconds

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:

  1. Point Clauster at your code. Set projects_root in clauster.yml to a directory whose subfolders are projects (e.g. ~/code); each child directory becomes a card.
  2. Sanity-check the host (optional). clauster doctor confirms claude is found and new enough and that projects_root / the state dir are usable — fix any ✗ before spawning.
  3. Open the dashboard at http://127.0.0.1:7621. You'll see one card per project.
  4. Start a bridge. On a project's card, click Start. Clauster launches claude remote-control in 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.)
  5. 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/code or the Claude mobile app. No SSH session.
  6. Stop or resume. Stop signals the bridge; Resume relaunches it (with claude.resume_recap or resume_mode: pty it 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.

Docker

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

Copy 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 — set CLAUSTER_AUTH_ENABLED=true and CLAUSTER_AUTH_PASSWORD_REQUIRED=true and a CLAUSTER_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.
  • /config holds clauster.yml + state; /projects is your projects_root. PUID/PGID remap the runtime user to own bind-mounts.
  • claude is not baked in — tell Clauster where it is one of two ways: mount the binary somewhere on the container PATH (the default claude.binary: claude is resolved via PATH), or set CLAUSTER_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 ~/.claude credentials — or build a derived image that installs claude.
  • Logs are JSON by default (CLAUSTER_LOG_FORMAT); health is at /healthz. Images are cosign-signed with build provenance + SBOM attestations.

Docker Compose

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

Auth & networking

Loopback (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.

Configuration

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

CLI

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

Roadmap

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 /metrics Prometheus 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.

Stack

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.

License

Apache License 2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

clauster-0.8.0.tar.gz (755.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

clauster-0.8.0-py3-none-any.whl (258.6 kB view details)

Uploaded Python 3

File details

Details for the file clauster-0.8.0.tar.gz.

File metadata

  • Download URL: clauster-0.8.0.tar.gz
  • Upload date:
  • Size: 755.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for clauster-0.8.0.tar.gz
Algorithm Hash digest
SHA256 7f8eb42d810223f2e3bf42a863a375379f719a9fe8d896c95576afc63fa76648
MD5 18e7527296ad786685ffb81970a4f966
BLAKE2b-256 416e7cbc695d3dcd67992957aea515abfa7204b57902d226ece1794fe9b71eef

See more details on using hashes here.

Provenance

The following attestation bundles were made for clauster-0.8.0.tar.gz:

Publisher: release.yml on schubydoo/clauster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file clauster-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: clauster-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 258.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for clauster-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5bc81da0d4c615e34da39f9b759006af2c6144b7b96c45164a2c32e9ed0f7ee0
MD5 696b399420fe144d7c2fec226c1b6925
BLAKE2b-256 529f8066cdaaa3c49db27f2a9bef7d4f840e929f4ffc6d71841689a1bb45e782

See more details on using hashes here.

Provenance

The following attestation bundles were made for clauster-0.8.0-py3-none-any.whl:

Publisher: release.yml on schubydoo/clauster

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page