Skip to content

feat(sandbox-daytona): Daytona sandbox provider worker#93

Open
rohitg00 wants to merge 5 commits into
mainfrom
sandbox-daytona
Open

feat(sandbox-daytona): Daytona sandbox provider worker#93
rohitg00 wants to merge 5 commits into
mainfrom
sandbox-daytona

Conversation

@rohitg00
Copy link
Copy Markdown
Contributor

@rohitg00 rohitg00 commented May 7, 2026

Member of the sandbox worker family. Read sandbox-CONTEXT.md (lands via #92) for family terminology and ABI contract.

What ships

sandbox-daytona/ — narrow Rust iii worker wrapping Daytona sandboxes. Live verified end-to-end against app.daytona.io/api.

Functions

Function Status
sandbox::daytona::create wired — POST /sandbox body {snapshot?, autoStopInterval} (autoStopInterval is minutes, rounded up from idle_timeout_secs)
sandbox::daytona::exec stub — Daytona exec is async via /sandbox/{id}/process; streaming wiring is a follow-up
sandbox::daytona::stop wired — DELETE /sandbox/{id}; 200 / 404 / 409 all treated as idempotent success
sandbox::daytona::list wired — GET /sandbox; reconciles in_flight
sandbox::daytona::snapshot / expose_port / fs::* stub

Capabilities advertised: ["snapshot","expose_port","fs"].

Daytona-specific bug found live

Daytona returns 409 Conflict "Sandbox state change in progress" when a stop races their async deletion (different from E2B's 404). This PR's stop treats 409 as idempotent success alongside 404 — the post-state is what the caller wanted either way. Pinned by stop_treats_409_as_success in the test suite.

Live verification cycle

1. CREATE image=default idle_timeout_secs=600 → real sandbox booted
2. LIST → reconciled, in_flight=1
3. STOP → {}
4. STOP same id (409 mid-delete) → {}        ← the bug fix
5. STOP a third time (404) → {}
6. STOP random uuid (404) → {}
7. Upstream account drains to zero

Tests

tests/integration.rs — 10 wiremock cases mirroring the e2b suite plus the Daytona-specific 409 case. cargo test, clippy, fmt clean.

Pin

iii-sdk = "=0.11.6".

rohitg00 added 4 commits May 7, 2026 11:26
Mirrors the sandbox::e2b::* ABI under sandbox::daytona::*. Same 8 functions
(create, exec, stop, list, snapshot, expose_port, fs::read, fs::write),
same S-code error space, same concurrency tracking. HTTP call bodies are
stubbed pending a verified pass against Daytona's REST surface.

Auth header: Authorization: Bearer DAYTONA_API_KEY. Default base URL:
https://app.daytona.io/api.

Tests pass; clippy clean; fmt clean. Part of the sandbox-as-worker family.
Aligns the handler surface with the sandbox-e2b structural fixes so
every Rust sandbox provider exposes the same self-healing list
contract. Specifically:

- `do_list` now reconciles the in-flight counter against the upstream
  count when the client succeeds, and falls back to the local counter
  when the provider is unreachable. The response gains a `reconciled`
  boolean so callers can tell the two cases apart.
- `SandboxRecord.started_at` switches from i64 to a pass-through
  RFC3339 String. Matches the e2b shape; spares us a bespoke ISO
  parser and removes a heap of clippy noise.

The Daytona REST client itself is still stubbed pending live
verification with a real DAYTONA_API_KEY. The 404-idempotent stop and
`timeout` body field will be wired with the same pattern the e2b
client now ships once the credentials land. The existing wiremock
suite (5 cases) keeps passing.
Three behaviour changes verified live against app.daytona.io/api:

1. `create` POSTs to /sandbox with body
   {snapshot?, autoStopInterval}. `autoStopInterval` is in MINUTES on
   Daytona's surface — not seconds — so we round up from
   idle_timeout_secs. `image` falls through to Daytona's default
   snapshot when the caller passes 'default' or empty. Response shape
   is {id, snapshot, state, createdAt, ...}; we map id → sandbox_id,
   snapshot → image.

2. DELETE /sandbox/{id} treats 200, 404, AND 409 as success. 409
   'Sandbox state change in progress' is Daytona-specific: the
   platform deletes asynchronously, so a second stop racing the
   cleanup hits a sandbox in a transitional state. From the caller's
   perspective the post-state is what they wanted, so we surface
   success and release the in-flight slot. The wiremock suite gains
   an explicit regression test covering this path.

3. GET /sandbox returns the array we feed into list reconciliation.
   The response includes sandboxes mid-cleanup (state: destroying,
   destroyed, etc.), which is intentional — those slots still consume
   account resources until Daytona finishes their teardown, so the
   reconciled in_flight reflects them.

Live verification cycle: create → list (reconciled, in_flight=1) →
stop (200) → stop again (409, treated as success) → stop a third
time (404, treated as success) → stop random uuid (404, treated as
success). Upstream account drains to zero.

Tests: 10 wiremock-backed cases now mirror the e2b suite plus a
Daytona-specific 409 case. clippy + fmt clean.
…ng engine

Verified the register_function/register_function_with handler signatures
are unchanged between 0.11.3 and 0.11.6 (handler closure stays `Fn(R) ->
Result<O,E>` — single-arg, no engine-supplied ctx; our HandlerCtx is
captured by the closure, not an SDK parameter). All wiremock tests
still pass. Pin moves to whatever the engine actually ships.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Warning

Rate limit exceeded

@rohitg00 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 23 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fddb336d-4f23-4933-9d0c-e567ae3e4fa6

📥 Commits

Reviewing files that changed from the base of the PR and between 2e53b44 and bfc1a6d.

⛔ Files ignored due to path filters (1)
  • sandbox-daytona/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • sandbox-daytona/Cargo.toml
  • sandbox-daytona/README.md
  • sandbox-daytona/iii.worker.yaml
  • sandbox-daytona/src/client.rs
  • sandbox-daytona/src/config.rs
  • sandbox-daytona/src/handler.rs
  • sandbox-daytona/src/lib.rs
  • sandbox-daytona/src/main.rs
  • sandbox-daytona/tests/integration.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sandbox-daytona

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rohitg00 rohitg00 requested a review from andersonleal May 7, 2026 14:25
Stops shadowing the bare sandbox::* namespace. Routes through the
sandbox router worker (provider="daytona"); direct invocation stays
supported and stable.

Handler logic, tests, S-code mapping unchanged.
@rohitg00
Copy link
Copy Markdown
Contributor Author

Refactored to register only sandbox::provider::daytona::*. Caller-facing sandbox::* now owned by the new sandbox router worker in #119. Direct invocation of sandbox::provider::daytona::* stays supported.

Validation: cargo fmt --check + cargo clippy -D warnings + tests clean. Depends on #119 for end-to-end use via the router; this PR is independently mergeable if direct invocation is sufficient.

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.

1 participant