Skip to main content

droneship

Python 3.12+ License: 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

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

The installer uses 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:

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

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.
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):

droneship login --headless

Credentials stay out of the sandbox

When Claude Code or Codex runs on Modal through the Highflame gateway (not --direct), the container it runs in holds no gateway key, your own API 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.

MCP servers

To give the agent MCP servers, commit a .mcp.json to the repo, as you would for Claude Code on your laptop. The sandbox clones it with the code.

  • In an interactive session, Claude Code asks you to approve the repo's servers once.
  • Prompt runs (-p) use them without asking: the sandbox trusts the checkout, so a prompt run starts every server the repo's .mcp.json names, including commands, before anyone reviews them. Run -p only on repos and branches you trust, not on fork PRs or third-party repos.
  • Each server's host must be on the sandbox's egress allowlist, or the server fails to connect.
  • Never put keys or tokens in .mcp.json: it is committed and cloned into every sandbox. There is no safe way yet to give a server its own API key; a key you paste into the sandbox stays in it, so use a short-lived, narrowly scoped key if you must.

claude mcp add inside a sandbox works for that sandbox only. Your laptop's MCP settings are not copied in.

Commands

droneship --help lists every command.

Account

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

Claude Code routines

Run Claude Code routines in Droneship sandboxes. Needs a Claude Team or Enterprise organization with self-hosted environments turned on, and a Droneship plan other than Free or Trial. Create one in claude.ai → Admin settings → Cloud environments, copy its key, then:

droneship routines enable      # paste the environment key (or pipe it in)
droneship routines             # is it connected?
droneship routines disable

Routines, claude.ai/code sessions and claude --cloud sessions sent to that environment then run here, one sandbox per session, and show up in droneship list. A sandbox ends soon after its session goes idle; a reply resumes the session in a new one. Git goes through Anthropic, so no git credentials are in the sandbox, and inference goes straight to Anthropic on the routine owner's plan. --policy-bundle sets what the sandboxes may reach (default baseline-secure); run enable again and press Enter at the key prompt to change only that.

Sandboxes

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 use the MCP servers in the repo's own .mcp.json (see MCP servers). Modal backend only. Billed with your own key through the AI gateway: with ANTHROPIC_API_KEY set in your shell, the run uses it (no --byok needed); with no key, the CLI stops before it starts a sandbox. Not with --subscription or --direct. A resumed run keeps the run's egress policy, isolation and time limit, and uses your key again (keys are never stored), or your plan with --sub. 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:

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, --no-byok, --direct). With no billing flag, a -p run of claude or codex uses your exported provider key (ANTHROPIC_API_KEY or OPENAI_API_KEY). An interactive launch in a terminal signs in to your plan inside the sandbox; add --byok to use your key there, as you must with no terminal. 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:

# 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://auth-dev.highflame.dev

--api-url and --auth-url live on droneship login only. A token minted against one environment is never sent to another. Keep one profile per deployment (droneship --profile dev login --api-url ...): login refuses to move a signed-in profile to a different deployment unless you pass --force.

DRONESHIP_AUTH_URL / --auth-url is the AuthN issuer (https://auth.highflame.ai by default). A profile, variable, or flag that still names a Studio origin (studio.highflame.ai, studio-dev.highflame.dev, localhost:3000) is mapped to its AuthN issuer automatically, so existing logins keep working.

Development

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 for details.

Metadata

Release files for droneship 1.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for droneship 1.6.0
File Interpreter ABI Platform
droneship-1.6.0-py3-none-any.whl Python 3 none any Details

Release files / droneship-1.6.0-py3-none-any.whl

Download URL droneship-1.6.0-py3-none-any.whl
Size 640.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
18b428ab7a0fb6bc6d077187539937a5262425c9d024e1f8ff81d0e4735491b4
BLAKE2b-256 checksum
How to use checksums
e8c616183354ae5ae247d2d2e135e022b93b072e443a8c96aa69da85b0e55bb6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for droneship 1.6.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 1 file in this release has been uploaded with provenance metadata.

Attestation for droneship-1.6.0-py3-none-any.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page