Runs Claude Code sessions in tmux on your machine and streams them to a Famulus backend
Project description
famulus-worker
The machine-side half of Famulus. It runs
Claude Code sessions on your computer, where your repositories, your tooling and
your claude login already live, and reports progress back to the Famulus backend.
Connections are outbound only. The worker dials the backend over WebSocket, so your machine needs no open ports, no port forwarding and no public IP.
┌─────────────┐ outbound ws ┌──────────────┐
│ worker │ ───────────────► │ backend │ ◄──── browser
│ (your Mac) │ ◄─────────────── │ (anywhere) │
└─────┬───────┘ tasks, replies └──────────────┘
│
▼ tmux session per task
`claude` ── writes result JSON ── picked up by the worker
⚠️ Read this before installing
The worker starts claude with --permission-mode auto and pre-accepts the
workspace trust prompt, because a pipeline can't answer interactive dialogs. In
practice that means:
- Claude will read, write and delete files in the working directory you configure, and run shell commands there, without asking for confirmation.
- Task titles, descriptions and imported ticket text are fed into that session as instructions. Treat anything you import as untrusted input. A hostile ticket description is a prompt-injection vector that can reach your shell.
- The backend can ask the worker to open an interactive terminal on this machine, relayed to whoever is authenticated to the web UI.
Run it against directories you're willing to let an agent modify, with a backend only you can reach. See SECURITY.md for the full trust model.
Requirements
- Python 3.12+
tmuxonPATH- Claude Code (
claude) onPATH, already signed in - A reachable Famulus backend
Install
uv tool install famulus-worker
Other installation methods
pipx install famulus-worker # isolated, also fine
pip install famulus-worker # into the current environment
# from a checkout
git clone https://github.com/getfamulus/famulus-worker && cd famulus-worker
uv sync && uv run famulus-worker --help
Run
famulus-worker --api http://localhost:8000 --worker-token "$FAMULUS_WORKER_TOKEN"
The token has to match the backend's FAMULUS_WORKER_TOKEN, or FAMULUS_AUTH_TOKEN if
you didn't configure a separate worker credential. On success you'll see:
Worker starting (reverse tunnel mode)
API: http://localhost:8000
Claude: /opt/homebrew/bin/claude
Connected to backend relay
Options
| Flag | Env | Default | Description |
|---|---|---|---|
--api |
FAMULUS_API |
http://localhost:8000 |
Backend base URL |
--worker-token |
FAMULUS_WORKER_TOKEN |
none | Worker credential (preferred) |
--token |
FAMULUS_TOKEN |
none | Fallback credential if no worker token is set |
--poll-interval |
none | 3 |
Seconds between task polls |
Keeping it running
macOS launchd example
Save to ~/Library/LaunchAgents/dev.famulus.worker.plist, then
launchctl load ~/Library/LaunchAgents/dev.famulus.worker.plist.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>dev.famulus.worker</string>
<key>ProgramArguments</key>
<array>
<string>/Users/you/.local/bin/famulus-worker</string>
<string>--api</string><string>https://famulus.example.com</string>
</array>
<key>EnvironmentVariables</key>
<dict><key>FAMULUS_WORKER_TOKEN</key><string>replace-me</string></dict>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict>
</plist>
How it works
- Poll. Ask the backend for steps whose status is
running, grouped by stage. - Session. Ensure a detached tmux session per task (
tr-<taskid>) running one persistentclaudeconversation keyed to the task id, so later stages keep context. - Dispatch. Paste a stage prompt into the session via a tmux paste-buffer, never a shell string, so task text can't escape into your shell. Claude is asked to fan out one subagent per parallel job.
- Collect. Claude writes
{"status","output"}JSON per job into<working_dir>/.tr/<task_id>/. The worker posts each result and deletes the file. - Relay. On request, open a PTY (attaching to the task's tmux session when there is one) and stream it to the browser through the backend.
State lives in ~/.famulus/: pane logs, session and dispatch markers, attachment cache.
Those markers are what make a restart adopt in-flight work instead of re-running it.
Worktrees (optional)
If a task asks for a git worktree, the worker runs
~/.claude/utils/worktree-new.sh <ticket-id> in the repository and expects it to print
WORKTREE_PATH=<path>. Supply your own script at that path. There's a reference
implementation in examples/worktree-new.sh.
Troubleshooting
| Symptom | Cause |
|---|---|
Backend closed during auth, reconnecting… |
Token mismatch. It must equal the backend's FAMULUS_WORKER_TOKEN |
claude cannot run: Login expired |
The CLI's login lapsed. Run claude on this machine, sign in, then re-run the task |
Session … did not report ready |
claude was slow or isn't signed in. Run claude once by hand |
| Steps stay "running" forever | No tmux, or Claude never wrote a result file. Open the live terminal to look |
Worktree failed |
Your worktree-new.sh is missing or didn't print WORKTREE_PATH= |
Development
uv sync
uv run famulus-worker --help
uv build
License
MIT, see 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 famulus_worker-0.1.1.tar.gz.
File metadata
- Download URL: famulus_worker-0.1.1.tar.gz
- Upload date:
- Size: 23.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9060e790263c31b05b1aeed32576767767414285511e4d5a244b64df48292708
|
|
| MD5 |
bd08d2b140bc9ab8bf0553002f073834
|
|
| BLAKE2b-256 |
4b7c416662c055c2ff64506edbdbfda34c6db4c637c589545874e7c32466f293
|
Provenance
The following attestation bundles were made for famulus_worker-0.1.1.tar.gz:
Publisher:
publish.yml on getfamulus/famulus-worker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
famulus_worker-0.1.1.tar.gz -
Subject digest:
9060e790263c31b05b1aeed32576767767414285511e4d5a244b64df48292708 - Sigstore transparency entry: 2300128770
- Sigstore integration time:
-
Permalink:
getfamulus/famulus-worker@1250618e679de604c79e2ea3836502c83886c5f8 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/getfamulus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1250618e679de604c79e2ea3836502c83886c5f8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file famulus_worker-0.1.1-py3-none-any.whl.
File metadata
- Download URL: famulus_worker-0.1.1-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3acf46bef54e11707654efc31b748f60fcc84879130168d2e5b2064944edb4b2
|
|
| MD5 |
5c3cc703b258e96ed043a7228d075fb9
|
|
| BLAKE2b-256 |
6b4941d8d6557ebc637b6d112043b633a90a6cc01becfeb14c03b3d9e9e87caa
|
Provenance
The following attestation bundles were made for famulus_worker-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on getfamulus/famulus-worker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
famulus_worker-0.1.1-py3-none-any.whl -
Subject digest:
3acf46bef54e11707654efc31b748f60fcc84879130168d2e5b2064944edb4b2 - Sigstore transparency entry: 2300128833
- Sigstore integration time:
-
Permalink:
getfamulus/famulus-worker@1250618e679de604c79e2ea3836502c83886c5f8 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/getfamulus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1250618e679de604c79e2ea3836502c83886c5f8 -
Trigger Event:
push
-
Statement type: