Skip to content

schubydoo/clauster

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

640 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 Greptile OpenSSF Scorecard OpenSSF Best Practices

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

Spawn a Claude session from the Clauster dashboard — open a project's launch menu, trust the directory, and the session starts, then shows Running under Active sessions

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.

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 — see Privacy & data at rest for what Clauster keeps locally.

Install now: curl -fsSL https://raw.githubusercontent.com/schubydoo/clauster/main/install.sh | bash

The full install recipes and verification steps are in the Installation guide.

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.

Show all features

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 (Server Mode / Interactive Session true-resume, POSIX) per launch; claude.launch_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 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. Bridges are cloud-visible; the experimental hosted live-view channel is local-only — it streams in the dashboard but is never attachable from the Claude app.
  • 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).
  • Config editor — edit an allowlist of operational clauster.yml settings from the dashboard (GET/PUT /api/config). Only a Tier-A allowlist is exposed — auth, bind, secret, and structural fields are never editable or even read back to the browser. Writes are lost-update-guarded (a content hash from the read must still match on write), backed up, and applied atomically with your comments preserved; the running process keeps its startup config until restarted. See docs/configuration.md.
  • 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. usage.mode selects what the badge shows — cost, tokens, or off (hide it for privacy / screen-share); usage.show_cost: false is a deprecated alias for off.

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

Show all 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 / "Interactive Session" mode (opt-in, POSIX)claude.launch_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). The dashboard surfaces the resume mode per bridge and rediscovers a running pty bridge after a Clauster restart.
  • 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.
  • Background agents (experimental) — a dashboard panel that lists, dispatches, stops, and resumes Claude Code background sessions (claude --bg), backed by GET/POST/DELETE /api/agents plus POST /api/agents/{job_id}/resume. It rides Claude Code's agent-view research preview, so it's experimental and may change with the upstream CLI.
  • Outbound notifications & webhooks — get told when a bridge changes state. notifications push a human message (Slack/Discord/Telegram/email via Apprise, the notify extra) on a crash; webhooks deliver a JSON POST to your own endpoint on every spawn / ready / stop / crash transition (no extra dependency, http(s) only), plus three opt-in events — bg-settled, permission-needed, clone-done (default off; enable per-event under webhooks.events). Both are off by default, and a failing endpoint is always logged-and-swallowed so it never affects a bridge's lifecycle. See Operations → Crash alerts and Lifecycle webhooks.
  • Prometheus /metrics — opt into a read-only text-format scrape endpoint (observability.prometheus_enabled) exposing build info, bridge counts by status, a crash counter, and per-bridge CPU/RSS. It stays behind the auth guard unless you set observability.metrics_token_hash for token-based scraping. See Operations → Metrics.
  • Hosted live-view channel (opt-in, experimental) — an alternate substrate to the remote-control bridge. With claustrum.enabled: true, Clauster connect-or-spawns a single claustrum daemon per deployment and runs claude headless over its stream-json channel, streaming the session live in the browser (with permission prompts surfaced in the UI) instead of being driven from Claude Desktop / claude.ai. Off by default and fail-closed — an unreachable daemon surfaces in /healthz and never affects the bridge lifecycle. Requires the separate claustrum daemon binary, which is not yet publicly distributed — there is currently no public install or build recipe, so leave claustrum.enabled: false unless you already have the binary on your PATH.

Install

No Python needed — the install script grabs the signed standalone binary for your OS, verifies its checksum, and installs it to ~/.local/bin (Linux & macOS), printing a PATH hint if that directory isn't already on your PATH:

curl -fsSL https://raw.githubusercontent.com/schubydoo/clauster/main/install.sh | bash

On Windows, the PowerShell equivalent installs clauster.exe the same way:

irm https://raw.githubusercontent.com/schubydoo/clauster/main/install.ps1 | iex

Or pick another path — uv tool install clauster (recommended for a Python host), pip/pipx, Scoop on Windows (scoop bucket add clauster https://github.com/schubydoo/clauster && scoop install clauster), or Docker. Full recipes — including supply-chain verification — are in the Installation guide. To hack on Clauster itself, use the dev quick-start below.

Uninstall

  • Install script / standalone binary: remove ~/.local/bin/clauster on Linux or macOS; on Windows, delete %LOCALAPPDATA%\Programs\clauster\clauster.exe unless you set a custom install directory.
  • Python tools: run uv tool uninstall clauster, pipx uninstall clauster, or pip uninstall clauster, matching how you installed it.
  • Scoop: run scoop uninstall clauster.
  • Docker: stop and remove the container with docker rm -f clauster, then remove the image with docker rmi ghcr.io/schubydoo/clauster:latest (substitute the pinned tag you pulled, e.g. :X.Y.Z, if you used a specific release) if you no longer need it. Docker Compose users: run docker compose down from the directory containing compose.yaml (add -v to also delete named volumes).
  • Full purge: stop Clauster first, then remove your state_dir if you want local state/config gone too. See Privacy & data at rest and the Installation guide.

Quick start (dev)

Just running Clauster, not hacking on it? Follow the canonical Quickstart guide — it takes you from nothing to your first attachable bridge in a few minutes. The steps below are the from-source path for working on Clauster itself.

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 an authenticated claude on your PATH, spawning your first bridge is a handful of clicks — no terminal needed once it's started. Clauster spawns claude — it doesn't vendor it — and a spawned bridge inherits the host user's claude authentication, so claude must be logged in (interactive claude login or ANTHROPIC_API_KEY in the environment — either satisfies the check) before any bridge can connect. clauster doctor (step 2) confirms it; see the Quickstart prerequisites for the full list.

  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, new enough, and logged in (the bridge inherits this login), 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. Launch a bridge. On a project's card, click Run Claude here ▾, choose In claude.ai / Desktop, pick a permission mode, then Run. Clauster launches claude remote-control in that directory and the card flips to Running with a live status badge. (The spawn-mode and permission defaults are safe out of the box.)
  5. Attach from anywhere. Use the card's Open 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 claude.launch_mode: pty it can carry the prior conversation forward — see Opt-in extras). For a deliberate fresh start, Forget the stopped session and launch again with Run Claude here.

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 human text by default; set CLAUSTER_LOG_FORMAT=json for structured JSON (both redact session URLs / bearer ids). 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.launch_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
claustrum.enabled false enable the hosted live-view channel (connect-or-spawn the claustrum daemon)
usage.mode cost per-project badge contents: cost (≈USD + tokens) · tokens (count only) · off (hide + skip the usage fetch). usage.show_cost: false is a deprecated alias for off
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.password_hash
clauster hash-token           # mint an API token + hash for auth.api_token_hash
clauster hash-metrics-token   # mint a /metrics scrape token + hash for observability.metrics_token_hash
clauster api-token issue|list|rotate|revoke   # manage named public-API bearer tokens
clauster mcp                  # read-only MCP server over stdio (list + status)
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 keepers              # list or stop orphaned pty keepers
clauster usage <transcript>   # token + approximate cost for a session transcript
clauster config reconcile     # remove deprecated config keys, writing their replacements

Roadmap

Planned work, roughly in priority order — the public-facing companion to the in-repo scratch/TODO.md.

  • Session naming — predictable/branded session display names instead of the random adjective-noun defaults; list active/resumable sessions in the UI.

Not planned: clauster is a single-operator tool — multi-user accounts, OIDC login, and per-user GDPR tooling were considered and declined (one deployment serves one operator; isolate with separate instances instead). The UI is English-only and not localized — there is no i18n string extraction on the roadmap (re-scope only if a real translation contributor appears).

Shipped:

  • Public API — a documented, versioned /api/v1 surface with named Bearer tokens (distinct from the session cookie), managed via clauster api-token issue|list|rotate|revoke; an opt-in OpenAPI schema (api.openapi_enabled) is available for third-party dashboards.
  • The in-repo docs/ pages (setup, networking, config reference, security model) are published as a live docs site at schubydoo.github.io/clauster.

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.

Support

Questions, bugs, and feature requests all go through GitHub Issues — Discussions are intentionally not enabled. See SUPPORT.md for how to get help, and SECURITY.md to report a vulnerability privately.

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

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors