Skip to content

feat(sandbox): add a boat.dev sandbox provider - #376

Merged
earakely-scale merged 2 commits into
mainfrom
edgararakelyan/boat-sandbox-provider
Oct 28, 2026
Merged

earakely-scale merged 2 commits into
mainfrom
edgararakelyan/boat-sandbox-provider

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Adds boat.dev as a built-in sandbox provider, boat, installed with the boat extra (boat-sdk 1.13, MIT).

[sandbox.providers.boat.config]
api_key = "secret:boat_api_key"   # or "env:BOAT_API_KEY"

What

  • Sandboxes: a boat sandbox is an Ubuntu 24.04 machine with Docker and Compose running, as user with passwordless sudo, so envs and agents run on it the way they do on the other VM providers' machines.
    • Each request gets the smallest boat type that covers it (small, default, large, xlarge). A larger request is refused.
    • Sandboxes are created with noEnv, so they get none of the boat account's secrets (model keys, GitHub token, environment variables), and with snapshots off, since agent-env never resumes one.
    • boat stops a sandbox at the run's timeout, and teardown deletes it with its data.
  • Ports: exposed ports are hosted on public HTTPS URLs, https://<sandbox>-<port>.on.boat.dev. They are recorded in the sandbox's labels, so get_sandbox on a freshly built provider restores them.
  • Commands: every exec runs detached and is followed to its end. A synchronous boat command stops at 600 s, while a detached one has no limit, and its output stream reconnects without a gap. A cancelled exec stops its command in the sandbox.
  • Creates: a create is idempotent across retries of a lost answer. A refused one (rate limit, no capacity) retries with a new key. A create that fails or is cancelled deletes what it made, found by a create label.
  • Files: bytes past one exec's worth, and objects a store cannot sign, go through boat's signed upload URLs. They are moved into place as root and checked by sha256.
  • Network policy: boat has no egress controls, so a policy that restricts egress is refused, as the provider contract asks. *.on.boat.dev joins the built-in egress floor, so an allowlisted workload on another provider can still reach a gateway hosted on boat.
  • Wiring:
    • _BUILTIN_SANDBOX_PROVIDERS and the dev extra;
    • the CLI's --sandbox help, AGENTS.md and config.example.toml;
    • THIRD_PARTY_NOTICES.md;
    • remote_sandbox_is_available, and the stamping and capability tables.

Website

Supported sandboxes should get a boat row, its logo, and a short section with the config above.

Tests

To come: the unit results, and the live runs on boat (smoke, agent-env run hello, an env deploy and chaos).

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 4/5

The previous fixes are addressed, but the remaining named-setting requirement must be satisfied before merging.

Fix All in CursorFindings

  1. P2 Name length is hardcoded ▶
Fix with agent prompt
### Issue 1
src/agent_env/providers/sandbox_providers/boat/config.py:97
`sandbox_name` hardcodes the random suffix length in `uuid.uuid4().hex[:8]`. The repository requires magic numbers to live in descriptive class or instance variables. Give this length a named setting so readers can understand and change it without editing the expression. This repository requirement must be satisfied before merging.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds boat.dev as a built-in sandbox provider, including commands, file uploads, hosted ports, reconnect, and cleanup.

  • Agent and environment workloads can run on boat.dev VMs.
  • Commands and file transfers work through boat VMs.
  • The boat option appears in provider setup and shared egress rules.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Exec["exec"] --> Streams["Output-stream workers"]
  Cancel["Cancel exec"] --> Calls["Separate SDK-call workers"]
  Create["Create or retry"] --> Calls
  Delete["Delete sandbox"] --> Calls
  Calls --> Boat["boat.dev"]
  Streams --> Boat
Loading

Reviews (2) · Last reviewed commit: "fix(sandbox): keep boat's cleanup off bu..." · Reviewed by Greptile

boat.dev sandboxes are Ubuntu machines with Docker and Compose running, as a
user with passwordless sudo, so they run envs and agents as the other VM
providers' machines do. The provider, `boat`, is the `boat` extra
(boat-sdk 1.13).

- A sandbox is the smallest boat type covering the request, created with none
  of the boat account's secrets (noEnv) and no snapshots, and stopped by boat
  at the run's timeout. Its ports are hosted on public HTTPS URLs and recorded
  in its labels, so get_sandbox on a fresh provider restores them.
- Every command runs detached and is followed to its end: no 600 s limit, and
  the output stream reconnects with no gap. A cancelled exec stops its command.
- A create is idempotent across retries of a lost answer; a refused one takes
  a new key. A create that failed or was cancelled deletes what it made, found
  by a create label.
- Files past one exec's worth, and objects a store cannot sign, go through
  signed upload URLs, are moved into place as root and checked by sha256.
- boat has no egress controls, so a policy that restricts egress is refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@socket-security

socket-security Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​boat-sdk@​1.13.0100100100100100

View full report

@earakely-scale
earakely-scale marked this pull request as ready for review October 28, 2026 22:01
@earakely-scale
earakely-scale requested a review from a team as a code owner October 28, 2026 22:01
@AnicetNgrt

Copy link
Copy Markdown

Thanks for taking the time to do this!

Comment thread src/agent_env/providers/sandbox_providers/boat/lifecycle.py Outdated
Comment thread src/agent_env/providers/sandbox_providers/boat/lifecycle.py Outdated
Comment thread src/agent_env/providers/sandbox_providers/boat/sandbox.py Outdated
…nections

- A create cancelled while it waits to retry deletes what an unanswered
  attempt made, found by its create label, as a create cancelled mid-call
  already did.
- Commands' output streams run on a thread pool of their own, so streams
  that fill it can't keep a stop or a delete from running.
- The Docker readiness wait is built from named attempts and interval.
- close clears the SDK client's urllib3 pool: the SDK's ApiClient has no
  close, and the end-to-end run's teardown failed on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
for key in sorted(attribution)
if attribution[key] is not None
]
return "-".join(["ae", uuid.uuid4().hex[:8], *filter(None, slugs)])[:_NAME_MAX].rstrip("-")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Name length is hardcoded

sandbox_name hardcodes the random suffix length in uuid.uuid4().hex[:8]. The repository requires magic numbers to live in descriptive class or instance variables. Give this length a named setting so readers can understand and change it without editing the expression. This repository requirement must be satisfied before merging.

Rule Used: Store magic numbers as class or instance variables with descriptive names rather than using them inline in the code. (source)

Learned From
scaleapi/scaleapi#126388

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/providers/sandbox_providers/boat/config.py
Line: 97

Comment:
**Name length is hardcoded**

`sandbox_name` hardcodes the random suffix length in `uuid.uuid4().hex[:8]`. The repository requires magic numbers to live in descriptive class or instance variables. Give this length a named setting so readers can understand and change it without editing the expression. This repository requirement must be satisfied before merging.

**Rule Used:** Store magic numbers as class or instance variables with descriptive names rather than using them inline in the code. ([source](https://app.greptile.com/scale-ai/-/custom-context?memory=002e0051-41ad-46c1-9098-47433c580150))

**Learned From**
[scaleapi/scaleapi#126388](https://github.com/scaleapi/scaleapi/pull/126388)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Cursor Fix in Claude Code Fix in Codex

@earakely-scale
earakely-scale merged commit d7748b9 into main Oct 28, 2026
25 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/boat-sandbox-provider branch October 28, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants