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.
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.
|
Dark / light — theme toggle persists across reloads |
Create or clone — SSRF-guarded, cloned code runs only on Start |
|
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 / restart 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) and
permission mode per launch.
bypassPermissionsis 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.
- 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_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.
Safety
- Workspace trust — a "Trust directory" action writes the Claude workspace-trust flag before spawning; untrusted directories are refused.
- 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.
Opt-in extras
- 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 Restart (--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.
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).
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 — 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.
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 Restart (POSIX) |
reaper.ui_enabled |
false |
expose the ghost-environment reaper in the dashboard |
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
/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.
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file clauster-0.2.2.tar.gz.
File metadata
- Download URL: clauster-0.2.2.tar.gz
- Upload date:
- Size: 627.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e01864d8a0c479303e7a15aeeb8f8a422c875ce6e867600acc43ae1732c61d65
|
|
| MD5 |
c6ad911709e951b18fb7e46423ce205c
|
|
| BLAKE2b-256 |
75ca6ec4de7216942d246a64878ed6df7113fbef71878ec59ef08c20f7815d3a
|
Provenance
The following attestation bundles were made for clauster-0.2.2.tar.gz:
Publisher:
release.yml on schubydoo/clauster
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
clauster-0.2.2.tar.gz -
Subject digest:
e01864d8a0c479303e7a15aeeb8f8a422c875ce6e867600acc43ae1732c61d65 - Sigstore transparency entry: 1709820228
- Sigstore integration time:
-
Permalink:
schubydoo/clauster@b98bc254ec194e2092da8473c6053b80d3c46e9a -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/schubydoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b98bc254ec194e2092da8473c6053b80d3c46e9a -
Trigger Event:
push
-
Statement type:
File details
Details for the file clauster-0.2.2-py3-none-any.whl.
File metadata
- Download URL: clauster-0.2.2-py3-none-any.whl
- Upload date:
- Size: 236.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f23db3fa55656cab64893031fe57a1bc46b947b21adb760b666e3b0b8bfbed2
|
|
| MD5 |
8989c57a5940ac023c5bfb76bf3cc138
|
|
| BLAKE2b-256 |
eaa64fb8d9fd0ae05cc1f25437bae0d16ee272cb04a714b26d8c202aae6b2384
|
Provenance
The following attestation bundles were made for clauster-0.2.2-py3-none-any.whl:
Publisher:
release.yml on schubydoo/clauster
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
clauster-0.2.2-py3-none-any.whl -
Subject digest:
6f23db3fa55656cab64893031fe57a1bc46b947b21adb760b666e3b0b8bfbed2 - Sigstore transparency entry: 1709820705
- Sigstore integration time:
-
Permalink:
schubydoo/clauster@b98bc254ec194e2092da8473c6053b80d3c46e9a -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/schubydoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b98bc254ec194e2092da8473c6053b80d3c46e9a -
Trigger Event:
push
-
Statement type: