# Skyfleet > Remote dev machines for Claude Code. By default Claude runs on the user's laptop and uses its agent's cloud machine over SSH: the laptop keeps the user's .env, databases, MCP servers and plugins, and the agent does the building, running and previewing. With `skyfleet --remote`, Claude runs on the agent itself instead. Every agent is reachable as `.skyfleet` over plain SSH (for example `activepieces-yak-2bb.skyfleet`). The name never changes; the address behind it does, and connecting wakes a sleeping agent. Only the agent's owner can connect. ## Your working folder When `skyfleet` starts you on your laptop it opens Claude in `~/.box///` (for example `~/.box/activepieces/otter-35d/`). Its CLAUDE.md names three things; read it first: - The machine: `.skyfleet` - The worktree: `./repo` is a git worktree on the laptop, one of several sharing the environment's clone in `~/.box//.clone`. Read and edit files there with your normal file tools, and run git there (`git -C repo …`): commit, branch and push from the laptop, never on the machine. While `skyfleet` is open, every save in `./repo` reaches `~/repo` on the machine within a second, and every `ssh .skyfleet …` you run pulls the machine's changes back (lockfiles, generated code) when it finishes. `.git`, `node_modules` and build output never sync. - The user's local copy: the folder where they ran `skyfleet` (for example `~/activepieces`). When the user says "my .env", "my data" or "my setup", they mean files and services in that folder and on their laptop, not on the machine. ## Run commands on the machine Never run the project on the laptop. Run it on the machine: ssh .skyfleet 'cd repo && npm test' Long-running processes (dev servers, watchers) go in the background so the command returns: ssh .skyfleet 'cd repo && nohup npm run dev > /tmp/dev.log 2>&1 &' ssh .skyfleet 'tail -n 50 /tmp/dev.log' The machine has git, gh, node, npm, bun, docker, Chrome and Claude Code. `sudo` works without a password. ## Bring the user's files and data Copy a local-only file, such as a .env, to the machine: scp ~/activepieces/.env .skyfleet:repo/ Copy a folder: rsync -a ~/activepieces/fixtures/ .skyfleet:repo/fixtures/ Let the machine use a database running on the laptop instead of copying it (the machine sees it on its own localhost): ssh -f -N -R 5432:localhost:5432 .skyfleet Copy a database once instead: pg_dump -Fc | ssh .skyfleet 'pg_restore -d --clean' Ask before copying secrets anywhere other than the user's own agent. ## Previews Start servers on 0.0.0.0, never only 127.0.0.1. Then publish the port and get its public URL: ssh .skyfleet 'preview 4200' Give the user that https URL, never a localhost one, and check it responds before saying the app is up. Vite already allows the preview host; for other dev servers allow `*.on.boat.dev`. To use the app on the laptop's own localhost instead: ssh -f -N -L 4200:localhost:4200 .skyfleet ## Sleep and cost - An agent stays awake while Claude is open in its folder. After that it sleeps after the environment's quiet window (usually 5 minutes). Asleep costs nothing and keeps every file; processes stop. - Any `ssh .skyfleet` wakes it in a few seconds. Restart dev servers after a wake. - An agent asleep for 7 days is deleted; unpushed work is pushed to the branch `cb/` first. - `skyfleet --always-on` or the dashboard's Always on toggle keeps an agent awake, for example for a demo. ## Databases the team shares The team's integrations (Postgres read-only replicas, ClickHouse Cloud, Linear) are available through the `skyfleet` MCP server, both in this folder and on the machine. It has four tools: `search_tools` finds a tool by keyword, `describe_tool` shows its input, `call_tool` runs it, and `submit_issues` hands work to new agents (below). Credentials stay on the Skyfleet server. ## Work leaves by git Commit and push from `./repo` on the laptop, and open the PR there with `gh`. A new agent starts on its own branch, `cb/`, or on the branch the user was on. Two agents can't have the same branch checked out; make a new branch instead. ## Split work across agents When the user says "submit issues", or asks to split work across agents, hand each independent piece to its own agent. Each issue starts a new agent as the user, from this environment, with its prompt as the first message, and shows in the Inbox with where it came from (Local or Driver). Submit 1 to 10 at a time; a title you already submitted starts nothing new. - Here or on the machine: the `submit_issues` tool of the `skyfleet` MCP. - From a terminal: `skyfleet submit -e "\n<prompt>" …`, one argument per issue, its first line the title. Write each prompt so its agent can finish alone: the goal, the files, how to check it, and where the result goes (usually a pull request). ## Start an agent from a webhook A Skyfleet webhook is a URL that starts an agent. The user creates one on Integrations → Webhooks with a name, the environment agents start from, and a prompt that says what the agent should do. Skyfleet shows the URL and a secret token once. Every call starts a new agent on the team account (a team Claude seat and a GitHub App, set up on Integrations → Team account), with only the tools the webhook was given. The agent gets the prompt first, then the request body as context, and has the team's integrations through the `skyfleet` MCP, so the prompt decides where the result goes (a pull request, a Linear issue, a comment). There's no queue and no limit: each call is its own agent. curl -X POST https://skyfleet.run/api/v1/webhooks/<id> \ -H "Authorization: Bearer $BOX_WEBHOOK_TOKEN" \ -H "content-type: application/json" \ -d '{"error": "TypeError: ...", "stack": "...", "route": "/checkout"}' It answers `202 {"box": "<name>"}`. A wrong token gets 401, and a body over 256 KB gets 413. When you wire a webhook into a project: - Keep the token in an env var such as `BOX_WEBHOOK_TOKEN` (in `.env` and the production secrets), never in code or a commit. - Call it fire-and-forget: a short timeout (2 seconds), never throw, never block a request, and do nothing when the token is unset. - Dedupe on your side. Every call starts an agent, so send each problem once, for example once per error name and code location per release. - Send what the agent needs to act: the error message and stack, the route or job, the release or commit. Never send request bodies, headers, cookies, tokens or user data. Good places to call it: an unhandled error handler, a failed background job, a CI failure on the main branch (a GitHub Action step that runs `curl`), a monitoring alert's webhook, or a scheduled job asking for routine work. ## Report errors so an agent fixes them Integrations → Add on App errors (https://skyfleet.run/integrations/errors) connects a team's apps to Skyfleet. The user picks the repo agents fix (an environment, made right there if the team has none) and presses Create token. Its agents run on the team account (Integrations → Team account: a Claude seat and a GitHub App). That makes one alert source with one `sig_…` token. Add App errors again for another repo or team: each source is its own row with its own environment, prompt and token, and works until someone deletes it. Every source runs on the team account; moving it to another environment changes nothing else, and anyone on the team can. Each production service posts its uncaught errors here: curl -X POST https://skyfleet.run/api/v1/signals \ -H "Authorization: Bearer $SIGNALS_TOKEN" \ -H "content-type: application/json" \ -d '{"kind": "error", "service": "api", "message": "Cannot read properties of undefined", "stack": "TypeError: ..."}' - `service` and `message` are required. `stack` and `fingerprint` are optional. An alerting tool that also reports recoveries sends `status`: only `firing` (the default) raises an issue; anything else, such as `resolved`, is accepted and ignored. - Errors with the same fingerprint are grouped into one issue in the Inbox. By default the fingerprint is made from the service, the error type and the top in-app stack frame without line numbers. Send your own `fingerprint` to group errors differently. - An agent starts only for a new issue, or for a resolved issue that comes back. Repeats only raise the issue's count. Ignored issues never start an agent. The agent is told to find the root cause and fix it with a test that fails before the fix. It is told to open a PR only when its owner has GitHub connected, and to track the issue in Linear only when the team has Linear connected; anything else (another tracker, how fixes ship) goes in the source's prompt. - It returns `202 {"issue": "<id>", "agent": true|false}`. A wrong token gets 401, a `kind` other than `error` gets 400, and a body over 256 KB gets 413. CORS is open, so browser code can call it too. The page gives the user a setup prompt to paste into Claude Code at the root of a repo. It points here and carries `SIGNALS_URL` and the real `SIGNALS_TOKEN`. When you wire it into a project: - In every service that runs in production, catch uncaught errors and POST each one to `SIGNALS_URL` as `{"kind":"error","service":"<service name>","message":err.message,"stack":err.stack}` with "Authorization: Bearer $SIGNALS_TOKEN". - Fire and forget: a 2 second timeout, never throw, never block a request, and do nothing when `SIGNALS_TOKEN` is unset. - Add both values to `.env` and `.env.example`, and the token to the production secrets. Never commit the token. - Throw one test error per service, check it lands in the Inbox, then open a PR. ## Send alerts from your tools to agents Integrations lists App errors (above), Sentry, PostHog, Better Stack, ClickStack, Cloudflare and Axiom. Each is an alert source with one `sig_…` token that the tool posts to `SIGNALS_URL` with. The person adds it in the Skyfleet dashboard with the tool's API key, and Skyfleet makes the tool's side (webhook, notifier, alert) itself and lists what it made, so there is nothing to set up from here. A team can add the same tool more than once; each source has its own environment, prompt and token. Repeats of the same incident or alert are one Inbox issue; a recovery, an acknowledgement or a comment never starts an agent. To check the Skyfleet side without starting an agent, send a recovery: it is accepted, marks the token as used on the page, and raises nothing. ## Commands the user runs - `skyfleet` open one of your agents, or make a new one - `skyfleet new` always make a fresh agent - `skyfleet <name>` open that agent - `skyfleet --remote` Claude runs on the agent instead of the laptop - `skyfleet -e <environment>` pick the environment; `--size small|medium|large`; `--always-on` - `skyfleet ls` list your agents, their state and whether Claude runs on the laptop or the agent - `skyfleet code [environment]` open that environment's agents in one VS Code window, each agent its own git worktree, and keep saves synced while it's open - `skyfleet submit "<issue>" …` start one agent per issue (first line = title); `-e <environment>` - `skyfleet rm <name>` delete an agent and its folder here (unpushed work goes to `cb/<name>`) - `skyfleet login` sign in Dashboard: https://skyfleet.run ## Links - [Docs](https://skyfleet.run/docs/): start an agent, connect error and alert tools, self-host - [Quickstart](https://skyfleet.run/docs/quickstart): the one-prompt setup - [Full docs as text](https://skyfleet.run/llms-full.txt) - [Security](https://skyfleet.run/security): what the agent can read and how access stays narrow - [Home](https://skyfleet.run/): what Skyfleet does - [Fix production errors](https://skyfleet.run/use-cases/app-errors): every new error gets a ticket and a PR - [Investigate on-call alerts](https://skyfleet.run/use-cases/alerts): each alert gets a cause, a ticket and a fix - [Hand off bugs in Linear](https://skyfleet.run/use-cases/linear-tickets): mention the agent on a ticket - [Integrations](https://skyfleet.run/docs/integrations): error, alert, log and ticket tools it connects to - [Self-hosting](https://skyfleet.run/docs/self-hosting/install): run it on your own server