Metadata-Version: 2.4
Name: droneship
Version: 0.1.3
Summary: Run coding agents like Claude Code in isolated, policy-enforced cloud sandboxes
Project-URL: Homepage, https://github.com/highflame-ai/highflame-forge
Project-URL: Documentation, https://github.com/highflame-ai/highflame-forge#readme
Project-URL: Repository, https://github.com/highflame-ai/highflame-forge
Project-URL: Issues, https://github.com/highflame-ai/highflame-forge/issues
Author-email: Highflame Team <team@highflame.ai>
License: MIT
License-File: LICENSE
Keywords: agent,ai-agent,claude-code,coding-agent,developer-tools,gvisor,isolation,sandbox,security
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27.0
Requires-Dist: packaging>=23.0
Requires-Dist: pathspec>=0.12
Requires-Dist: pydantic>=2.5.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.7.0
Requires-Dist: typer>=0.9.0
Requires-Dist: websockets>=13.0
Provides-Extra: all
Requires-Dist: highflame-forge[gcp,modal,runpod,service]; extra == 'all'
Provides-Extra: dev
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: opentelemetry-sdk>=1.27.0; extra == 'dev'
Requires-Dist: pre-commit>=3.6.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest-timeout>=2.3.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.1.6; extra == 'dev'
Provides-Extra: gcp
Requires-Dist: google-cloud-aiplatform>=1.38.0; extra == 'gcp'
Requires-Dist: google-cloud-compute>=1.14.0; extra == 'gcp'
Provides-Extra: modal
Requires-Dist: modal>=1.6.1; extra == 'modal'
Provides-Extra: runpod
Requires-Dist: asyncssh>=2.23.1; extra == 'runpod'
Requires-Dist: runpod>=1.6.0; extra == 'runpod'
Provides-Extra: seccomp
Requires-Dist: pyseccomp>=0.1.2; (sys_platform == 'linux') and extra == 'seccomp'
Provides-Extra: service
Requires-Dist: cryptography>=49.0.0; extra == 'service'
Requires-Dist: fastapi>=0.137.0; extra == 'service'
Requires-Dist: highflame-policy>=2.2.28; extra == 'service'
Requires-Dist: httpx>=0.27.0; extra == 'service'
Requires-Dist: opentelemetry-api>=1.27.0; extra == 'service'
Requires-Dist: opentelemetry-exporter-otlp>=1.27.0; extra == 'service'
Requires-Dist: opentelemetry-sdk>=1.27.0; extra == 'service'
Requires-Dist: pydantic-settings>=2.14.2; extra == 'service'
Requires-Dist: pyjwt[crypto]>=2.14.0; extra == 'service'
Requires-Dist: starlette>=1.3.1; extra == 'service'
Requires-Dist: uvicorn[standard]>=0.26.0; extra == 'service'
Description-Content-Type: text/markdown

# droneship

[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Run coding agents like Claude Code in isolated, policy-enforced cloud sandboxes.**

`droneship` is a CLI for the hosted Highflame control plane. You sign in, launch
an agent in an isolated sandbox, and never put cloud-provider credentials on
your laptop. Runs on macOS and Linux.

## Install

```bash
curl -fsSL https://droneship.run/install | sh
```

The installer uses [uv](https://docs.astral.sh/uv/) (and installs it if it is
missing) to put the `droneship` binary on your PATH, usually `~/.local/bin`.
If the next prompt says `command not found`, add the line the installer
printed, or:

```bash
export PATH="$HOME/.local/bin:$PATH"
```

### Updates

`droneship` checks droneship.run for a newer release at most once an hour. When
there is one, it installs it and runs your command on the new version. Set
`DRONESHIP_NO_AUTO_UPDATE=1` to turn this off, and run `droneship update` to
update right away.

## First run

```bash
droneship login
droneship connect
droneship run claude
```

1. `droneship login` opens a browser and stores credentials for this machine.
   Account and project are fixed at sign-in; switch project by signing in
   again with `--project NAME`.
2. `droneship connect` authorizes GitHub so private repos clone. Skip it for
   public repos — `droneship run claude` will also prompt the first time it meets a
   private repo, and again if the connection lapses. `--force` replaces a
   stored connection.
3. `droneship run claude` starts Claude Code in a sandbox with a fresh clone of
   this repo's `origin` at your current branch (unless you pass `--repo` /
   `--ref`), or reattaches to the one already running for this repo (another
   repo's sandbox is never reattached; `--id` names one). Push first: uncommitted
   work and unpushed commits are not copied, and the CLI says so when you have
   either. `--new` starts another alongside it. A saved workspace resumes on
   your branch when its tree is clean; with uncommitted work it stays on the
   branch it was left on, and the CLI says which.

```bash
cd ~/src/my-repo
droneship run claude
droneship resume claude      # pick up a previous conversation
```

Arguments after `droneship run claude` are passed through to `claude`. Put `--` first
for any that droneship would otherwise read as its own.

In a sandbox Claude doesn't ask permission for each command; the sandbox is the boundary. `--ask` restores the prompts.

Claude runs inside the sandbox's tmux session, so it survives a dropped connection: detach on purpose with `~.` (ssh's escape, at the start of a line) and `droneship run claude` reattaches to the same agent. `run claude` exits with tmux's status, not Claude's. tmux has mouse mode on, so a drag selects inside tmux; hold Shift while dragging to select with your terminal instead.

On a machine with no browser (typically SSH into a dev box):

```bash
droneship login --headless
```

### Credentials stay out of the sandbox

When Claude Code runs on Modal through the Highflame gateway (not `--direct`),
the container it runs in holds no gateway key, MCP token or GitHub token. A
separate sidecar injects them on the way out, and the GitHub token can only be
used for this sandbox's repo. Private repos get the same KERNEL isolation as
public ones, except with subscription sign-in.

## Commands

`droneship --help` lists every command.

### Account

```bash
droneship login                 # sign in through your browser
droneship login --headless      # print a code to approve from another device
droneship login --project NAME  # sign in to this project (name, slug, or id)
droneship logout                # discard this profile's stored credentials
droneship whoami                # identity, project, and which control plane
droneship connect               # GitHub, so private repos work
```

### Sandboxes

```bash
droneship run claude                   # Claude Code; reattaches if one is running
droneship run claude --new             # another sandbox, alongside the first
droneship run claude --id ID           # attach to a specific sandbox
droneship run claude --fresh           # discard this repo's saved workspace first
droneship run claude --ephemeral       # save nothing when the sandbox goes
droneship run claude -L 3000           # forward a sandbox port while you work
droneship run claude -p "<prompt>" -d  # run one prompt in the background
droneship resume claude                # pick up a previous conversation
droneship resume claude ID             # continue a stopped prompt run
droneship shell                        # interactive shell (your only sandbox)
droneship shell --id ID pwd            # one-off remote command
droneship shell -L 3000                # forward a sandbox port to this machine
droneship list                         # running sandboxes, with repo@branch (-v: every column)
droneship list -a                      # include stopped ones you can resume
droneship logs [-f] ID                 # its output, live or saved
droneship kill ID                      # shut one down; its saved workspace stays
droneship diff [--stat]                # what the agent changed: commits + uncommitted work
droneship pull                         # fetch it into a local branch, droneship/<branch>
droneship pr                           # push the sandbox's branch and open a PR
droneship workspace list [-v]          # saved workspaces (outlive the sandbox); -v adds Name
droneship workspace delete [NAME]      # permanently delete a saved workspace (this repo's when omitted)
```

`list` fits its columns to the terminal; scripts should read `droneship list --json`.

The agent's work comes back through git. Run these in your checkout of the
repo. `droneship diff` shows everything that isn't on GitHub yet: the sandbox's
commits since `origin/<branch>` (or `--base REF`), plus its uncommitted and
untracked files. On a terminal it opens in your pager, and
`droneship diff > x.patch` gives a plain patch. `droneship pull` fetches the
same work into the local branch `droneship/<branch>`, with any uncommitted
changes as one extra commit. It never touches your working tree or current
branch. Review with `git log`, then `git merge --ff-only droneship/<branch>`.
If you have committed on `droneship/<branch>` yourself, a later pull refuses
to replace it unless you pass `--force`.
`droneship pr` pushes the sandbox's branch from inside the sandbox. It refuses
if there are uncommitted changes or the branch is the default one. It then
opens the PR with your local `gh` if that is signed in, or prints GitHub's
compare page and opens it in your browser.

Prompt runs (`-p`) are one-off Claude Code tasks in a new sandbox that shuts down when done. Without `-d`, the CLI streams output and exits with the agent's exit code; with `-d`, it detaches (Ctrl-C also detaches without stopping the run). Stopped runs stay in `droneship list -a` for 7 days, as long as the snapshot exists, and can be resumed via `droneship resume claude ID`. Each prompt run gets a fresh clone of the current repo at the current branch (uncommitted local changes are not included) and never uses your saved workspace, so it can run while you work in your own sandbox on the same repo; to keep its changes, resume it and push. Prompt runs get no MCP tools yet. Modal backend only. Billed key-free through the AI gateway or with your own key (`--byok`); not with `--subscription` or `--direct`. A resumed run keeps the run's egress policy, isolation and time limit, and bills key-free (`--byok` keys are never stored). The model credential lives in a separate sidecar container, so the agent and its snapshot never hold it. A private repo's GitHub token is the exception: the agent needs it to push, so it stays in the sandbox. `droneship kill ID` on a stopped run deletes it and its snapshot; if the snapshot can't be deleted, it expires within 7 days.

Other agents wired today:

```bash
droneship run codex
droneship run python --entrypoint agent.py
droneship run langgraph
```

`python` and `langgraph` run **your** agent. The repo comes from your
checkout, its dependencies are installed at boot, and inference is governed
the same way as every other harness — with no Highflame-specific code in it.

See `droneship run --help` for the full agent list, egress bundles
(`--policy-bundle`), isolation floor (`--isolation`), and billing flags
(`--subscription`, `--byok`, `--key-free`, `--direct`). Names that are not
wired yet fail rather than provision a broken sandbox.

## Configuration

The CLI talks HTTPS to the control plane. It does not take cloud-provider
credentials. Optional overrides:

```bash
# Which stored login to use (also: droneship --profile NAME …)
DRONESHIP_PROFILE=default

# Set at login if you are not on the default deployment (api.highflame.ai)
DRONESHIP_API_URL=https://api-dev.highflame.dev
DRONESHIP_AUTH_URL=https://studio-dev.highflame.dev
```

`--api-url` and `--auth-url` live on `droneship login` only. Switching
deployments is a re-login, so a token minted against one environment is never
sent to another.

## Development

```bash
git clone https://github.com/highflame-ai/highflame-forge.git
cd highflame-forge
uv sync --extra dev

uv run pytest tests/
uv run mypy src/highflame_forge
uv run ruff check src/
```

## License

MIT License — see [LICENSE](LICENSE) for details.
