Skip to content

schubydoo/clauster

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

222 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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.

About

Your homelab's Claude Code launchpad. Self-hosted web UI that spawns & manages remote-control bridges on a remote host from any browser or phone — start/stop, spawn & permission modes, CLAUDE.md editor, git clone, live log tail, cost tracking. Loopback by default; password/reverse-proxy auth. No telemetry.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages