cli-bridge
Manage short-code bundles that authorize the local starchild CLI to talk to this agent, including the agent-shell local-exec channel and the local MCP proxy (stdio MCP servers on the user's machine). Use when connecting or disconnecting the starchild CLI (e.g. mint a CLI bridge code, list my CLI bun
By starchild-ai-agent · 1,783 installs
npx skills add starchild-ai-agent/official-skills --skill cli-bridge
Source repository · Upstream listing
cli bridge — issue CLI bundles for the user's own starchild binary
This skill mints a fresh AKM key ( scope=chat:bridge:cli ) on the local
clawd, then registers it with sc chatroom in exchange for a short opaque
code ( sc xxxxxxxx ). The bundle handed to the user contains only that
short code — never the AKM secret, never the Fly machine id.
Why a short code instead of the raw AKM?
Earlier versions baked the AKM secret + Fly machine id into the bundle
directly. That worked but had two downsides — the bundle leaked routing
metadata when decoded, and any party that ever held the bundle held a
permanent AKM secret. The short code form fixes both:
Bundle base64 decodes to {d, c:"", k:"sc …", s, exp, l} — no
secret, no Fly machine id.
cli revoke <sc … kills just the short code; the underlying AKM
stays alive (use cli revoke akm <prefix to nuke that too).
sc chatroom now holds the AKM secret in its DB. That's a deliberate
trust shift — the AKM stays inside Fly's internal network instead of
riding around on user laptops.
Scope boundary — read this first
cli bridge covers exactly one path : the user's local CLI talking 1:1
to that user's own clawd. It is not a chatroom membership credential.
Use case Right credential Wrong
Personal CLI ↔ own clawd (this skill) chat:bridge:cli AKM, fronted by sc … code —
Join an sc chatroom room chat:thread:chatroom {room id} AKM via chatroom join chat:bridge:cli AKM
Browse a public room as a guest no credential needed any AKM
Install the CLI
The rest of this skill assumes starchild is on the user's $PATH — install
it first if it isn't.
One liner (auto detects OS + arch)
Picks the right binary for darwin/linux × arm64/amd64, drops it on
$PATH (Apple Silicon lands in /opt/homebrew/bin ; Linux falls back to
~/.local/bin ; sudo only when the dir isn't user writable), patches the
user's shell rc if the install dir wasn't already on $PATH , and runs
starchild version as a self check. SHA256 etag means re running is a
cheap "already current" no op (HTTP 304, no download). Source for review:
[tools/install cli.sh](https://workroom.iamstarchild.com/install/cli)
( SERVER URL is rewritten at request time).
Homebrew
The starchild formula ships binaries for macOS (arm64 / amd64) and
Linux (arm64 / amd64) — brew install picks the right one for the host.
The formula has no bottle block, so install runs a tiny Ruby script that
downloads the prebuilt binary from the server ( workroom.iamstarchild.com )
and drops it on $PATH — there's no local compile step. To upgrade later:
brew update && brew upgrade starchild .
Linux caveat: Homebrew itself works on Linux, but expects a Ruby +
build toolchain (one time apt install build essential ruby / distro
equivalent). For a Linux host, the oneliner above skips that and is
functionally identical, so prefer it unless the user is already a brew
user. starchild app (the desktop workspace) is macOS only — that
formula builds from source (rust + node) and only the macOS build is
meaningful.
Verify
If you just ran the one liner and your shell still says command not found ,
open a new terminal — the PATH update is in your rc, not the current
session.
Prerequisites
Same as chatroom :
AKM is installed in this clawd ( POST /api/keys works on loopback)
AKM accepts scope="chat:bridge:cli" and the /chat/stream middleware
allows arbitrary thread id for that scope (already shipped in clawd
branch aladdin/feat/akm chatroom )
sc chatroom is on a build that includes POST /cli keys (migration 007+)
FLY MACHINE ID (or CONTAINER ID ) env is set
CHATROOM PUBLIC URL env points at the sc chatroom gateway (defaults
to https://workroom.iamstarchild.com )
CHATROOM SERVER URL env points at the Fly internal sc chatroom
(defaults to http://sc chatroom.internal:8080 )
Commands
cli login — mint a new bundle
Default TTL is 90 days; max is 365 days. Output is a one liner the user
copies into starchild login . The bundle is opaque — sc chatroom
resolves it on each call.
cli list — show active bundles
Lists every CLI short code minted by this user on sc chatroom. Columns:
code, issued, expires, uses, label.
cli revoke — kill a bundle
Default: kills the short code in sc chatroom; underlying AKM stays alive.
With akm : also revokes the AKM on local clawd, taking out every
bundle backed by it.
Local shell via agent shell (CLI ≥ v0.2.0)
A cli login bundle minted with enable shell also authorizes the
agent to run shell commands on the user's own machine — for "is nginx
running on my laptop", "organize ~/Downloads", and the like. A plain bundle
is a chat bridge only and grants no shell access (see "Shell is off by
default" below). The user starts a small daemon:
agent shell refuses to start if the logged in bundle wasn't granted shell
— it tells the user to get a enable shell bundle rather than connecting
a channel clawd would reject.
The daemon is single instance (pidfile + flock) and macOS/Linux only. It
self updates at startup and periodically; downloaded binaries are verified
against an embedded Ed25519 release key before swapping, so a hostile or
MITM'd update server can't push arbitrary code to the user's machine.
How it works: the daemon dials wss://<chatroom /ws/cli shell with the
bundle's sc … code. sc chatroom resolves the code and reverse proxies
the WebSocket to the user's clawd machine — it accepts the laptop's
upgrade, opens its own upstream WS to clawd pinned with
fly force instance id , and pumps bytes between the two (this is not
fly replay : chatroom and clawd are different Fly apps, and cross app
replay is rejected with 403). The AKM is injected server side on the
upstream hop — it never reaches the laptop. clawd holds the connection in
its ShellHubService ; the local shell tool is then exposed to the LLM
only while a shell capable laptop is connected , and pushes commands
down the socket.
Shell is off by default (capability gate)
cli login does not grant shell unless enable shell is passed. The
AKM is the authoritative capability source: clawd reads it on the
/ws/cli shell handshake and refuses every exec for a connection that
doesn't carry shell ( 264). So a leaked plain bundle is a chat credential,
never local RCE.
Grant shell: cli login.py label … enable shell → AKM
capabilities: ["shell"] , bundle carries x: ["shell"] .
Upgrade an existing no shell bundle: you can't flip it in place — mint a
new enable shell bundle, starchild login it, and cli revoke the
old one. Privilege escalation always goes through a fresh issuance.
What the agent knows up front (capability manifest)
On connect, the daemon sends a hello frame advertising:
Platform — os (darwin/linux), arch (arm64/amd64), and the active
shell . So the agent knows whether it's talking to BSD or GNU userland,
which package manager to assume, etc. — no more guessing ps flags or
hitting ps: illegal option .
Policy summary — mode ( default deny when no allow rules exist, else
allowlist ), the user's allowed rules, explicit denied extra rules,
and the always on builtin denied list.
File transfer policy — the transfer dir (always allowed workspace),
yolo flag, and the read allow / write allow globs from
~/.config/starchild/file policy.toml . Present only when the bundle
carries the files capability. See "File path policy" below for the
full rules; this bullet is just so the agent knows the laptop
advertised file transfer at all.
clawd renders this into the agent's system prompt (only while connected),
so the agent picks a permitted command — or tells the user plainly that the
local policy forbids it — instead of probing blindly.
Session behavior
Connection level cwd. Each command's resulting working directory is
echoed back (via a trailing pwd sentinel stripped from stdout) and
persisted for the next command, so cd has real meaning across calls
within a session — without the cost/fragility of a full PTY. An explicit
per call cwd overrides it.
Output truncation. stdout/stderr are each capped at 200 lines (plus a
byte cap) so a find / or log dump can't flood the LLM context. The full
pre truncation line count is reported ( stdout lines / stderr lines ),
and truncated: true is set — the agent can say "showing first 200 of N
lines" rather than truncating silently.
Heartbeat. The daemon pings every 45s to keep the idle WebSocket
alive (Fly's edge cuts idle sockets at ~2.5min). Exec runs in a goroutine
so a long command doesn't block heartbeats.
Local execution policy (the only auto run guard)
The daemon runs headless (no TTY to prompt on), so every command is
gated by ~/.config/starchild/exec policy.toml (parsed as a tiny
YAML allow: / deny: line format — no TOML dependency, despite the name).
Rules are substring matches by default; wrap a rule in / / for a
regex:
Decision order: built in deny (always wins) → file deny → file
allow → default deny. Two hard rules apply regardless of the file:
A built in deny list of interactive/TTY blocking and destructive
commands is always refused: vim / vi / nano / emacs ,
less / more / man , top / htop / btop , ssh / telnet , sudo / su /
doas , tmux / screen , reboot / shutdown / halt , plus the shapes
rm rf , mkfs , dd if= , … sh , … bash , /dev/sd .
Default deny: anything not matched by an allow rule is denied. So
with no policy file the policy mode is default deny and nothing runs
until the user opts commands in.
Limitations
Unattended policy only. There is no interactive approval prompt; the
policy file is the sole guard. A future version adds a web approval popup.
Synchronous commands only. No background jobs / progress polling yet.
macOS/Linux only. The daemon refuses to run on Windows.
Revocation: cli revoke <sc … kills the short code; the daemon's
next reconnect then fails auth and the channel closes.
File transfer via agent shell (CLI ≥ v0.3.0)
When the bundle is minted with enable files , the same agent shell
daemon also serves file transfer between the user's machine and the
agent's workspace. Content streams disk→disk and never passes through the
chat, so large/binary files (10MB+ PDFs, images, archives) work .
Three agent facing tools + one user command:
request upload(laptop path) — agent pulls a file FROM the laptop into
workspace/uploads/ ("take my ~/big.pdf and summarize it").
write local file(src, dst) — agent sends a workspace file TO the laptop
("save workspace/output/report.pdf to my ~/Downloads"). src is a
workspace path, not inline content.
read local file(path) — read a small text file for the agent to see
(config/log snippet). Large/binary files go through request upload .
starchild push <file — user proactively uploads a local file into the
agent's workspace/uploads/ ; it's announced to the agent in its prompt.
files is an independent capability from shell — a bundle can have
either, both, or neither. Like shell, it's off by default and authoritative
on the AKM (clawd refuses transfer frames for a connection without it).
File path policy (laptop side, layered)
Transfers are gated on the laptop by a path policy, strictest first:
1. Built in protected paths are ALWAYS refused (even under yolo ):
~/.ssh , ~/.aws , shell rc ( .zshrc / .bashrc /…), .config/starchild ,
launchd/systemd/cron, .git/hooks , browser cookie stores, .env , ssh
keys. Writing those would be persistent RCE; reading them leaks creds.
2. Dedicated transfer dir ( ~/starchild transfer , auto created) — always
allowed for read + write. The safe default workspace; prefer it.
3. Outside that