feat(box): image pipeline + single box identity (drop legacy boxId)#753
feat(box): image pipeline + single box identity (drop legacy boxId)#753law-chain-hot wants to merge 13 commits into
Conversation
Minimal image rebuild after the box_template/catalog deletion: a hardcoded
allowlist of 3 curated images, no DB table. Runner pulls any OCI ref directly
(runtime.Create(artifactRef)), so the catalog lives in code.
- curated-images.constant.ts: keys 'base'|'python'|'node' -> ghcr digest refs
from BOXLITE_SYSTEM_{BASE,PYTHON,NODE}_IMAGE env (already set in sst.config),
resolveCuratedImageRef() validates at the API boundary (400 if off-list)
- create-box.dto (internal) gains image?; mapper now threads dto.image (was dropped)
- box.service.createBoxInternal: resolve ref, stash on box.labels['boxlite.io/image-ref']
(zero migration)
- runnerAdapter.createBox (v2): enqueue CREATE_BOX job with hand-built payload whose
key is artifactRef (NOT the generated client's snapshot field — V2 Go validate trap)
- box-start.action: read the label, call createBox, set CREATING; existing
handleCreateBoxJobCompletion drives CREATING->STARTED
api/src tsc 0. Contract = opaque keys (user can't pass arbitrary OCI ref). Images
stay private ghcr (runner token already wired). Dashboard 3-image picker is a
separate follow-up (mid-rebuild).
…ho curated key
Three follow-ups on the curated-image create path:
- replaceLabels must not let a user-supplied label overwrite the reserved
boxlite.io/image-ref label: it holds the resolved curated OCI ref the
runner pulls, so overwriting it would escape the curated allowlist and
make the runner pull an arbitrary image with its private-registry token.
- createForWarmPool must stash the default curated image ref on the same
reserved label; without it warm-pool boxes ERROR on start ("missing
image ref") and the refill loop recreates them indefinitely.
- Box responses now echo the curated key the box was created with
(reverse-mapped from the reserved label) instead of a hardcoded '';
the shared REST contract documents that hosted deployments restrict
image to the curated keys.
Unit tests cover the allowlist boundary (invalid key -> 400), the
CREATE_BOX payload contract (artifactRef key, never snapshot), the
reserved-label guard, the warm-pool default, and the create/echo mapping.
The API now accepts only curated image keys (base|python|node) on box create. Switch the suite's image literals from alpine:3.23 to the 'base' key; bootstrap maps the curated env overrides to the same public refs the suite pulled before, so runner-side behavior is unchanged. Drop fixture_setup's snapshot registration: it targets the /snapshots endpoint and snapshot table that the box_template removal deleted (bootstrap hard-fails on main today). e2e-stack has been red on main since that removal; this adaptation is a precondition for it to go green again.
…ner contract to ociImageRef The curated image key now lives on the Box entity (image column, default 'base') instead of hiding a resolved OCI ref in a reserved label. The key resolves to a pinned ref at the last point before the payload leaves the API: RunnerAdapterV2.createBox. This removes the replaceLabels sanitization (the attack surface is gone with the label) and the ref->key reverse lookup in the REST mapper, and survives image digest rotation since the DB stores the stable key. Runner contract changes (atomic with the adapter payload): - dto.CreateBoxDTO: ArtifactRef -> OCIImageRef (json ociImageRef) - drop dead fields GpuQuota and Registry; relax userId/osUser (unread in the create flow, previously validate:required) - API payload drops boxId/gpuQuota/authToken (unread by the runner) createBox(box, artifactRef) collapses to createBox(box).
…key translation layer - image is now a full pinned OCI ref at every layer: create request -> Box.image column -> CREATE_BOX payload -> runner runtime.Create. The curated allowlist shrinks to a thin boundary gate (supportedImages / assertSupportedImage) designed to be deleted when custom images land; rejections list the supported refs so callers can self-correct. - rename the runner contract field ociImageRef -> image (Go: Image, json "image"), aligning with the embedded SDKs' image parameter. - restore boxId in the CREATE_BOX payload so runner logs show the user-facing short id instead of the internal uuid. - e2e: stack-aware default image — fixture_setup records the local stack's base image in the credentials profile; conftest resolves BOXLITE_E2E_IMAGE env > profile default_image > pinned ghcr fallback. - drop WorkspaceDto's redundant image declaration (now inherited from BoxDto).
…-char box id A box now has exactly one identity: the 12-char base62 public id, minted in-memory at construction and used as the primary key, in CREATE_BOX payloads, as the engine VM name, in runner logs, and in every API response. The internal uuid (a Daytona leftover) is gone — it offered neither the small-ordered-key benefit nor the ABA protection that justify a dual-id scheme, and it kept leaking into runner logs and the in-box BOXLITE_BOX_ID env. - Box entity: id = generateBoxId(); the boxId column, its unique and org-scoped indexes, and the uuid default are removed (baseline migration updated; pre-launch DB rebuild). - Lookups collapse: findOneByIdOrName / getOrganizationId lose the boxId-first branch; lookup caches lose the by-box-id variants. - CREATE_BOX payload sends a single id; the Go DTO drops BoxId and client.go drops the publicBoxId fallback. - Admin overview mirrors id into the legacy boxId DTO field; API surface is unchanged. API jest: 112/112. Go pkg build+vet+gofmt clean (cmd link needs the prebuilt libboxlite.a absent in this worktree). Dashboard tsc clean.
…ents (#744) ## Summary Stacked on #735. The Box entity's identity collapsed there into a single 12-char id; the `boxId` fields on `BoxDto` / the admin overview DTO were kept only as value mirrors of `id` to avoid touching the API contract and generated clients in that PR. This PR removes the mirror everywhere: ```text before (after #735) after (this PR) ─────────────────── ─────────────── entity/DB: id ✅ single unchanged BoxDto: id + boxId (same id only value, legacy shape) admin DTO: id + boxId mirror id only clients: Box/AdminBoxItem/ boxId field gone (TS + Go) Workspace carry boxId dashboard: reads .boxId ?? .id reads .id ``` - API: `BoxDto.boxId`, admin overview `boxId`, dual-id correlation matching in admin observability — all removed. - Dashboard: `box-identity` helpers read `.id`; dual-id cache updates and the uuid-shaped-name special case removed (after the DB rebuild a box name can never be a uuid). - Generated clients: regenerated with the repo's own codegen **inside a disposable BoxLite box** (CI-faithful: Linux + JDK + GNU sed, no host gofmt drift). Drift is exactly the `boxId` removal on the `Box` / `AdminBoxItem` / `Workspace` models in the TS and Go clients. - Untouched on purpose: route/query parameter names like `:boxId` (they carry the single id), other tables' `boxId` FK column names, and the analytics `ModelsBoxUsage.boxId` (a real data field, not the mirror). ## Verification - API jest: 38 suites / 112 tests pass - Dashboard: `tsc --noEmit` clean against the regenerated client - Regen ran in a BoxLite box (`boxlite run node:22-bookworm`); output is byte-for-byte what CI's api-client-drift check regenerates ## Merge order #743 (remove TS cloud SDK) → #735 (image pipeline + single box identity) → this PR.
…ne name slot The control-plane box id rides in the engine's name slot (pre-#747 behaviour); the engine still mints its own internal id. User-facing names live only in the cloud's PG, so the engine never needs a separate user name in cloud mode. Removes the BoxOptions.external_ref field and its full SDK/FFI/CLI plumbing — keeping the runtime contract minimal. Engine lib 734/734, boxlite-c 55/55, go vet clean.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
🚧 Files skipped from review as they are similar to previous changes (7)
📝 WalkthroughWalkthroughThe PR consolidates box identity from a dual UUID/boxId model to a single 12-character base62 ChangesBox identity and image contract consolidation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
apps/api-client-go/api/openapi.yaml (4)
9938-10002:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
idis still documented as a UUID in both public box schemas.The PR objective says the UUID/legacy-
boxIdsplit is gone andidis now the single 12-character base62 identifier. These descriptions and examples still say "internal UUID", so the generated docs and example payloads will contradict the new contract.Update the inherited `PaginatedBoxes` item examples too so the example set stays internally consistent.🛠️ Proposed OpenAPI fix
- id: fd955d93-e74a-48e7-9f2d-fcbe6dd9e920 + id: A1b2C3d4E5f6 @@ id: - description: The internal UUID of the box - example: fd955d93-e74a-48e7-9f2d-fcbe6dd9e920 + description: The public box identifier + example: A1b2C3d4E5f6 type: stringAlso applies to: 12487-12552
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api-client-go/api/openapi.yaml` around lines 9938 - 10002, The OpenAPI docs still describe the Box "id" as an internal UUID and use a UUID example; update the Box schemas and any public-box schema examples (the schema that defines properties.id and the example block under "example: id") to describe "id" as the 12-character base62 identifier and replace the UUID example with a representative 12-char base62 value (e.g., a-zA-Z0-9 string of length 12); also update the PaginatedBoxes example set so its box examples use the same 12-char base62 id and adjusted description text for properties.id to match the new contract.
13450-13472:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftDon't overload
organizationIdwith the box public ID.The stack context says
AdminBoxItem.organizationIdis only becoming optional in the generated contract. Re-describing and re-exampleing that field as "Public box ID shown to users" changes its meaning for every generated client and leaks the same mismatch into the observability investigate response.If admin views need a separate user-facing box identifier, add that as its own optional field and keep
organizationIdreserved for the organization foreign key. TheAdminObservabilityInvestigateResponse.boxes[]examples should then be updated to match the correctedAdminBoxItemshape.Also applies to: 14788-14804
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api-client-go/api/openapi.yaml` around lines 13450 - 13472, The OpenAPI spec incorrectly repurposes AdminBoxItem.organizationId as a user-facing public box ID; revert organizationId to its original organization foreign-key meaning (restore description and example to an organization ID) and add a new optional property (e.g., publicBoxId or publicId) on AdminBoxItem with the description "Public box ID shown to users" and an example like "box_abc123"; then update the example objects used by AdminObservabilityInvestigateResponse.boxes[] to populate the new publicBoxId/publicId instead of overloading organizationId so generated clients and observability responses reflect the correct shapes.
10216-10321:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd
imagetoCreateBoxbefore regenerating clients.
POST /boxstill points atCreateBox, but this request schema has noimagefield even though this PR makes image selection part of box creation. Any SDK regenerated from this spec will be unable to send the selected image and will stay on the legacy create contract.🛠️ Proposed OpenAPI fix
CreateBox: example: name: MyBox + image: ghcr.io/boxlite-ai/boxlite-agent-python@sha256:80d562a57f4bc12def4e54dbdb9e7d26d3268fe0767a2955ab5ad718041145d6 user: boxlite env: NODE_ENV: production @@ properties: name: description: "The name of the box. If not provided, the box ID will be used as the name" example: MyBox type: string + image: + description: OCI image ref to boot the box from. When omitted, the server selects the default curated image. + example: ghcr.io/boxlite-ai/boxlite-agent-python@sha256:80d562a57f4bc12def4e54dbdb9e7d26d3268fe0767a2955ab5ad718041145d6 + type: string user: description: The user associated with the project example: boxlite type: string🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api-client-go/api/openapi.yaml` around lines 10216 - 10321, The CreateBox schema is missing the image field so generated SDKs can't send the selected image; update the CreateBox object (schema name "CreateBox") to include an "image" property (string, with an example like "ubuntu:22.04" or whatever project expects) and appropriate description, and ensure it's added alongside other properties (e.g., near cpu/gpu/memory) and included in the example block at the top of CreateBox so clients and regenerated SDKs will include it.
1678-1686:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove the legacy UUID/
boxIdquery contract fromlistBoxesPaginated.This endpoint still documents filtering by internal UUID and still exposes
sort=boxId, but the PR collapses box identity to a singleid. Leaving those values in the spec will regenerate stale SDK enums and advertise unsupported query behavior.🛠️ Proposed OpenAPI fix
- - description: "Filter by partial Box ID, internal UUID, or name match" + - description: Filter by partial box ID or name match explode: true in: query name: id @@ schema: default: createdAt enum: - id - - boxId - name - state - region - updatedAtAlso applies to: 1827-1836
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api-client-go/api/openapi.yaml` around lines 1678 - 1686, The OpenAPI docs still advertise filtering by internal UUID/`boxId` and exposing sort=boxId for the listBoxesPaginated operation; update the parameter and enums to reflect the collapsed identity model: change the query parameter "id" description to remove any mention of "internal UUID" or "boxId" (e.g., "Filter by partial id or name match"), ensure the example/type remain string, and remove the "boxId" value from any sort enum/options so SDKs won't regenerate the legacy enum; target the listBoxesPaginated operation and the "id" parameter and the related sort enum entries to make these removals.apps/runner/pkg/boxlite/stubs.go (1)
95-108:⚠️ Potential issue | 🔴 CriticalRemove hardcoded
alpine:latestfromRecoverBox(bypasses curated image allowlist)
apps/runner/pkg/boxlite/stubs.goRecoverBoxalways recreates usingdto.CreateBoxDTO{ Image: "alpine:latest" }.apps/runner/pkg/boxlite/client.goCreate()passesboxDto.Imagedirectly toc.runtime.Create(...), so recovery will attempt to pullalpine:latest.- The API allowlist/
assertSupportedImagegate inapps/api/src/box/constants/curated-images.constant.tsis applied only at request-boundary create/warm-pool;RecoverBoxDTOdoes not includeimage, so recovery bypasses the allowlist.- Fix
RecoverBoxto use the box’s original/metadata image (or include a vetted/pinned image inRecoverBoxDTO) instead of a hardcoded fallback.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/runner/pkg/boxlite/stubs.go` around lines 95 - 108, RecoverBox currently hardcodes the image to "alpine:latest" when constructing dto.CreateBoxDTO (see dto.CreateBoxDTO in RecoverBox), which bypasses the curated image allowlist applied elsewhere; change RecoverBox to populate CreateBoxDTO.Image from the box's original metadata image (e.g., the stored box record or Inspect/metadata lookup) or, if Recovery DTO should carry it, extend RecoverBoxDTO to include Image and use that vetted value instead of the hardcoded literal; ensure this value flows unchanged to client.Create and ultimately c.runtime.Create so the assertSupportedImage/curated-images gate is not bypassed.scripts/test/e2e/cases/test_go_entry.py (1)
23-25:⚠️ Potential issue | 🟠 Major | ⚡ Quick winShared root cause: e2e tests still parse legacy UUID box IDs after migration to 12-char base62 IDs. This will cause brittle or failing ID extraction in SDK entry tests despite successful create flows.
scripts/test/e2e/cases/test_go_entry.py#L23-L25: replace UUID regex withBOX_ID=([0-9A-Za-z]{12})capture and use capture group 1 asbox_id.scripts/test/e2e/cases/test_node_entry.py#L28-L30: replace UUID regex withBOX_ID=([0-9A-Za-z]{12})capture and use capture group 1 asbox_id.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/test/e2e/cases/test_go_entry.py` around lines 23 - 25, Replace the legacy UUID regex and extraction with a 12-char base62 box ID capture: in scripts/test/e2e/cases/test_go_entry.py (lines 23-25) replace the UUID_RE definition with BOX_ID = re.compile(r"BOX_ID=([0-9A-Za-z]{12})") and update any extraction sites that used UUID_RE to read group(1) into box_id; in scripts/test/e2e/cases/test_node_entry.py (lines 28-30) do the same replacement (define BOX_ID = re.compile(r"BOX_ID=([0-9A-Za-z]{12})")) and change code that previously used UUID_RE to extract box_id = match.group(1). Ensure all references to the old UUID_RE are updated to use BOX_ID and group(1) for the box_id value.
🧹 Nitpick comments (3)
apps/api/src/box/runner-adapter/runnerAdapter.v2.ts (1)
121-136: ⚡ Quick winType the CREATE_BOX payload to prevent silent contract drift.
This payload is a cross-language contract but currently untyped; a key typo/removal can compile and fail only at runtime. Please enforce a local payload type at construction time.
Proposed refactor
+type CreateBoxJobPayload = { + id: string + userId: string + image: string + osUser?: string + cpuQuota?: number + memoryQuota?: number + storageQuota?: number + env?: Record<string, string> + networkBlockAll?: boolean + networkAllowList?: string + organizationId?: string + regionId?: string +} + - const payload = { + const payload: CreateBoxJobPayload = { id: box.id, userId: box.organizationId, image: box.image, osUser: box.osUser, cpuQuota: box.cpu, memoryQuota: box.mem, storageQuota: box.disk, env: box.env, networkBlockAll: box.networkBlockAll, networkAllowList: box.networkAllowList, organizationId: box.organizationId, regionId: box.region, }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/box/runner-adapter/runnerAdapter.v2.ts` around lines 121 - 136, The CREATE_BOX payload is untyped causing silent contract drift; define a local TypeScript interface or type (e.g., CreateBoxPayload) describing the exact fields expected (id, userId, image, osUser, cpuQuota, memoryQuota, storageQuota, env, networkBlockAll, networkAllowList, organizationId, regionId) and then annotate the payload variable as CreateBoxPayload before passing it to this.jobService.createJob (JobType.CREATE_BOX, ResourceType.BOX, box.id, payload) so the compiler enforces the contract and prevents accidental typos or removals.apps/api/src/box/runner-adapter/runnerAdapter.v2.createBox.spec.ts (1)
58-83: ⚡ Quick winAdd assertions for tenancy/routing payload fields.
Current tests miss
userId,organizationId, andregionId, which are part of the CREATE_BOX payload contract.Suggested assertion additions
const payload = createJob.mock.calls[0][5] as Record<string, unknown> @@ expect(payload.storageQuota).toBe(10) + expect(payload.userId).toBe(box.organizationId) + expect(payload.organizationId).toBe(box.organizationId) + expect(payload.regionId).toBe('us')🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/box/runner-adapter/runnerAdapter.v2.createBox.spec.ts` around lines 58 - 83, The test in runnerAdapter.v2.createBox.spec.ts is missing assertions that the CREATE_BOX payload includes tenancy/routing fields; after obtaining payload from createJob.mock.calls[0][5] in the tests that call adapter.createBox(box) (use the existing createJob, createAdapter, buildBox fixtures), add assertions that payload.userId, payload.organizationId, and payload.regionId exist and equal the corresponding properties on the box (e.g. expect(payload.userId).toBe(box.userId), expect(payload.organizationId).toBe(box.organizationId), expect(payload.regionId).toBe(box.regionId)); also assert they are the correct types/format if applicable (e.g. non-empty strings) and do not add or change other assertions. Ensure you add these checks in both tests that inspect the payload (the image/resources test and the id test) or at least in the primary test that verifies the full CREATE_BOX payload.apps/dashboard/src/providers/PlaygroundProvider.tsx (1)
300-303: ⚡ Quick winCentralize pinned image refs in one shared dashboard constant.
Line 302 hardcodes a digest that is also defined in other dashboard create flows. Keeping these in multiple files increases drift risk during allowlist rotations.
♻️ Suggested direction
- const createBoxFromImageParams: CreateBoxFromImageParams = { - image: - 'ghcr.io/boxlite-ai/boxlite-agent-base@sha256:834dcb65465985fc2f648451d76c81d166bc7672391c9064a0a115ce6306c85f', - } + import { DEFAULT_BOX_IMAGE } from '`@/lib/supported-images`' + const createBoxFromImageParams: CreateBoxFromImageParams = { + image: DEFAULT_BOX_IMAGE, + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/dashboard/src/providers/PlaygroundProvider.tsx` around lines 300 - 303, The hardcoded image digest in the createBoxFromImageParams object should be replaced with a single shared dashboard constant to avoid drift; update the createBoxFromImageParams (the image property) to reference a centralized constant (e.g., DASHBOARD_PINNED_AGENT_IMAGE) imported from the shared dashboard constants module, and if that constant does not yet exist, add it to the central constants file (with the current digest value) and use that constant here and in any other dashboard create flows that currently hardcode the same digest.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/src/box/dto/create-box.dto.ts`:
- Around line 22-24: The CreateBoxDto.image property is missing OpenAPI
metadata; add the `@ApiPropertyOptional`() decorator above the image field in the
CreateBoxDto class so the generated schema includes this optional string
property (specify type: 'string' and an appropriate description if desired).
Ensure the existing validators (`@IsOptional`, `@IsString`) remain and that the
import for ApiPropertyOptional from `@nestjs/swagger` is added if not already
present.
In `@apps/api/src/box/services/box.service.warm-pool.spec.ts`:
- Around line 34-36: The test currently hardcodes a specific fallback image
digest for box.image which makes it flaky because createForWarmPool calls
assertSupportedImage(undefined) and the curated image can change via env; update
the assertion to avoid requiring the exact digest: in the test replace the exact
equality check with a looser check that the image references the correct
repository/name (for example assert box.image startsWith or matches the prefix
"ghcr.io/boxlite-ai/boxlite-agent-base" or a regex like
/^ghcr\.io\/boxlite-ai\/boxlite-agent-base(@|:)/), so the test verifies the
correct image source without asserting a fixed digest while leaving
createForWarmPool and assertSupportedImage logic unchanged.
In `@apps/dashboard/src/components/admin/adminHelpers.ts`:
- Around line 223-226: The function findBoxById currently lowercases both inputs
causing case-insensitive matches; change it to use exact trimmed comparison
instead: set targetBoxId = boxId.trim() (no toLowerCase()) and in the loop use
group.boxes.find(b => b.id.trim() === targetBoxId) so IDs that differ by case
are distinguished while still trimming whitespace.
In `@openapi/box.openapi.yaml`:
- Around line 1527-1532: The OpenAPI schema's CreateBoxRequest.image field
currently uses unpinned container refs ("alpine:latest" as default and
"python:3.11-slim" as example) which contradict the pinned-image allowlist
constraint; update the CreateBoxRequest.image schema to either remove the
unpinned default/example or replace both with concrete pinned image references
that are present in the service allowlist (or use a neutral placeholder like
"REPLACE_WITH_PINNED_IMAGE@sha256:<digest>" if a specific digest can't be
committed), and ensure the default/example reflect the pinned form required by
the API.
In `@scripts/test/e2e/cases/test_cli_entry.py`:
- Around line 33-35: Tests still use UUID_RE and assert "uuid" but the new ID
contract uses 12-char base62 box IDs; replace the UUID regex with a 12-character
base62 matcher and update assertion text to "id" in each affected file: in
scripts/test/e2e/cases/test_cli_entry.py (lines 33-35) change the UUID_RE
definition to match /^[A-Za-z0-9]{12}$/ (i.e., 12 base62 chars) and update any
assertions/messages that refer to "uuid" to "id"; in
scripts/test/e2e/cases/test_cli_detach_recovery.py (lines 41-43) do the same for
UUID_RE and rename "uuid" text to "id"; and in
scripts/test/e2e/cases/test_c_entry.py (lines 30-32) apply the identical regex
replacement and update "uuid" to "id". Ensure the symbol name UUID_RE remains
(or rename consistently) so callers continue to work.
---
Outside diff comments:
In `@apps/api-client-go/api/openapi.yaml`:
- Around line 9938-10002: The OpenAPI docs still describe the Box "id" as an
internal UUID and use a UUID example; update the Box schemas and any public-box
schema examples (the schema that defines properties.id and the example block
under "example: id") to describe "id" as the 12-character base62 identifier and
replace the UUID example with a representative 12-char base62 value (e.g.,
a-zA-Z0-9 string of length 12); also update the PaginatedBoxes example set so
its box examples use the same 12-char base62 id and adjusted description text
for properties.id to match the new contract.
- Around line 13450-13472: The OpenAPI spec incorrectly repurposes
AdminBoxItem.organizationId as a user-facing public box ID; revert
organizationId to its original organization foreign-key meaning (restore
description and example to an organization ID) and add a new optional property
(e.g., publicBoxId or publicId) on AdminBoxItem with the description "Public box
ID shown to users" and an example like "box_abc123"; then update the example
objects used by AdminObservabilityInvestigateResponse.boxes[] to populate the
new publicBoxId/publicId instead of overloading organizationId so generated
clients and observability responses reflect the correct shapes.
- Around line 10216-10321: The CreateBox schema is missing the image field so
generated SDKs can't send the selected image; update the CreateBox object
(schema name "CreateBox") to include an "image" property (string, with an
example like "ubuntu:22.04" or whatever project expects) and appropriate
description, and ensure it's added alongside other properties (e.g., near
cpu/gpu/memory) and included in the example block at the top of CreateBox so
clients and regenerated SDKs will include it.
- Around line 1678-1686: The OpenAPI docs still advertise filtering by internal
UUID/`boxId` and exposing sort=boxId for the listBoxesPaginated operation;
update the parameter and enums to reflect the collapsed identity model: change
the query parameter "id" description to remove any mention of "internal UUID" or
"boxId" (e.g., "Filter by partial id or name match"), ensure the example/type
remain string, and remove the "boxId" value from any sort enum/options so SDKs
won't regenerate the legacy enum; target the listBoxesPaginated operation and
the "id" parameter and the related sort enum entries to make these removals.
In `@apps/runner/pkg/boxlite/stubs.go`:
- Around line 95-108: RecoverBox currently hardcodes the image to
"alpine:latest" when constructing dto.CreateBoxDTO (see dto.CreateBoxDTO in
RecoverBox), which bypasses the curated image allowlist applied elsewhere;
change RecoverBox to populate CreateBoxDTO.Image from the box's original
metadata image (e.g., the stored box record or Inspect/metadata lookup) or, if
Recovery DTO should carry it, extend RecoverBoxDTO to include Image and use that
vetted value instead of the hardcoded literal; ensure this value flows unchanged
to client.Create and ultimately c.runtime.Create so the
assertSupportedImage/curated-images gate is not bypassed.
In `@scripts/test/e2e/cases/test_go_entry.py`:
- Around line 23-25: Replace the legacy UUID regex and extraction with a 12-char
base62 box ID capture: in scripts/test/e2e/cases/test_go_entry.py (lines 23-25)
replace the UUID_RE definition with BOX_ID =
re.compile(r"BOX_ID=([0-9A-Za-z]{12})") and update any extraction sites that
used UUID_RE to read group(1) into box_id; in
scripts/test/e2e/cases/test_node_entry.py (lines 28-30) do the same replacement
(define BOX_ID = re.compile(r"BOX_ID=([0-9A-Za-z]{12})")) and change code that
previously used UUID_RE to extract box_id = match.group(1). Ensure all
references to the old UUID_RE are updated to use BOX_ID and group(1) for the
box_id value.
---
Nitpick comments:
In `@apps/api/src/box/runner-adapter/runnerAdapter.v2.createBox.spec.ts`:
- Around line 58-83: The test in runnerAdapter.v2.createBox.spec.ts is missing
assertions that the CREATE_BOX payload includes tenancy/routing fields; after
obtaining payload from createJob.mock.calls[0][5] in the tests that call
adapter.createBox(box) (use the existing createJob, createAdapter, buildBox
fixtures), add assertions that payload.userId, payload.organizationId, and
payload.regionId exist and equal the corresponding properties on the box (e.g.
expect(payload.userId).toBe(box.userId),
expect(payload.organizationId).toBe(box.organizationId),
expect(payload.regionId).toBe(box.regionId)); also assert they are the correct
types/format if applicable (e.g. non-empty strings) and do not add or change
other assertions. Ensure you add these checks in both tests that inspect the
payload (the image/resources test and the id test) or at least in the primary
test that verifies the full CREATE_BOX payload.
In `@apps/api/src/box/runner-adapter/runnerAdapter.v2.ts`:
- Around line 121-136: The CREATE_BOX payload is untyped causing silent contract
drift; define a local TypeScript interface or type (e.g., CreateBoxPayload)
describing the exact fields expected (id, userId, image, osUser, cpuQuota,
memoryQuota, storageQuota, env, networkBlockAll, networkAllowList,
organizationId, regionId) and then annotate the payload variable as
CreateBoxPayload before passing it to this.jobService.createJob
(JobType.CREATE_BOX, ResourceType.BOX, box.id, payload) so the compiler enforces
the contract and prevents accidental typos or removals.
In `@apps/dashboard/src/providers/PlaygroundProvider.tsx`:
- Around line 300-303: The hardcoded image digest in the
createBoxFromImageParams object should be replaced with a single shared
dashboard constant to avoid drift; update the createBoxFromImageParams (the
image property) to reference a centralized constant (e.g.,
DASHBOARD_PINNED_AGENT_IMAGE) imported from the shared dashboard constants
module, and if that constant does not yet exist, add it to the central constants
file (with the current digest value) and use that constant here and in any other
dashboard create flows that currently hardcode the same digest.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f254a8aa-ab70-4eef-81b2-a1cb7a0c6706
📒 Files selected for processing (66)
apps/api-client-go/api/openapi.yamlapps/api-client-go/model_admin_box_item.goapps/api-client-go/model_box.goapps/api-client-go/model_workspace.goapps/api/src/admin/dto/admin-overview.dto.tsapps/api/src/admin/services/observability.service.spec.tsapps/api/src/admin/services/observability.service.tsapps/api/src/admin/services/overview.service.tsapps/api/src/box/constants/curated-images.constant.spec.tsapps/api/src/box/constants/curated-images.constant.tsapps/api/src/box/dto/box.dto.spec.tsapps/api/src/box/dto/box.dto.tsapps/api/src/box/dto/create-box.dto.tsapps/api/src/box/dto/workspace.deprecated.dto.tsapps/api/src/box/entities/box.entity.spec.tsapps/api/src/box/entities/box.entity.tsapps/api/src/box/managers/box-actions/box-start.action.tsapps/api/src/box/repositories/box.repository.tsapps/api/src/box/runner-adapter/runnerAdapter.tsapps/api/src/box/runner-adapter/runnerAdapter.v0.tsapps/api/src/box/runner-adapter/runnerAdapter.v2.createBox.spec.tsapps/api/src/box/runner-adapter/runnerAdapter.v2.tsapps/api/src/box/services/box-lookup-cache-invalidation.service.tsapps/api/src/box/services/box.service.box-id.spec.tsapps/api/src/box/services/box.service.tsapps/api/src/box/services/box.service.warm-pool.spec.tsapps/api/src/box/utils/box-id.util.tsapps/api/src/box/utils/box-lookup-cache.util.tsapps/api/src/boxlite-rest/mappers/box-to-box.mapper.spec.tsapps/api/src/boxlite-rest/mappers/box-to-box.mapper.tsapps/api/src/migrations/1741087887225-migration.tsapps/dashboard/src/components/Box/CreateBoxSheet.tsxapps/dashboard/src/components/BoxTable/BoxTableActions.tsxapps/dashboard/src/components/Playground/Box/CodeSnippets/python.tsapps/dashboard/src/components/admin/AdminPeopleBoxesView.tsxapps/dashboard/src/components/admin/adminDiagnoseTarget.tsapps/dashboard/src/components/admin/adminHelpers.tsapps/dashboard/src/hooks/useBoxWsSync.tsapps/dashboard/src/lib/box-identity.tsapps/dashboard/src/lib/cloudBox.tsapps/dashboard/src/lib/onboarding-code-examples.tsapps/dashboard/src/providers/PlaygroundProvider.tsxapps/libs/api-client/src/docs/AdminBoxItem.mdapps/libs/api-client/src/docs/Box.mdapps/libs/api-client/src/docs/Workspace.mdapps/libs/api-client/src/models/admin-box-item.tsapps/libs/api-client/src/models/box.tsapps/libs/api-client/src/models/workspace.tsapps/runner/pkg/api/dto/box.goapps/runner/pkg/boxlite/client.goapps/runner/pkg/boxlite/stubs.goopenapi/box.openapi.yamlscripts/test/e2e/README.mdscripts/test/e2e/bootstrap.shscripts/test/e2e/cases/conftest.pyscripts/test/e2e/cases/test_c_entry.pyscripts/test/e2e/cases/test_cli_detach_recovery.pyscripts/test/e2e/cases/test_cli_entry.pyscripts/test/e2e/cases/test_error_code_mapping.pyscripts/test/e2e/cases/test_go_entry.pyscripts/test/e2e/cases/test_node_entry.pyscripts/test/e2e/cases/test_quota_enforcement.pyscripts/test/e2e/fixture_setup.pyscripts/test/e2e/sdks/c/e2e_basic.cscripts/test/e2e/sdks/go/e2e_basic.goscripts/test/e2e/sdks/node/e2e_basic.ts
💤 Files with no reviewable changes (6)
- apps/api/src/admin/services/overview.service.ts
- apps/api/src/admin/dto/admin-overview.dto.ts
- apps/dashboard/src/components/BoxTable/BoxTableActions.tsx
- apps/api/src/box/utils/box-lookup-cache.util.ts
- apps/api/src/box/utils/box-id.util.ts
- apps/api/src/box/dto/workspace.deprecated.dto.ts
- create-box.dto: add @ApiPropertyOptional to image (was the only field missing it; would drop from the CreateBox swagger schema) - warm-pool.spec: isolate BOXLITE_SYSTEM_* env so the pinned-fallback assertion is deterministic (flaky if the host sets those vars) - adminHelpers.findBoxById: exact case-sensitive match — box ids are 12-char base62, lowercasing could resolve the wrong box - openapi: CreateBoxRequest.image default/example use pinned supported refs instead of unpinned alpine/python (contradicted the allowlist) - e2e: UUID_RE -> BOX_ID_RE (anchored 12-char base62) across the CLI/SDK entry tests; the new single id is not uuid-shaped API jest 112/112, dashboard tsc clean, e2e py_compile clean.
|
Thanks @coderabbitai — all five addressed in 127a7a7. Quick notes:
|
|
Tip For best results, initiate chat on the files or code changes.
The warm-pool env-isolation approach is the right call. You're correct that 🐇 ✨ |
- create-box.dto: add @ApiPropertyOptional to image (was the only field missing it; would drop from the CreateBox swagger schema) - warm-pool.spec: isolate BOXLITE_SYSTEM_* env so the pinned-fallback assertion is deterministic (flaky if the host sets those vars) - adminHelpers.findBoxById: exact case-sensitive match — box ids are 12-char base62, lowercasing could resolve the wrong box - e2e: UUID_RE -> BOX_ID_RE (anchored 12-char base62) across the CLI/SDK entry tests; the new single id is not uuid-shaped Not changed: openapi CreateBoxRequest.image example/default — that spec is shared with the embedded engine (boxlite serve), which accepts any OCI image, so python:3.11-slim stays the illustrative example; the hosted-only pinned constraint is already spelled out in the description. API jest 112/112, dashboard tsc clean, e2e py_compile clean.
127a7a7 to
4b096b2
Compare
- create-box.dto: add @ApiPropertyOptional to image (was the only field missing it; would drop from the CreateBox swagger schema) - warm-pool.spec: isolate BOXLITE_SYSTEM_* env so the pinned-fallback assertion is deterministic (flaky if the host sets those vars) - adminHelpers.findBoxById: exact case-sensitive match — box ids are 12-char base62, lowercasing could resolve the wrong box - e2e: UUID_RE -> BOX_ID_RE (anchored 12-char base62) across the CLI/SDK entry tests; the new single id is not uuid-shaped Not changed: openapi CreateBoxRequest.image example/default — that spec is shared with the embedded engine (boxlite serve), which accepts any OCI image, so python:3.11-slim stays the illustrative example; the hosted-only pinned constraint is already spelled out in the description. API jest 112/112, dashboard tsc clean, e2e py_compile clean.
4b096b2 to
73e8d6c
Compare
Summary
Consolidated box image + identity refactor in one PR (base
main). Three coherent, jointly-tested changes:imageis a first-classBoxcolumn (no key→ref translation); runner contract field isimage; dashboard image picker; e2e adapted.boxIdinto one 12-char base62 id (the PK). The internal uuid (Daytona leftover) is gone; one id threads PG → job → runner → engine.boxIdDTO field + regenerate clients — remove theboxIdmirror fromBoxDto/admin DTO, regenerate the 4 API clients (CI-faithful, run inside a BoxLite box), dashboard reads.id.Notes
external_refis intentionally NOT included. The cloud box id rides in the engine'snameslot (the engine mints its own internal id); user-facing names live only in PG. This keeps the runtime contract minimal — no dedicated orchestrator-reference field.cloudBoxadapter (added here) instead of@boxlite-ai/sdk, but thesdk-typescriptpackage itself is not deleted in this diff. (The branch's merge-base is chore(apps): remove TypeScript cloud SDK (re-apply #738 onto main) #743, which was later reverted frommainby Revert "chore(apps): remove TypeScript cloud SDK (re-apply #738 onto main)" (#743) #751 — so the deletion does not appear here andsdk-typescriptstill exists onmain.) The SDK package removal will be handled as its own follow-up; this PR only migrates the dashboard tocloudBox.Verification
vetclean (sdks/go + runner pkg; final cgo link needs prebuilt libboxlite.a, absent in worktree)tsc --noEmitclean · API clients regenerated inside a BoxLite box (CI-faithful)maincontains zeroexternal_refreferences.Deployment
Baseline migration edited in place (box PK =
varchar(12),imagecolumn added,boxIddropped) → requires a DB rebuild (agreed; nothing launched).Summary by CodeRabbit
Release Notes
New Features
Changes
imagefield is now required for box creation; separate box identifier field removedArchitecture
1. Image: full OCI ref, end-to-end, no translation layer
The pinned OCI ref is the single representation of "image" at every layer — there is no
curated-key ⇄ ref translation step.
2. Identity: one 12-char base62 id, no second id
uuid(a Daytona leftover) and the duplicateboxIdcolumn/DTO field are removed: it offeredneither a smaller-ordered-key benefit nor ABA protection, and kept leaking into runner logs.
Two ways the CLI/API resolve a box
What this PR does NOT add