Claude Code sessions run for a long time and then stop dead on a prompt โ a permission dialog, a plan approval, a yes/no. You walk away, come back, and find it spent nineteen of those minutes waiting for you to press Enter.
shabadoo is a self-hosted control plane for every Claude Code session on every machine you use. One dashboard. A queue of the ones waiting on you. A notification when one has been stuck. And the ability to answer from wherever you are.
One static binary for macOS and Linux, or a container for the coordinator. No Go toolchain,
no clone, and deliberately no curl | sh.
It is the round trip back to your attention. Multiply one blocked session by the number of projects you have going, across a workstation and a laptop and a box under the desk, and the cost is not compute โ it is you, not being there.
Each host's agent classifies every pane. A session waiting on a prompt is flagged the moment it happens โ no polling from your browser.
Not before. Ninety seconds of grace means the prompts you were already there to answer never notify at all โ and the question itself is the notification's first line, not "a session is waiting".
Open the pane, see what you are agreeing to, send the keystroke. From a browser or a phone, over your own private network โ and the timer resets.
Prebuilt binaries and a container image are published for every tagged release. Neither needs a Go toolchain or a clone.
OS=$(uname -s | tr '[:upper:]' '[:lower:]') # linux | darwin ARCH=$(uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/') BASE=https://github.com/alexj212/shabadoo/releases/latest/download curl -fsSL -o shabadoo "$BASE/shabadoo-$OS-$ARCH" curl -fsSL -o SHA256SUMS "$BASE/SHA256SUMS" # Verify before running it. The whole point of a published checksum is that # somebody checks it, and this is a binary you are about to give a terminal. grep " shabadoo-$OS-$ARCH$" SHA256SUMS | sed "s/shabadoo-$OS-$ARCH/shabadoo/" | sha256sum -c - chmod +x shabadoo && ./shabadoo version
sha256sum is shasum -a 256 on macOS. Platforms:
linux-amd64, linux-arm64, darwin-amd64,
darwin-arm64.
curl does not set the quarantine attribute, so the commands above work as
written. A binary downloaded through a browser is quarantined and Gatekeeper will
refuse it โ clear it with xattr -d com.apple.quarantine shabadoo.
About 27 MB, a static binary on Alpine running as UID 1000. Built for
linux/amd64 and linux/arm64 โ the coordinator's natural home is a
small always-on box, and a lot of those are Raspberry Pis. The image is the
coordinator only: an agent drives its host's tmux, so an agent in a container would
have nothing to manage.
docker pull ghcr.io/alexj212/shabadoo:0.1.5
latest does track the newest release, but pin a version anyway. This
image drives every pane on every connected machine, so "whatever was pushed most recently"
is not a property a restart should pick up on its own.
A coordinator you can actually reach is a few more lines โ a data directory, an auth
posture, one pairing code.
examples/docker-compose.yml is exactly that, commented, and takes about a
minute.
The Traefik variant is the same thing behind TLS, annotated with the one proxy setting
that otherwise breaks it silently.
curl โฆ | shThis binary installs a toolchain, and a project whose own rule is never fetch from the network during install should not open by asking you to pipe an unread script into a shell. Two commands and a checksum is the honest version of the same convenience.
Or build from source, which needs only a Go toolchain.
One static binary, three roles. A coordinator you run somewhere always-on, an agent on each machine that has tmux, and the launcher that starts the sessions in the first place.
# on the always-on box (--bootstrap prints one pairing code, then drop it) shabadoo hub --device-tokens --bootstrap --addr 0.0.0.0:8787 # on every machine you actually work on shabadoo setup --service --coord https://coordinator.example # then, from anywhere shabadoo sessions # every session on every host shabadoo tail homelab # what is that pane asking? shabadoo keys --pane homelab Enter
Your laptop needs no inbound port, no tunnel, no static address. It works behind NAT, on hotel wifi, asleep half the day. Only the coordinator has to be reachable.
Copy a single file to a fresh machine and it installs the services, the launcher and the config โ with no network and no source tree. No Node, no Python, no Docker required.
Each session is the expert in its own project. They message each other by domain
โ send to "homelab" โ see who is online, and say what they are working
on.
Every keystroke sent into every pane is recorded and attributed to an enrolled device. Read-only enrolment exists for anything that only needs to watch.
The premise is not a web terminal. It is that you already run several Claude sessions, each holding context nobody else has, and they should be able to act as one system โ monitored and driven from anywhere, without any of them leaving your network.
| Capability | What it does |
|---|---|
| Route by domain | Hand a task to whichever session owns a project. Ambiguous names are refused rather than guessed; unknown ones bounce with the list of what exists. |
| Durable inbox | Mail waits for a session that is offline and is delivered when it returns. Draining is final, so nothing is silently redelivered. |
| Declared status | tmux can tell you a window is idle. Only the session can tell you it is idle because it is waiting on a peer. |
| Blocked-session queue | Everything waiting on a human, longest first, at the top of the dashboard โ rendered only when it is not empty. |
Not fine print. If any of this is not acceptable to you, this is the wrong tool and it is better to find that out here.
Whoever holds a credential can read every project path, read the full buffer of any pane,
read any Claude session transcript, and send keystrokes into any pane. Those panes
typically run claude --dangerously-skip-permissions, so a keystroke can do
anything Claude can do on that machine.
Read-only enrolment limits a client to watching, and is the right default for anything that does not need to type. It mitigates this; it does not eliminate it.
Authorising an agent is the same decision as handing that machine's Claude panes to anyone who can reach your coordinator. A private network โ a VPN or a tailnet โ is the simplest way to mean that. If it must be reachable from the internet, put it behind an identity proxy: it speaks Cloudflare Access natively. What it must never be is a bare public port, which is why it refuses to start without an auth posture at all.
Everything is self-hosted. There is no cloud service, no account, and no telemetry: your prompts, panes and transcripts never leave your machines.
| Limitation | Detail |
|---|---|
| Requires tmux | Sessions must run in tmux. If you use Claude Code in an IDE or a bare terminal, this is not for you. |
| macOS and Linux | No native Windows agent. WSL works. |
| One user | Multi-tenancy is implemented but barely exercised. Treat it as single-operator. |
| Notifications via a relay | Blocked-session alerts go through an Apprise relay to Telegram, Pushover and similar. There is no native push app. |