Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Using shellshare from scripts and AI agents

> Contributing to this repository? See [CLAUDE.md](CLAUDE.md) for build,
> lint, and test instructions. This file documents how to *use* shellshare
> programmatically — e.g. an AI agent sharing a live terminal with its user.

shellshare broadcasts a terminal session to a web link in one command. No
signup, no configuration: run it, parse the link, hand the link to a human.
Viewers see the terminal live in their browser, read-only.

## Install

```bash
npx -y shellshare --help # no install (Node.js)
curl -sLo shellshare https://get.shellshare.net/ && chmod +x shellshare # static binary (auto-detects OS)
```

Binaries exist for Linux x64, macOS x64/arm64, and Windows x64
(`https://get.shellshare.net/?os=linux|mac|mac-arm|windows`).

## The machine-readable contract: `--json`

With `--json`, shellshare prints newline-delimited JSON events to stdout:

- First line, before any terminal output:
`{"event":"sharing","url":"https://shellshare.net/r/<room>","room":"<room>","server":"https://shellshare.net"}`
- Last line: `{"event":"end","exit_code":0}`

Errors are printed to stderr as `ERROR: ...` and the process exits non-zero.
Parse the `url` field from the first stdout line — that is the link to give
to your user.

## Recipes

**Share one command and exit when it finishes** (the usual agent case —
e.g. let the user watch a long build, test run, or migration live):

```bash
shellshare exec --json -- npm test
```

`exec` runs the command in a PTY, streams it live, and exits with the
command's exit code. Note the `--` separator before the command.

**Stream a log or pipe** (no PTY, reads stdin until EOF):

```bash
tail -f build.log | shellshare --stdin --json
```

**Background it and capture the URL while you keep working:**

```bash
shellshare exec --json -- ./long-task.sh > /tmp/ss.out 2>/tmp/ss.err &
until URL=$(head -1 /tmp/ss.out | jq -re .url) 2>/dev/null; do sleep 0.2; done
echo "Watch live: $URL"
```

**Stable room name across restarts** (same link every time):

```bash
shellshare exec --json -r my-room -W my-password -- make deploy
```

Without `-W`, the machine's MAC address is the password, so the same room
is only reclaimable from the same machine.

**Fully local / private** — `shellshare serve` runs the broadcast through an
embedded server on localhost (nothing leaves the machine); add `--tunnel` to
get a public `https://*.trycloudflare.com` link without using shellshare.net
(requires `cloudflared` installed).

## Behavior worth knowing

- One-way only: viewers cannot send input to the terminal.
- The share link is unguessable (18 random alphanumerics) but public —
anyone with the link can watch. Don't broadcast secrets.
- Broadcasts are live-only and not recorded; rooms are deleted when the
broadcast ends (or after 6 hours of inactivity, the server default).
- Late joiners see recent history, so the page is not blank if the user
opens the link mid-run.
- Transient network failures are handled: output is buffered and replayed
on reconnect. Only authorization errors (room owned by someone else) are
fatal.
- `--theme <name>` controls the colors viewers see (e.g. `dracula`,
`solarized-dark`; see `--help` for the full list).

Machine-readable copy of this document: https://shellshare.net/llms.txt
Source: https://github.com/vitorbaptista/shellshare
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ cd e2e && uv sync && uv run pytest -n 10

**Dual-mode binary**: `shellshare` operates as client (default) or server (`shellshare server`). `shellshare serve` combines both: it boots the embedded server on a background thread (default `localhost:3000`, configurable via `--host`/`--port`) and runs the client against it, sharing the terminal with no external server. Both `serve` and `server` accept `--tunnel` (`src/tunnel.rs`): it spawns the user's pre-installed `cloudflared` against the local server, waits for the `https://*.trycloudflare.com` URL from its stderr banner, and uses it as the share link (the broadcaster still talks to localhost); missing cloudflared is a fatal error pointing at the install docs, and the tunnel process dies with shellshare.

**Scripting surface**: `shellshare exec -- <cmd>` runs one command in the PTY (instead of a shell), broadcasts it, and exits with the command's exit code. The global `--json` flag switches stdout to newline-delimited JSON events: first `{"event":"sharing","url":...}`, last `{"event":"end","exit_code":N}` (errors stay on stderr as `ERROR: ...`). This contract is documented in `AGENTS.md` and `public/llms.txt` and covered by `e2e/test_agents.py` - the three must stay in lockstep.

### Client (`src/cli/`)
Multi-threaded design ensures network latency never blocks terminal display:
- **PTY reader thread**: Captures shell output, displays locally, sends to the sender thread
Expand Down
153 changes: 81 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,87 @@
[![E2E Tests](https://github.com/vitorbaptista/shellshare/actions/workflows/e2e.yml/badge.svg)](https://github.com/vitorbaptista/shellshare/actions/workflows/e2e.yml)
[![Release](https://github.com/vitorbaptista/shellshare/actions/workflows/release.yml/badge.svg)](https://github.com/vitorbaptista/shellshare/actions/workflows/release.yml)

Live broadcast of terminal sessions.
Broadcast your terminal live to anyone with a link — read-only, one command,
viewers just need a browser.

## Why?

Ever wanted to quickly show what you're doing to some friends? Maybe you're seeing a weird error and would like some help. Or the other way around: some friend of yours is asking for help on something, then you start to ping-pong: you tell a command, he pastes the output, then you tell another, and so on...

The objective of [shellshare.net](https://shellshare.net) is to provide an easy way to broadcast your terminal live. No signups, no configurations, anything: simply run a command and you're good to go.

## Using

Copy and paste the following line in your terminal:
## Quick start

```bash
curl -sLo shellshare https://get.shellshare.net/ && chmod +x shellshare && ./shellshare
npx shellshare
```

If you have Node.js, you can also run it with no manual download on Linux, macOS, or Windows:
Or download the binary directly:

```bash
npx shellshare
curl -sLo shellshare https://get.shellshare.net/ && chmod +x shellshare && ./shellshare
```

You'll see a line saying `Sharing session in
You'll see a line saying `Sharing terminal in
https://shellshare.net/r/h2Uont4F8bvZ8VDjHb` (your link will be different).
Anyone that opens this link will be able to see what you're doing in your
terminal. When you're done, type `exit` or hit CTRL+D.

### Hosting a server
## Why shellshare

- **Read-only by design** — viewers can never type into your terminal
- **Viewers only need a browser** — no install, no account; broadcasters run one command
- **No signups, no configuration** — one command in, one URL out
- **Single binary contains client _and_ server** — self-host with
`shellshare serve`, or go public without shellshare.net via `--tunnel`
- **Free and open source** — Apache-2.0

## Use cases

- **Teach a class or run a workshop**: students follow your terminal on
their own screens instead of squinting at a projector
- **Live demos and conference talks**: attendees open a URL and watch in
real time
- **Get or give help**: show a colleague a weird error as it happens,
instead of ping-ponging commands and pasted output
- **Stream a long-running job**: let teammates (or an AI agent's user)
watch a build, deploy, or migration as it runs

## How it compares

| You want to... | Use |
|---|---|
| Watch together, live | **shellshare** |
| Let viewers type (pair programming, remote rescue) | [tmate](https://tmate.io), [upterm](https://upterm.dev), [sshx](https://sshx.io) |
| Record now, replay later | [asciinema](https://asciinema.org) or [other terminal recorders](https://github.com/topics/terminal-recording) |
| Full two-way terminal in a web page | [ttyd](https://github.com/tsl0922/ttyd), [gotty](https://github.com/sorenisanerd/gotty) |

## Features

- Read-only, one-to-many live broadcasting to the browser
- Named rooms with passwords (`--room MY-ROOM --password MY-PASS`)
- Viewer color themes (`--theme dracula` — same themes as asciinema)
- Late joiners see recent history, not a blank page
- Network drops are handled: output is buffered and replayed on reconnect
- Linux, macOS (Intel and Apple Silicon), and Windows binaries
- Machine-readable mode for scripts and AI agents (`--json`, `exec`)

### Scripting & AI agents

shellshare is built to be driven by scripts and AI agents — for example, an
agent sharing a live view of a long build with its user. Add `--json` for a
machine-readable contract: the first line on stdout is
`{"event":"sharing","url":"https://shellshare.net/r/..."}` (parse `url` and
hand it to your user), and a final `{"event":"end","exit_code":N}` line is
printed when the broadcast finishes. Errors go to stderr as `ERROR: ...`
with a non-zero exit.

```bash
# Share a single command live; exits with the command's exit code
shellshare exec --json -- npm test

# Stream a log or any pipe (reads stdin until EOF)
tail -f build.log | shellshare --stdin --json
```

See [AGENTS.md](AGENTS.md) (or https://shellshare.net/llms.txt) for the full
agent-facing documentation and recipes.

## Self-hosting

The same `shellshare` binary also includes the server code, allowing you to broadcast your terminal to a server you control.

Expand All @@ -48,9 +101,9 @@ shellshare serve --tunnel

The share link becomes a public `https://*.trycloudflare.com` URL that anyone can open, while your terminal never leaves your machine except through that tunnel. It requires [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) to be installed (`brew install cloudflared` on macOS) - no Cloudflare account needed. The tunnel closes when shellshare exits.

## Installing
### Building from source

Requires [Rust](https://rustup.rs/) to build from source:
Requires [Rust](https://rustup.rs/):

```bash
cargo build --release
Expand All @@ -64,64 +117,20 @@ broadcast to this instance, use the `--server` option:
./target/release/shellshare --server http://localhost:3000
```

## Deploy

To deploy with [Dokku](https://dokku.com/), let it build the image from
source on each push using the project's `Dockerfile`:

```bash
# Create the app
dokku apps:create shellshare

# Build from the source Dockerfile (this is also Dokku's default)
dokku builder-dockerfile:set shellshare dockerfile-path Dockerfile

# Deploy: pushes the current commit; Dokku builds and releases it
make deploy
```

Each `make deploy` builds the pushed commit on the Dokku host, so the
deployed code always matches what you pushed — there is no separate image
tag to bump.

## Analytics (optional, off by default)

The server can send anonymous usage events (rooms created, broadcast
durations, viewer counts) to [PostHog](https://posthog.com). Nothing is
collected unless you opt in by setting both variables:

```bash
SHELLSHARE_POSTHOG_KEY=phc_yourprojectkey \
SHELLSHARE_POSTHOG_SALT=some-long-random-secret \
shellshare server
```

(Set `SHELLSHARE_POSTHOG_HOST` for self-hosted PostHog. The equivalent
`--posthog-*` flags also exist, but prefer the environment variables:
the salt is a secret, and command-line arguments are visible to other
local users.)

No personal data is sent: no IP addresses, no room names, no passwords.
Broadcasters are identified only by `HMAC-SHA256(salt, password)` and
rooms by `HMAC-SHA256(salt, room_name)`, which lets the operator count
returning users without being able to identify anyone. Keep the salt
stable across restarts and servers so returning users stay recognizable;
rotating it resets all identities. Events are fire-and-forget and never
block or slow down broadcasting.

## Releasing

```bash
make release # patch bump, e.g. 2.0.6 -> 2.0.7
make release VERSION=2.1.0 # explicit version
```
## Security model

This bumps Cargo.toml, commits, tags, and pushes. CI then runs the e2e tests, builds all platforms, creates the GitHub release with binaries, and publishes the [npm packages](https://www.npmjs.com/package/shellshare).
Data flows one way: from your terminal to the server to the viewers.
Viewers cannot send input. Share links are unguessable (18 random
alphanumerics) but public — anyone with the link can watch, so don't
broadcast secrets. Broadcasts are not recorded: rooms are deleted when
the broadcast ends or after 6 hours of inactivity (server default,
configurable with `--room-ttl`). If you don't want your
bytes to touch shellshare.net at all, self-host (`shellshare serve`,
optionally with `--tunnel`).

## Limitations
## Deploying shellshare.net, analytics, releasing

This project is intended for live broadcasts only. If you'd like to record your terminal, check [asciinema.org](https://asciinema.org)
or [other terminal recording tools](https://github.com/topics/terminal-recording).
Maintainer documentation lives in [docs/OPERATIONS.md](docs/OPERATIONS.md).

# License

Expand Down
59 changes: 59 additions & 0 deletions docs/OPERATIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Operations

Maintainer-facing documentation: deploying shellshare.net, server analytics,
and cutting releases. If you just want to use or self-host shellshare, see
the [README](../README.md).

## Deploy

To deploy with [Dokku](https://dokku.com/), let it build the image from
source on each push using the project's `Dockerfile`:

```bash
# Create the app
dokku apps:create shellshare

# Build from the source Dockerfile (this is also Dokku's default)
dokku builder-dockerfile:set shellshare dockerfile-path Dockerfile

# Deploy: pushes the current commit; Dokku builds and releases it
make deploy
```

Each `make deploy` builds the pushed commit on the Dokku host, so the
deployed code always matches what you pushed — there is no separate image
tag to bump.

## Analytics (optional, off by default)

The server can send anonymous usage events (rooms created, broadcast
durations, viewer counts) to [PostHog](https://posthog.com). Nothing is
collected unless you opt in by setting both variables:

```bash
SHELLSHARE_POSTHOG_KEY=phc_yourprojectkey \
SHELLSHARE_POSTHOG_SALT=some-long-random-secret \
shellshare server
```

(Set `SHELLSHARE_POSTHOG_HOST` for self-hosted PostHog. The equivalent
`--posthog-*` flags also exist, but prefer the environment variables:
the salt is a secret, and command-line arguments are visible to other
local users.)

No personal data is sent: no IP addresses, no room names, no passwords.
Broadcasters are identified only by `HMAC-SHA256(salt, password)` and
rooms by `HMAC-SHA256(salt, room_name)`, which lets the operator count
returning users without being able to identify anyone. Keep the salt
stable across restarts and servers so returning users stay recognizable;
rotating it resets all identities. Events are fire-and-forget and never
block or slow down broadcasting.

## Releasing

```bash
make release # patch bump, e.g. 2.0.6 -> 2.0.7
make release VERSION=2.1.0 # explicit version
```

This bumps Cargo.toml, commits, tags, and pushes. CI then runs the e2e tests, builds all platforms, creates the GitHub release with binaries, and publishes the [npm packages](https://www.npmjs.com/package/shellshare).
Loading