Skip to content

feat(box): restore image-keyed warm-pool reuse and boot path#755

Merged
DorianZheng merged 1 commit into
mainfrom
feat/box-image-warm-pool-reuse
Jun 13, 2026
Merged

feat(box): restore image-keyed warm-pool reuse and boot path#755
DorianZheng merged 1 commit into
mainfrom
feat/box-image-warm-pool-reuse

Conversation

@DorianZheng

@DorianZheng DorianZheng commented Jun 13, 2026

Copy link
Copy Markdown
Member

Rename BoxService.createFromTemplate -> create; remove the vestigial Template/savedImage concept.

Re-introduce image as a plain box/warm-pool match key; restore warm-pool reuse (fetchWarmPoolBox/assignWarmPoolBox) in create().

Rebuild a snapshot-free CREATE_BOX boot path (runnerAdapter.createBox + box-start UNKNOWN handler).

Unify the create-box image field to 'image' across the TS API, Go runner, and swagger; fold schema into the baseline migration.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added image field support for box configuration and specification.
  • Refactor

    • Updated box creation flow to utilize image-based configuration instead of template-based approach.
    • Refactored warm pool functionality to work with image specifications.
    • Updated API schemas and request payloads to reflect image field usage.

Rename BoxService.createFromTemplate -> create; remove the vestigial Template/savedImage concept.

Re-introduce image as a plain box/warm-pool match key; restore warm-pool reuse (fetchWarmPoolBox/assignWarmPoolBox) in create().

Rebuild a snapshot-free CREATE_BOX boot path (runnerAdapter.createBox + box-start UNKNOWN handler).

Unify the create-box image field to 'image' across the TS API, Go runner, and swagger; fold schema into the baseline migration.
@DorianZheng DorianZheng requested a review from a team as a code owner June 13, 2026 09:46
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR migrates box creation infrastructure from template/snapshot fields to a unified image-based field throughout the API, services, database schema, and runner implementations. Controllers now invoke a single create() service path supporting warm-pool reuse, BoxStartAction can bootstrap UNKNOWN boxes via the runner adapter's new createBox() method, and the database schema aligns WarmPool columns with the image field.

Changes

Template-to-Image Field Migration

Layer / File(s) Summary
Database schema and entity updates
apps/api/src/box/entities/box.entity.ts, apps/api/src/box/entities/warm-pool.entity.ts, apps/api/src/box/entities/warm-pool.entity.spec.ts, apps/api/src/migrations/1741087887225-migration.ts
Box entity gains an image field with database index; WarmPool entity replaces template property (mapped to savedImage column) with direct image column, migration updates both DDL and index definitions.
DTO contract updates
apps/api/src/box/dto/create-box.dto.ts, apps/api/src/box/dto/box.dto.ts, apps/api/src/box/dto/workspace.deprecated.dto.ts, apps/api/src/box/services/box-warm-pool.service.ts, apps/libs/runner-api-client/src/models/create-box-dto.ts
CreateBoxDto, BoxDto, and WorkspaceDto expose optional image fields; warm pool service and runner API client DTO rename snapshot to image as required field.
Runner adapter interface and implementations
apps/api/src/box/runner-adapter/runnerAdapter.ts, apps/api/src/box/runner-adapter/runnerAdapter.v0.ts, apps/api/src/box/runner-adapter/runnerAdapter.v2.ts
RunnerAdapter interface declares new createBox(box, metadata?) method; V0 adapter forwards to API client, V2 adapter enqueues a CREATE_BOX job, both return daemon version or undefined.
Warm pool service image-based matching
apps/api/src/box/services/box-warm-pool.service.ts
FetchWarmPoolBoxParams uses image field; all warm-pool lookups, candidate-box queries, and event handlers filter/count by box.image = warmPoolItem.image.
Box service creation flow
apps/api/src/box/services/box.service.ts
Default resource values (CPU/memory) reduced; unified create() method captures image from DTO, attempts warm-pool reuse unless skipped, and assigns image to fresh boxes; createFromTemplate() removed in favor of this consolidated path.
BoxStartAction UNKNOWN state handling
apps/api/src/box/managers/box-actions/box-start.action.ts, apps/api/src/box/managers/box-actions/box-start.action.spec.ts
UNKNOWN boxes now attempt creation on READY runners via createBox() when image is present, or transition to ERROR if image is missing; test coverage validates both scenarios.
Box and workspace creation controllers
apps/api/src/box/controllers/box.controller.ts, apps/api/src/box/controllers/workspace.deprecated.controller.ts, apps/api/src/boxlite-rest/boxlite-box.controller.ts
All box creation entry points switch from createFromTemplate() to create(), invoking the unified service flow.
API scope and response mapping
apps/api/src/boxlite-rest/boxlite-me.controller.ts, apps/api/src/boxlite-rest/dto/box-response.dto.ts, apps/api/src/boxlite-rest/mappers/box-to-box.mapper.ts
Default scope list removes template:* entries; image field description updated in response DTO; mappers populate image from box.image instead of hardcoded defaults.
Go runner implementation updates
apps/runner/pkg/api/dto/box.go, apps/runner/pkg/api/docs/docs.go, apps/runner/pkg/api/docs/swagger.json, apps/runner/pkg/api/docs/swagger.yaml, apps/runner/pkg/boxlite/client.go, apps/runner/pkg/boxlite/stubs.go
Swagger docs and Go DTOs require Image instead of snapshot; boxlite client passes Image to runtime; recovery stub uses Image: "alpine:latest" for box recovery.
Supporting comments and formatting
apps/api/src/box/services/job-state-handler.service.ts, apps/api/src/box/services/runner.service.ts, apps/dashboard/src/components/admin/AdminTelemetryDrawer.tsx, apps/dashboard/src/components/ui/sheet.tsx
TODO comments updated to reflect image subsystem; dashboard component formatting adjusted for readability.

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • law-chain-hot
  • G4614

🐰 A migration hops along with care,
From templates past to images fair,
Warm pools now match by sight, not name,
CreateBox debuts in the game,
The schema's fresh, the services sing! 🎭

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main objective: restoring image-keyed warm-pool reuse and re-introducing the boot path, which aligns with the core changes across service, entity, and adapter layers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/box-image-warm-pool-reuse

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

@DorianZheng DorianZheng enabled auto-merge June 13, 2026 09:52
@DorianZheng DorianZheng disabled auto-merge June 13, 2026 09:52
@DorianZheng DorianZheng merged commit 6f96da3 into main Jun 13, 2026
29 of 31 checks passed
@DorianZheng DorianZheng deleted the feat/box-image-warm-pool-reuse branch June 13, 2026 09:52
G4614 added a commit that referenced this pull request Jun 13, 2026
#755

#755 edited the squashed baseline file in-place to rename
warm_pool.savedImage → warm_pool.image and add box.image, and a pre-#736
stack additionally dropped box.boxId via 0e6b875. TypeORM keys the
ledger by class name, so Tokyo's recorded Migration1741087887225 row
will never re-run with the new content — producing the live errors

  column WarmPool.image does not exist
  column Box.boxId does not exist

on POST/GET/DELETE /boxes.

This new class adds box.boxId (NOT NULL char(12), backfilled with a
12-hex slice of uuid_generate_v4 so SET NOT NULL holds), box.image
(nullable), and warm_pool.image (renamed from savedImage if present,
otherwise added). All steps are wrapped in information_schema guards so
the file is a no-op on fresh stacks and on any stack that converged via
another path.

Local-verified on a legacy-shape DB (box without boxId/image, warm_pool
with savedImage) and re-run for idempotency.
pull Bot pushed a commit to CrazyForks/boxlite that referenced this pull request Jun 13, 2026
…ite-ai#758)

## Summary

main already accepts a free-form `image` string on box creation (landed
in boxlite-ai#755). **This PR adds only the curated allowlist on top** — the
pre-launch security gate.

```text
main (boxlite-ai#755):   create accepts ANY image string  →  runner pulls it
this PR:       create validates image against the supported pinned set
               (base / python / node, sha256-pinned ghcr refs)
               unsupported  → 400 listing the supported refs
               undefined    → defaults to the base image
```

Without this gate, a request can make the runner pull an arbitrary image
using its private-registry token — so the allowlist is required before
launch.

## Changes (server-side only)

- `apps/api/src/box/constants/curated-images.constant.ts` —
`supportedImages()` / `assertSupportedImage()`; env-overridable
(`BOXLITE_SYSTEM_{BASE,PYTHON,NODE}_IMAGE`) with sha256-pinned
fallbacks. + spec.
- `box.service` create: validate `createBoxDto.image` at the request
boundary.

## Scope boundary (what this PR is NOT)

- **No client regen** — the `image` field shape is unchanged (still a
string), only its accepted values are restricted server-side.
- **No SDK / dashboard / identity changes.** The single-id collapse and
the SDK removal are separate PRs.

## Verification

API jest 106/106 (curated allowlist spec 5/5).


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
  * Added comprehensive test coverage for image allowlist validation.

* **New Features**
* The API now validates container image requests against a curated
allowlist. Unsupported images are rejected with error messages listing
available options.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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