Docs

Serverless Python on hardware you own. Decorate, run, record.

Install

roost is a Python package. The only requirement on the client is Python 3.9+; fleet nodes need Python 3 and SSH access.

# from PyPI
pip install roost

# or from source
git clone https://github.com/memjar/roost
pip install -e roost

Your first run

Write a function, decorate it with an app, and run it. roost ships the source to the target, builds the environment once, and streams the output back.

# train.py
import roost

app = roost.App("probe")
image = roost.Image.debian_slim().pip_install("numpy")

@app.function(image=image, gpu="fleet:jl1", timeout=300)
def train(epochs=5):
    import os, numpy as np
    print(f"[boot] on {os.uname().nodename}")
    return {"epochs": epochs}
roost run train.py::train '{"epochs": 5}'

Run targets

The gpu argument decides where a function executes. roost never hides the host — a fleet run records the node it ran on.

gpu=None

Runs in an isolated subprocess on this machine. Zero network. Good for local iteration.

gpu="local"

Same as None — explicit local execution.

gpu="fleet:jl1"

Ships the call to node jl1 over SSH, provisions the venv, runs, and copies the result back.

.map() / .spawn()

Fan work across the fleet, or detach a call and collect it later with .get().

Primitives

Image

roost.Image.debian_slim().pip_install(...) describes an environment. It hashes to a content-addressed digest that names a cached venv on each node — build once, reuse forever.

Volume

roost.Volume.from_name(name) gives durable storage mounted into the function. Persist with vol.commit(). Volumes live under ~/.roost/volumes.

App & function

app = roost.App("name") then @app.function(...). For one-off scripts, @roost.function(...) binds to a default app.

The ledger

Every run writes to ~/.roost/runs/<runid>/: a meta.json (status, target, duration, host, result) plus stdout.log and stderr.log. This is the source of truth — the CLI and console both read straight from it. It's local, it's plain JSON, and it's yours.

~/.roost/runs/train-fleet-jl1-1756-4821-0/
├── meta.json      # status, target, duration_s, remote_host, result
├── stdout.log
└── stderr.log

CLI

roost run file.py::fn [json-args]   # run on the fleet
roost runs -n 20                     # list the ledger
roost logs <runid>                  # meta + stdout/stderr
roost serve --port 8787            # the console + JSON API

The console

Run roost serve and open the console. It serves a dashboard and a JSON API over your local ledger — per-call telemetry, live metrics, streamed logs, and full run detail. Nothing leaves your machine.

The console at roost.computer/console.html auto-connects to http://127.0.0.1:8787. Point it elsewhere with the endpoint field or ?api=. Without a daemon it shows demo data so you can see the shape.

API

GET /api/runs          # the full ledger
GET /api/runs/<id>     # one run + logs
GET /api/stats         # aggregate metrics

MCP server

roost ships a Model Context Protocol server so any MCP client (Claude Code, etc.) can run functions and read the ledger. It's a native binary distributed via npx.

// mcp config
{
  "mcpServers": {
    "roost": { "command": "npx", "args": ["-y", "@glenmaura/roost-mcp"] }
  }
}

Tools: roost_run, roost_runs, roost_logs. See the package.