Skip to content

feat(box): image pipeline + single box identity (drop legacy boxId)#753

Closed
law-chain-hot wants to merge 13 commits into
mainfrom
feat/box-image-identity
Closed

feat(box): image pipeline + single box identity (drop legacy boxId)#753
law-chain-hot wants to merge 13 commits into
mainfrom
feat/box-image-identity

Conversation

@law-chain-hot

@law-chain-hot law-chain-hot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Consolidated box image + identity refactor in one PR (base main). Three coherent, jointly-tested changes:

  1. Image pipeline — boxes boot from supported pinned OCI refs; image is a first-class Box column (no key→ref translation); runner contract field is image; dashboard image picker; e2e adapted.
  2. Single box identity — collapse the dual uuid/boxId into one 12-char base62 id (the PK). The internal uuid (Daytona leftover) is gone; one id threads PG → job → runner → engine.
  3. Drop legacy boxId DTO field + regenerate clients — remove the boxId mirror from BoxDto/admin DTO, regenerate the 4 API clients (CI-faithful, run inside a BoxLite box), dashboard reads .id.

Notes

  • external_ref is intentionally NOT included. The cloud box id rides in the engine's name slot (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.
  • The TS cloud SDK removal is NOT part of this PR. The dashboard image-picker / identity code uses the cloudBox adapter (added here) instead of @boxlite-ai/sdk, but the sdk-typescript package 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 from main by Revert "chore(apps): remove TypeScript cloud SDK (re-apply #738 onto main)" (#743) #751 — so the deletion does not appear here and sdk-typescript still exists on main.) The SDK package removal will be handled as its own follow-up; this PR only migrates the dashboard to cloudBox.

Verification

  • Engine lib: 734/734 · boxlite-c: 55/55 · Go vet clean (sdks/go + runner pkg; final cgo link needs prebuilt libboxlite.a, absent in worktree)
  • API jest: 112/112 (from the consolidated branch; unchanged by the external_ref strip)
  • Dashboard tsc --noEmit clean · API clients regenerated inside a BoxLite box (CI-faithful)
  • Net diff vs main contains zero external_ref references.

Deployment

Baseline migration edited in place (box PK = varchar(12), image column added, boxId dropped) → requires a DB rebuild (agreed; nothing launched).

Summary by CodeRabbit

Release Notes

  • New Features

    • Box creation now requires selecting from a curated list of supported OCI container images, ensuring consistency and security
    • Streamlined box identification with a unified ID format
  • Changes

    • Updated API contracts: image field is now required for box creation; separate box identifier field removed

Architecture

1. Image: full OCI ref, end-to-end, no translation layer

create request                 PG (box row)            CREATE_BOX payload          runner / engine
  image: "ghcr.io/...@sha256"  ──►  box.image  ───────►  image (verbatim)  ────────►  runtime.Create(image)
        │                            (first-class column)
        ▼
  assertSupportedImage(image)   ← thin allowlist gate at the request boundary only.
   not in allowlist → 400 listing the supported refs.
   Deletable when custom images land; no other layer knows the curated set exists.

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

            id = "a7Kf93mQpX2z"   (minted in-memory at create; THE identity everywhere)
                  │
   ┌──────────────┼───────────────────────────────────────────────────────────┐
   ▼              ▼                         ▼                                   ▼
 PG boxes.id    jobs.resourceId        runner box map key                engine VM name
 (primary key)  (= id)                 (= id)                            (WithName(id))
   │              │                       │                                   │
   └─ findOneByIdOrName(id | name)        └─ proxy /boxes/{id}/...             └─ lookup by name slot

cloud box id rides in the engine's name slot (the engine still mints its own internal id,
which never surfaces). No uuid, no separate boxId column, no external_ref field.

uuid (a Daytona leftover) and the duplicate boxId column/DTO field are removed: it offered
neither a smaller-ordered-key benefit nor ABA protection, and kept leaking into runner logs.

Two ways the CLI/API resolve a box

metadata read (inspect / ls)   →  PG findOneByIdOrName(id | name)         [stops at PG]
operation     (exec / stats)   →  PG resolve → id → runner → engine VM    [reaches the box]

What this PR does NOT add

external_ref (CRI-uid-style field)  →  intentionally excluded.
  The cloud id rides in the engine name slot; user-facing names live only in PG.
  Keeps the runtime contract minimal (no dedicated orchestrator-reference field).

law-chain-hot and others added 12 commits June 12, 2026 22:25
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.
@law-chain-hot law-chain-hot requested a review from a team as a code owner June 13, 2026 08:55
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 930218f7-339e-4b6f-a887-a87365a5c689

📥 Commits

Reviewing files that changed from the base of the PR and between 127a7a7 and 73e8d6c.

📒 Files selected for processing (8)
  • apps/api/src/box/dto/create-box.dto.ts
  • apps/api/src/box/services/box.service.warm-pool.spec.ts
  • apps/dashboard/src/components/admin/adminHelpers.ts
  • scripts/test/e2e/cases/test_c_entry.py
  • scripts/test/e2e/cases/test_cli_detach_recovery.py
  • scripts/test/e2e/cases/test_cli_entry.py
  • scripts/test/e2e/cases/test_go_entry.py
  • scripts/test/e2e/cases/test_node_entry.py
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/dashboard/src/components/admin/adminHelpers.ts
  • scripts/test/e2e/cases/test_cli_detach_recovery.py
  • scripts/test/e2e/cases/test_c_entry.py
  • scripts/test/e2e/cases/test_node_entry.py
  • apps/api/src/box/services/box.service.warm-pool.spec.ts
  • scripts/test/e2e/cases/test_go_entry.py
  • scripts/test/e2e/cases/test_cli_entry.py

📝 Walkthrough

Walkthrough

The PR consolidates box identity from a dual UUID/boxId model to a single 12-character base62 id and introduces curated OCI image references throughout the system. It updates OpenAPI contracts, generated clients, entity persistence, service lookups, runner orchestration, dashboard UX, and e2e test infrastructure to enforce id-based identity and image allowlisting.

Changes

Box identity and image contract consolidation

Layer / File(s) Summary
OpenAPI and generated client contract updates
apps/api-client-go/api/openapi.yaml, apps/api-client-go/model_*.go, apps/libs/api-client/src/models/*
OpenAPI schemas and generated Go/TypeScript models remove boxId, make image required in Box/Workspace, and change AdminBoxItem.organizationId to optional to represent the public box ID.
Box entity identity and migration changes
apps/api/src/box/entities/box.entity.ts, apps/api/src/box/entities/box.entity.spec.ts, apps/api/src/migrations/1741087887225-migration.ts
Box entity switches primary key from UUID to 12-character base62 id, adds image column, updates uniqueness to (organizationId, name), adds indexes for state/desiredState/runnerId, and migration DDL reflects the new schema and index set.
API DTO and response mapper surface
apps/api/src/box/dto/*.ts, apps/api/src/boxlite-rest/mappers/box-to-box.mapper.ts
BoxDto removes boxId, adds image, CreateBoxDto adds optional image field, WorkspaceDto replaces image with optional info, and REST mapper aligns box_id to use internal id and passes through image from request/entity.
Curated image allowlist and create-time validation
apps/api/src/box/constants/curated-images.constant.ts, apps/api/src/box/constants/curated-images.constant.spec.ts, apps/api/src/box/services/box.service.ts, apps/api/src/box/services/box.service.warm-pool.spec.ts
New allowlist sources and assertSupportedImage() validate OCI refs at request boundaries; warm-pool and normal box creation enforce image selection via the allowlist; tests isolate environment variables to ensure deterministic pinned defaults.
ID-based lookup and cache invalidation refactor
apps/api/src/box/utils/box-lookup-cache.util.ts, apps/api/src/box/services/box-lookup-cache-invalidation.service.ts, apps/api/src/box/repositories/box.repository.ts, apps/api/src/box/services/box.service.ts, apps/api/src/box/services/box.service.box-id.spec.ts
Cache key helpers renamed *ByBoxId*ById with prefix changes; invalidation payloads and repository hooks use id instead of boxId; BoxService lookup queries changed to search by id as primary key with id/name alternatives; not-found messages updated.
Runner createBox orchestration and payload alignment
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, apps/api/src/box/runner-adapter/runnerAdapter.v2.createBox.spec.ts, apps/api/src/box/managers/box-actions/box-start.action.ts, apps/runner/pkg/api/dto/box.go, apps/runner/pkg/boxlite/client.go, apps/runner/pkg/boxlite/stubs.go
RunnerAdapter gains createBox() contract; V0 rejects, V2 enqueues CREATE_BOX jobs with payloads containing id/image/quotas; BoxStartAction calls createBox() instead of failing unknown boxes; runner DTO removes BoxId/ArtifactRef/GpuQuota, adds required Image, with client/stubs updated to use image field.
Admin overview and observability ID alignment
apps/api/src/admin/dto/admin-overview.dto.ts, apps/api/src/admin/services/overview.service.ts, apps/api/src/admin/services/observability.service.ts, apps/api/src/admin/services/observability.service.spec.ts
AdminBoxItemDto gains Swagger docs for optional organizationId; overview.listBoxes() emits organizationId instead of boxId; observability correlation populates boxIds with box.id and matches only against box.id; investigation tests updated to single canonical box IDs.
Dashboard create flow and ID-based identity helpers
apps/dashboard/src/components/Box/CreateBoxSheet.tsx, apps/dashboard/src/lib/box-identity.ts, apps/dashboard/src/hooks/useBoxWsSync.ts, apps/dashboard/src/components/admin/adminHelpers.ts, apps/dashboard/src/components/admin/AdminPeopleBoxesView.tsx, apps/dashboard/src/components/admin/adminDiagnoseTarget.ts, apps/dashboard/src/components/BoxTable/BoxTableActions.tsx
CreateBoxSheet adds RadioGroup image picker with form schema validation; identity helpers switch to Box.id only with updated type signatures; admin search and filter use id only; WebSocket sync matches active boxes by id; aria-labels and diagnostics target text use id unconditionally.
Dashboard code examples and image default strings
apps/dashboard/src/lib/onboarding-code-examples.ts, apps/dashboard/src/lib/cloudBox.ts, apps/dashboard/src/providers/PlaygroundProvider.tsx, apps/dashboard/src/components/Playground/Box/CodeSnippets/python.ts
Onboarding examples and Python snippet generation emit pinned ghcr.io/boxlite-ai/boxlite-agent-base@sha256:... image; Playground defaults initialize with specific image digest; validation error messaging references supported OCI refs instead of curated keys.
Documentation and reference text synchronization
apps/libs/api-client/src/docs/*.md, scripts/test/e2e/README.md
API client markdown models document organizationId as "Public box ID shown to users", remove boxId, and include image field; e2e README removes snapshot registration steps and documents allowlist-based image usage.
E2E fixture/default image propagation and ID regex updates
scripts/test/e2e/bootstrap.sh, scripts/test/e2e/cases/conftest.py, scripts/test/e2e/fixture_setup.py, scripts/test/e2e/cases/test_*.py, scripts/test/e2e/sdks/*.{c,go,ts}
Bootstrap writes BOXLITE_SYSTEM_*_IMAGE env vars; fixture reads base image from API env and populates credentials default_image; snapshot registration removed; fixture SQL uses default-user join filter; test cases import and use DEFAULT_IMAGE, switch ID parsing from UUID regex to BOX_ID_RE (12-char base62); SDK smoke drivers emit pinned images.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • boxlite-ai/boxlite#720: Both PRs modify scripts/test/e2e/fixture_setup.py's patch_admin_quota() SQL update—main PR adjusts how quota rows are selected (default-org join vs personal filter), while the retrieved PR changes which quota limit columns are updated (per-sandbox → per-box).

Suggested reviewers

  • DorianZheng
  • G4614

Poem

A rabbit hops with joy today—
The boxes now have cleaner ways!
Old IDs shuffled, images shine,
Twelve-char IDs in perfect line.
🐰 ✨

✨ 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-identity
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/box-image-identity

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

id is still documented as a UUID in both public box schemas.

The PR objective says the UUID/legacy-boxId split is gone and id is 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.

🛠️ 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: string
Update the inherited `PaginatedBoxes` item examples too so the example set stays internally consistent.

Also 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 lift

Don't overload organizationId with the box public ID.

The stack context says AdminBoxItem.organizationId is 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 organizationId reserved for the organization foreign key. The AdminObservabilityInvestigateResponse.boxes[] examples should then be updated to match the corrected AdminBoxItem shape.

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 win

Add image to CreateBox before regenerating clients.

POST /box still points at CreateBox, but this request schema has no image field 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 win

Remove the legacy UUID/boxId query contract from listBoxesPaginated.

This endpoint still documents filtering by internal UUID and still exposes sort=boxId, but the PR collapses box identity to a single id. 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
           - updatedAt

Also 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 | 🔴 Critical

Remove hardcoded alpine:latest from RecoverBox (bypasses curated image allowlist)

  • apps/runner/pkg/boxlite/stubs.go RecoverBox always recreates using dto.CreateBoxDTO{ Image: "alpine:latest" }.
  • apps/runner/pkg/boxlite/client.go Create() passes boxDto.Image directly to c.runtime.Create(...), so recovery will attempt to pull alpine:latest.
  • The API allowlist/assertSupportedImage gate in apps/api/src/box/constants/curated-images.constant.ts is applied only at request-boundary create/warm-pool; RecoverBoxDTO does not include image, so recovery bypasses the allowlist.
  • Fix RecoverBox to use the box’s original/metadata image (or include a vetted/pinned image in RecoverBoxDTO) 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 win

Shared 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 with BOX_ID=([0-9A-Za-z]{12}) capture and use capture group 1 as box_id.
  • scripts/test/e2e/cases/test_node_entry.py#L28-L30: replace UUID regex with BOX_ID=([0-9A-Za-z]{12}) capture and use capture group 1 as box_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 win

Type 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 win

Add assertions for tenancy/routing payload fields.

Current tests miss userId, organizationId, and regionId, 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 win

Centralize 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0cc2dda and f4fbf5d.

📒 Files selected for processing (66)
  • apps/api-client-go/api/openapi.yaml
  • apps/api-client-go/model_admin_box_item.go
  • apps/api-client-go/model_box.go
  • apps/api-client-go/model_workspace.go
  • apps/api/src/admin/dto/admin-overview.dto.ts
  • apps/api/src/admin/services/observability.service.spec.ts
  • apps/api/src/admin/services/observability.service.ts
  • apps/api/src/admin/services/overview.service.ts
  • apps/api/src/box/constants/curated-images.constant.spec.ts
  • apps/api/src/box/constants/curated-images.constant.ts
  • apps/api/src/box/dto/box.dto.spec.ts
  • apps/api/src/box/dto/box.dto.ts
  • apps/api/src/box/dto/create-box.dto.ts
  • apps/api/src/box/dto/workspace.deprecated.dto.ts
  • apps/api/src/box/entities/box.entity.spec.ts
  • apps/api/src/box/entities/box.entity.ts
  • apps/api/src/box/managers/box-actions/box-start.action.ts
  • apps/api/src/box/repositories/box.repository.ts
  • 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.createBox.spec.ts
  • apps/api/src/box/runner-adapter/runnerAdapter.v2.ts
  • apps/api/src/box/services/box-lookup-cache-invalidation.service.ts
  • apps/api/src/box/services/box.service.box-id.spec.ts
  • apps/api/src/box/services/box.service.ts
  • apps/api/src/box/services/box.service.warm-pool.spec.ts
  • apps/api/src/box/utils/box-id.util.ts
  • apps/api/src/box/utils/box-lookup-cache.util.ts
  • apps/api/src/boxlite-rest/mappers/box-to-box.mapper.spec.ts
  • apps/api/src/boxlite-rest/mappers/box-to-box.mapper.ts
  • apps/api/src/migrations/1741087887225-migration.ts
  • apps/dashboard/src/components/Box/CreateBoxSheet.tsx
  • apps/dashboard/src/components/BoxTable/BoxTableActions.tsx
  • apps/dashboard/src/components/Playground/Box/CodeSnippets/python.ts
  • apps/dashboard/src/components/admin/AdminPeopleBoxesView.tsx
  • apps/dashboard/src/components/admin/adminDiagnoseTarget.ts
  • apps/dashboard/src/components/admin/adminHelpers.ts
  • apps/dashboard/src/hooks/useBoxWsSync.ts
  • apps/dashboard/src/lib/box-identity.ts
  • apps/dashboard/src/lib/cloudBox.ts
  • apps/dashboard/src/lib/onboarding-code-examples.ts
  • apps/dashboard/src/providers/PlaygroundProvider.tsx
  • apps/libs/api-client/src/docs/AdminBoxItem.md
  • apps/libs/api-client/src/docs/Box.md
  • apps/libs/api-client/src/docs/Workspace.md
  • apps/libs/api-client/src/models/admin-box-item.ts
  • apps/libs/api-client/src/models/box.ts
  • apps/libs/api-client/src/models/workspace.ts
  • apps/runner/pkg/api/dto/box.go
  • apps/runner/pkg/boxlite/client.go
  • apps/runner/pkg/boxlite/stubs.go
  • openapi/box.openapi.yaml
  • scripts/test/e2e/README.md
  • scripts/test/e2e/bootstrap.sh
  • scripts/test/e2e/cases/conftest.py
  • scripts/test/e2e/cases/test_c_entry.py
  • scripts/test/e2e/cases/test_cli_detach_recovery.py
  • scripts/test/e2e/cases/test_cli_entry.py
  • scripts/test/e2e/cases/test_error_code_mapping.py
  • scripts/test/e2e/cases/test_go_entry.py
  • scripts/test/e2e/cases/test_node_entry.py
  • scripts/test/e2e/cases/test_quota_enforcement.py
  • scripts/test/e2e/fixture_setup.py
  • scripts/test/e2e/sdks/c/e2e_basic.c
  • scripts/test/e2e/sdks/go/e2e_basic.go
  • scripts/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

Comment thread apps/api/src/box/dto/create-box.dto.ts
Comment thread apps/api/src/box/services/box.service.warm-pool.spec.ts
Comment thread apps/dashboard/src/components/admin/adminHelpers.ts Outdated
Comment thread openapi/box.openapi.yaml Outdated
Comment thread scripts/test/e2e/cases/test_cli_entry.py Outdated
law-chain-hot added a commit that referenced this pull request Jun 13, 2026
- 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.
@law-chain-hot

Copy link
Copy Markdown
Contributor Author

Thanks @coderabbitai — all five addressed in 127a7a7. Quick notes:

  • create-box.dto image @ApiPropertyOptional ✅ added (it was the only field on this DTO missing it).

  • adminHelpers findBoxById ✅ now exact, case-sensitive — box ids are 12-char base62, so lowercasing could resolve the wrong box. Good catch.

  • openapi image default/example ✅ switched to pinned ghcr refs so they no longer contradict the allowlist wording.

  • e2e UUID_RE ✅ replaced with an anchored 12-char base62 matcher (BOX_ID_RE) across all CLI/SDK entry tests; assertion text updated uuid → box id.

  • warm-pool.spec digest ✅ fixed, but not the suggested way. Asserting box.image against assertSupportedImage(undefined) would make the test tautological (production sets box.image = assertSupportedImage(undefined), and the assertion would call the same function) — nothing would cross a boundary. Instead I isolate BOXLITE_SYSTEM_* env in beforeEach/afterEach so the pinned fallback is deterministic, and keep the literal assertion. This removes the env-driven flakiness while still proving "default → base ref".

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@law-chain-hot: All five look good, thanks for the detailed notes.

The warm-pool env-isolation approach is the right call. You're correct that assertSupportedImage(undefined) in both production code and the test assertion would only prove that calling the function twice returns the same value — it wouldn't verify that the image written to box.image is actually the pinned base ref. Isolating BOXLITE_SYSTEM_* in beforeEach/afterEach so the fallback path is deterministic, then asserting the literal digest, is the meaningful boundary-crossing check.

🐇 ✨

law-chain-hot added a commit that referenced this pull request Jun 13, 2026
- 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.
@law-chain-hot law-chain-hot force-pushed the feat/box-image-identity branch from 127a7a7 to 4b096b2 Compare June 13, 2026 09:40
- 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.
@law-chain-hot law-chain-hot force-pushed the feat/box-image-identity branch from 4b096b2 to 73e8d6c Compare June 13, 2026 09:43
@law-chain-hot

Copy link
Copy Markdown
Contributor Author

Superseded by #756 — same image + single-identity + drop-boxId work, rebuilt cleanly against main: no cloudBox migration, no SDK package removal (those stay in #748). Only touches sdk-typescript/src/Box.ts as a field mirror so it compiles after the regen.

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