HTTP API¶
Every path below is prefixed with /api. The base URL defaults to http://localhost:8787.
Errors use a consistent shape:
The HTTP status is always the same as the upstream when the error came from a shed-server.
Hosts¶
| Method | Path | Purpose |
|---|---|---|
| GET | /hosts |
List configured shed hosts from ~/.shed/config.yaml |
Response:
Sheds¶
| Method | Path | Purpose |
|---|---|---|
| GET | /sheds |
Flat list of sheds across all hosts; unreachable hosts surface under errors[] |
| GET | /sheds/:host/:name |
Single shed (proxied from shed-server, augmented with host) |
| POST | /sheds/:host |
Create shed (streams SSE from upstream) |
| POST | /sheds/:host/:name/start |
Start |
| POST | /sheds/:host/:name/stop |
Stop |
| DELETE | /sheds/:host/:name |
Delete |
| GET | /sheds/:host/:name/sessions |
tmux sessions in the shed (each annotated with is_remote_control) |
| DELETE | /sheds/:host/:name/sessions/:session |
Kill a specific tmux session |
Create body¶
{
"name": "my-shed",
"repo": "charliek/shed-remote-agent",
"image": "base",
"backend": "firecracker",
"cpus": 2,
"memory_mb": 2048,
"no_provision": false
}
repo and local_dir are mutually exclusive. The backend validates the request shape before proxying to shed-server.
Create SSE wire format¶
POST /sheds/:host sends Accept: text/event-stream. The response is a passthrough of shed-server's SSE stream:
event: progress
data: {"phase":"image","message":"Pulling ghcr.io/charliek/shed-fc-base:v0.1.0..."}
event: progress
data: {"phase":"vm-start","message":"starting Firecracker VM"}
event: complete
data: {"name":"my-shed","status":"running","host":"localhost-dev", ...}
Errors come on event: error:
The parser in packages/shared/src/sse.ts handles : comment lines, multi-line data: concat, and trailing events without a blank line.
Remote Control¶
| Method | Path | Purpose |
|---|---|---|
| GET | /sheds/:host/:name/rc |
List RC sessions in a shed (filtered tmux sessions, probed for state) |
| POST | /sheds/:host/:name/rc |
Bootstrap a new RC session and block until a terminal state (≤ 20 s) |
| DELETE | /sheds/:host/:name/rc/:slug |
Kill the underlying tmux session |
| GET | /sheds/rc/_meta |
Debug: exposed constants (prefix, default_workdir) |
Bootstrap body (all optional)¶
Defaults: slug auto-generated (6 confusable-free chars), display_name = <shed>/<slug>, workdir = /workspace.
Bootstrap response¶
{
"slug": "abc123",
"tmux_session": "rc-abc123",
"shed_name": "my-shed",
"host": "localhost-dev",
"display_name": "my-shed/abc123",
"workdir": "/workspace",
"state": "ready",
"url": "https://claude.ai/code?environment=env_01RP..."
}
See Remote Control for the meaning of each state.
Hosts (secondary)¶
| Method | Path | Purpose |
|---|---|---|
| GET | /hosts/:host/images |
List image variants available on the shed host |
| GET | /hosts/:host/workspaces |
List local directories on the shed host (requires local_dir config) |
Repos¶
| Method | Path | Purpose |
|---|---|---|
| GET | /repos |
List repos across github.owners via gh repo list (60 s TTL cache) |
Response includes owners so the UI can distinguish "no results" from "no owners configured":
{
"repos": [
{"nameWithOwner": "charliek/foo", "description": "...", "updatedAt": "2026-04-01T00:00:00Z", "isPrivate": false}
],
"owners": ["charliek"]
}
Health¶
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Liveness check: {status, timestamp, uptime} (no /api prefix) |