feat(volumes): add hotmount and blockmount volume types#64
Conversation
|
Thank you for your contribution! Before we can merge it, we need you to sign the Daytona Contributor License Agreement. You only need to do this once, and it will cover all of your future contributions to this repository. If you are signing on behalf of an entity (for example, your employer), please also reply with the entity's legal name and your role or title, and confirm that you are authorized to bind it. Please read the CLA and, if you agree, post the following comment on this PR: I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
Sync clients with the API volume-types refactor: regenerate all API
clients (TS, Go, Python, Python-async, Ruby, Java) from the updated
openapi-specs/api.json, and add the hand-written SDK/CLI/example support.
Spec additions: VolumeType, HotmountRegion, BlockmountConflict,
CreateVolumeMountToken, VolumeMountTokenDto; region/size/type fields on
Volume/CreateVolume/SandboxVolume/Region; and the /volumes/hotmount-regions,
/volumes/blockmount-regions and /volumes/{volumeId}/mount-token endpoints.
SDKs: Volume/VolumeType surface, Sandbox.mountVolume (hotmount, on-the-fly
token bootstrap) and Sandbox.pullVolumes (blockmount explicit pull) across
all languages; CLI volume create/list/info; new blockmount and hotmount
TypeScript examples.
There was a problem hiding this comment.
34 issues found across 113 files
You’re at about 90% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="openapi-specs/api.json">
<violation number="1" location="openapi-specs/api.json:19805">
P3: Blockmount conflict metadata loses type safety because `winner` and `reason` document finite wire values but model them as unrestricted strings. Generated SDK users can therefore pass or receive values that the schema does not describe, and cannot exhaustively handle the known cases. Defining enums for `winner` (`ours`, `theirs`) and `reason` (`mtime`, `tie`, `modify-over-delete`, `type`) would make the generated models match the API contract.</violation>
<violation number="2" location="openapi-specs/api.json:20025">
P2: Mount-token expiration is exposed as an untyped string in the generated clients because `expiresAt` omits the `date-time` format, even though the value is an ISO timestamp. This differs from the existing timestamp schemas and prevents clients such as TypeScript and Go from generating their normal date/time types. Adding `format: date-time` keeps this response consistent with the rest of the API.</violation>
</file>
<file name="examples/typescript/hotmount/index.ts">
<violation number="1" location="examples/typescript/hotmount/index.ts:44">
P2: When no hotmount region is available, this continues with `region === undefined` and attempts to create the demo volume without the required placement, producing a later and less actionable API failure (or unintended default placement). As in the blockmount example, handling the empty list before selecting a region would give users the correct configuration error.</violation>
<violation number="2" location="examples/typescript/hotmount/index.ts:65">
P2: A failure after either sandbox is created leaves billable sandbox resources running because cleanup is not protected by `finally`. Track the created sandboxes and delete them in a `finally` block, including the partial-creation case.</violation>
<violation number="3" location="examples/typescript/hotmount/index.ts:92">
P2: If the second sandbox never sees the shared file, the example still exits successfully after logging `<not visible yet>`. That masks hotmount synchronization or mounting failures in manual/automated verification; after the retry loop, an undefined `contents` should throw instead of being treated as a successful demo result.</violation>
</file>
<file name="sdk-go/pkg/daytona/sandbox.go">
<violation number="1" location="sdk-go/pkg/daytona/sandbox.go:489">
P2: `MountVolume` accepts relative mount paths even though its contract requires an absolute sandbox path. Validating `mountPath` before building the command would prevent mounts landing in unintended directories and provide a clearer error.</violation>
<violation number="2" location="sdk-go/pkg/daytona/sandbox.go:588">
P1: Mounting can execute unintended shell fragments because the generated `env` assignments interpolate unescaped values into single-quoted shell strings. `mountPath` is caller-provided and is inserted into `SEAWEED_MOUNT_DIR` without escaping, so a `'` in the path can break quoting and change the executed command. Escaping shell values before formatting the command would prevent both injection and quote-related mount failures.</violation>
</file>
<file name="sdk-go/pkg/types/types.go">
<violation number="1" location="sdk-go/pkg/types/types.go:147">
P3: The SDK comment currently marks `Volume.Region` as hotmount-only, but the API returns region for blockmount volumes too. Updating this comment avoids consumers treating valid blockmount region metadata as absent/irrelevant.</violation>
</file>
<file name="sdk-ruby/lib/daytona/sandbox.rb">
<violation number="1" location="sdk-ruby/lib/daytona/sandbox.rb:360">
P3: The new volume APIs are not instrumented, so mount/pull calls won’t emit Sandbox telemetry like other public operations. Adding `:mount_volume` and `:pull_volumes` to `instrument(...)` keeps observability consistent.</violation>
<violation number="2" location="sdk-ruby/lib/daytona/sandbox.rb:366">
P2: Forked Sandbox objects will always fail `mount_volume` with “Volume service is not available” because the fork constructor path does not pass `volume_service`. Propagating the parent volume service in `experimental_fork` would keep volume features working after fork.</violation>
<violation number="3" location="sdk-ruby/lib/daytona/sandbox.rb:712">
P1: Mounting can execute unintended shell fragments when the mount path (or token-derived value) contains a single quote, because `build_hotmount_mount_command` inserts values into shell assignments without escaping. Escaping shell values before building `env` assignments would prevent command injection in `sandbox.mount_volume`.</violation>
</file>
<file name="cli/cmd/volume/create.go">
<violation number="1" location="cli/cmd/volume/create.go:35">
P1: Invalid `--type` values are not rejected here: the generated `NewVolumeTypeFromValue` returns the unknown-default sentinel (`"11184809"`) with a nil error for unrecognized strings. As a result, typos are sent to the API rather than producing the CLI's advertised validation error. Validate explicitly against the three user-facing `VolumeType` constants before assigning `createVolume.Type`.</violation>
<violation number="2" location="cli/cmd/volume/create.go:42">
P2: `--region` can currently be supplied without a non-legacy `--type`, producing a legacy request even though `CreateVolume.Region` is explicitly disallowed for legacy volumes. Local validation would return an actionable CLI error before making a request that the API must reject.</violation>
<violation number="3" location="cli/cmd/volume/create.go:62">
P3: The checked-in command reference now tells users to pass the removed `--size` flag and omits both new flags. Regenerating `cli/docs/daytona_volume_create.md` alongside these registrations would keep published CLI usage accurate.</violation>
</file>
<file name="cli/views/volume/list.go">
<violation number="1" location="cli/views/volume/list.go:31">
P3: The volume list still omits the region even though region is now a key placement attribute and this change is intended to add type/region to list output. Only `Type` was added to the headers and row data, while `RenderInfo` does show `volume.Region`. Consider adding a `Region` column (using `-` when the nullable field is absent) so hotmount/blockmount placement is visible without inspecting every volume.</violation>
</file>
<file name="sdk-java/src/main/java/io/daytona/sdk/Sandbox.java">
<violation number="1" location="sdk-java/src/main/java/io/daytona/sdk/Sandbox.java:492">
P1: A mount path containing a single quote is interpreted as shell syntax inside the Sandbox because `appendEnvVar` inserts values verbatim between single quotes. For example, a crafted `mountPath` can terminate `SEAWEED_MOUNT_DIR='...'` and append another command when `process.executeCommand` runs it. Escaping embedded single quotes (for every value, including token DTO fields) before constructing the command would keep these values as data.</violation>
</file>
<file name="sdk-python/src/daytona/common/volume.py">
<violation number="1" location="sdk-python/src/daytona/common/volume.py:40">
P2: Hotmount fails for valid paths containing `'`, and crafted values can inject shell syntax into the sandbox command. Quote each value with `shlex.quote()` instead of manually surrounding it with apostrophes.</violation>
</file>
<file name="sdk-java/src/main/java/io/daytona/sdk/model/Volume.java">
<violation number="1" location="sdk-java/src/main/java/io/daytona/sdk/model/Volume.java:19">
P3: Returned volumes lose the SDK's `VolumeType` type safety because this public field is exposed as `String`, allowing invalid values and forcing callers to use literals. Consider exposing `VolumeType` consistently with `VolumeService.create` and preserving the enum in `toVolume`.</violation>
<violation number="2" location="sdk-java/src/main/java/io/daytona/sdk/model/Volume.java:88">
P3: Blockmount volumes also return a region, so this public getter documentation incorrectly tells users to expect `null`. Describe it as the volume data/deployment region and reserve `null` for legacy volumes.</violation>
</file>
<file name="sdk-python/src/daytona/_async/sandbox.py">
<violation number="1" location="sdk-python/src/daytona/_async/sandbox.py:259">
P2: Relative `mount_path` values are accepted even though this API requires an absolute sandbox path, causing the mount location to depend on the toolbox process working directory. Validate the path before requesting the token or executing the mount command.</violation>
<violation number="2" location="sdk-python/src/daytona/_async/sandbox.py:260">
P1: A crafted `mount_path` can execute arbitrary shell commands inside the sandbox because `build_hotmount_mount_command` interpolates it into a single-quoted shell assignment without escaping. Quote every environment value with `shlex.quote` in the command builder before passing this user-controlled value to `_process.exec`.</violation>
</file>
<file name="sdk-python/src/daytona/_sync/sandbox.py">
<violation number="1" location="sdk-python/src/daytona/_sync/sandbox.py:276">
P1: A crafted `mount_path` can inject arbitrary shell commands into the Sandbox because `build_hotmount_mount_command` embeds it in a single-quoted assignment without shell escaping. Shell-quote every interpolated environment value (for example with `shlex.quote`) before passing the command to `Process.exec`.</violation>
</file>
<file name="sdk-python/src/daytona/_sync/volume.py">
<violation number="1" location="sdk-python/src/daytona/_sync/volume.py:51">
P3: `get()` now gives users the opposite blockmount contract: `region` is optional and selects the region-local store; it does not pin attachment to that region. Aligning this text with `create()` avoids callers adding unnecessary region-selection logic.</violation>
<violation number="2" location="sdk-python/src/daytona/_sync/volume.py:108">
P3: The return-value documentation advertises `HotmountRegion.id`, which does not exist and would raise `AttributeError` if consumers follow it. The field is named `region`, as the example below already shows.</violation>
</file>
<file name="sdk-python/src/daytona/_async/volume.py">
<violation number="1" location="sdk-python/src/daytona/_async/volume.py:51">
P3: `get(..., create=True, region=...)` now documents blockmount `region` as required and as an attach pin, contradicting both `create()` and the API contract where it is optional and only controls data placement. Aligning this text avoids steering SDK users toward incorrect creation and attachment assumptions.</violation>
<violation number="2" location="sdk-python/src/daytona/_async/volume.py:106">
P3: The new async volume-type parameters and three API wrappers have no unit coverage, so argument-order, forwarding, or await regressions can pass the SDK test suite. Tests that assert the exact generated-client calls and returned values would protect this new public surface.</violation>
</file>
<file name="examples/typescript/blockmount/index.ts">
<violation number="1" location="examples/typescript/blockmount/index.ts:34">
P2: A transient refresh, authentication, or server failure is treated as proof that teardown finished, so the example may try deleting a volume while its sandbox is still active. The catch should return only for `DaytonaNotFoundError` and rethrow every other error.</violation>
<violation number="2" location="examples/typescript/blockmount/index.ts:74">
P2: A partial sandbox-creation failure leaves the successful sandbox (and volume) running because `Promise.all` rejects before either reference is assigned. Tracking each result (for example with `Promise.allSettled`) and cleaning created resources in `finally` would prevent leaked billable resources.</violation>
<violation number="3" location="examples/typescript/blockmount/index.ts:88">
P2: The demo's expected B-wins result is flaky when the two runners' clocks differ by more than this delay, because merge order is based on runner-local mtimes rather than client request order. Setting an explicit ordered mtime inside the sandboxes, or avoiding an asserted winner, would make the example deterministic.</violation>
</file>
<file name="sdk-typescript/src/Sandbox.ts">
<violation number="1" location="sdk-typescript/src/Sandbox.ts:64">
P0: A mount path containing a single quote can break out of the environment assignment and execute arbitrary shell commands, potentially as root via `sudo`. Shell-escape every assignment value before building the command (including API-provided token fields).</violation>
<violation number="2" location="sdk-typescript/src/Sandbox.ts:183">
P2: Hotmount mounting is unavailable on every Sandbox returned by `_experimental_fork()` because that constructor call does not propagate `this.volumeService`. Pass the service into the forked `Sandbox` just as `Daytona.create/get/list` do.</violation>
<violation number="3" location="sdk-typescript/src/Sandbox.ts:277">
P3: Relative or empty `mountPath` values are accepted despite the absolute-path contract, so the volume can be mounted under the process working directory instead of the caller's intended location. Validate that `mountPath` starts with `/` and raise `DaytonaValidationError` otherwise.</violation>
</file>
<file name="sdk-go/pkg/daytona/volume.go">
<violation number="1" location="sdk-go/pkg/daytona/volume.go:118">
P3: The new Create parameter docs understate region support and can mislead users selecting blockmount placement. Wording this as a general region option would match current behavior and type docs.</violation>
<violation number="2" location="sdk-go/pkg/daytona/volume.go:175">
P2: GetMountToken can panic on invalid input because `volume.ID` is dereferenced unconditionally. Adding a nil guard keeps this path in normal error handling instead of crashing the caller.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| } | ||
| const envAssignments = Object.entries(envVars) | ||
| .filter(([, value]) => value !== undefined && value !== '') | ||
| .map(([key, value]) => `${key}='${value}'`) |
There was a problem hiding this comment.
P0: A mount path containing a single quote can break out of the environment assignment and execute arbitrary shell commands, potentially as root via sudo. Shell-escape every assignment value before building the command (including API-provided token fields).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-typescript/src/Sandbox.ts, line 64:
<comment>A mount path containing a single quote can break out of the environment assignment and execute arbitrary shell commands, potentially as root via `sudo`. Shell-escape every assignment value before building the command (including API-provided token fields).</comment>
<file context>
@@ -41,6 +41,43 @@ import { ComputerUse } from './ComputerUse'
+ }
+ const envAssignments = Object.entries(envVars)
+ .filter(([, value]) => value !== undefined && value !== '')
+ .map(([key, value]) => `${key}='${value}'`)
+ .join(' ')
+
</file context>
| .map(([key, value]) => `${key}='${value}'`) | |
| .map(([key, value]) => `${key}='${String(value).replace(/'/g, "'\\''")}'`) |
| raise DaytonaError("Volume service is not available for this Sandbox instance.") | ||
|
|
||
| mount_token = await self._volume_service.get_mount_token(volume) | ||
| command = build_hotmount_mount_command(mount_token, mount_path) |
There was a problem hiding this comment.
P1: A crafted mount_path can execute arbitrary shell commands inside the sandbox because build_hotmount_mount_command interpolates it into a single-quoted shell assignment without escaping. Quote every environment value with shlex.quote in the command builder before passing this user-controlled value to _process.exec.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-python/src/daytona/_async/sandbox.py, line 260:
<comment>A crafted `mount_path` can execute arbitrary shell commands inside the sandbox because `build_hotmount_mount_command` interpolates it into a single-quoted shell assignment without escaping. Quote every environment value with `shlex.quote` in the command builder before passing this user-controlled value to `_process.exec`.</comment>
<file context>
@@ -217,6 +226,85 @@ async def get_work_dir(self) -> str:
+ raise DaytonaError("Volume service is not available for this Sandbox instance.")
+
+ mount_token = await self._volume_service.get_mount_token(volume)
+ command = build_hotmount_mount_command(mount_token, mount_path)
+ response = await self._process.exec(command)
+ if response.exit_code != 0:
</file context>
| raise DaytonaError("Volume service is not available for this Sandbox instance.") | ||
|
|
||
| mount_token = self._volume_service.get_mount_token(volume) | ||
| command = build_hotmount_mount_command(mount_token, mount_path) |
There was a problem hiding this comment.
P1: A crafted mount_path can inject arbitrary shell commands into the Sandbox because build_hotmount_mount_command embeds it in a single-quoted assignment without shell escaping. Shell-quote every interpolated environment value (for example with shlex.quote) before passing the command to Process.exec.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-python/src/daytona/_sync/sandbox.py, line 276:
<comment>A crafted `mount_path` can inject arbitrary shell commands into the Sandbox because `build_hotmount_mount_command` embeds it in a single-quoted assignment without shell escaping. Shell-quote every interpolated environment value (for example with `shlex.quote`) before passing the command to `Process.exec`.</comment>
<file context>
@@ -233,6 +242,85 @@ def get_work_dir(self) -> str:
+ raise DaytonaError("Volume service is not available for this Sandbox instance.")
+
+ mount_token = self._volume_service.get_mount_token(volume)
+ command = build_hotmount_mount_command(mount_token, mount_path)
+ response = self._process.exec(command)
+ if response.exit_code != 0:
</file context>
| volumeType, err := apiclient.NewVolumeTypeFromValue(typeFlag) | ||
| if err != nil { | ||
| return fmt.Errorf("invalid volume type %q: must be one of legacy, hotmount, blockmount", typeFlag) | ||
| } | ||
| createVolume.Type = volumeType |
There was a problem hiding this comment.
P1: Invalid --type values are not rejected here: the generated NewVolumeTypeFromValue returns the unknown-default sentinel ("11184809") with a nil error for unrecognized strings. As a result, typos are sent to the API rather than producing the CLI's advertised validation error. Validate explicitly against the three user-facing VolumeType constants before assigning createVolume.Type.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli/cmd/volume/create.go, line 35:
<comment>Invalid `--type` values are not rejected here: the generated `NewVolumeTypeFromValue` returns the unknown-default sentinel (`"11184809"`) with a nil error for unrecognized strings. As a result, typos are sent to the API rather than producing the CLI's advertised validation error. Validate explicitly against the three user-facing `VolumeType` constants before assigning `createVolume.Type`.</comment>
<file context>
@@ -27,9 +27,23 @@ var CreateCmd = &cobra.Command{
+ }
+
+ if typeFlag != "" {
+ volumeType, err := apiclient.NewVolumeTypeFromValue(typeFlag)
+ if err != nil {
+ return fmt.Errorf("invalid volume type %q: must be one of legacy, hotmount, blockmount", typeFlag)
</file context>
| volumeType, err := apiclient.NewVolumeTypeFromValue(typeFlag) | |
| if err != nil { | |
| return fmt.Errorf("invalid volume type %q: must be one of legacy, hotmount, blockmount", typeFlag) | |
| } | |
| createVolume.Type = volumeType | |
| volumeType := apiclient.VolumeType(typeFlag) | |
| switch volumeType { | |
| case apiclient.VOLUMETYPE_LEGACY, apiclient.VOLUMETYPE_HOTMOUNT, apiclient.VOLUMETYPE_BLOCKMOUNT: | |
| createVolume.Type = &volumeType | |
| default: | |
| return fmt.Errorf("invalid volume type %q: must be one of legacy, hotmount, blockmount", typeFlag) | |
| } |
| 'SEAWEED_VERSION' => mount_token.version | ||
| } | ||
| env_assignments = env_vars.reject { |_, value| value.nil? || value.empty? } | ||
| .map { |key, value| "#{key}='#{value}'" } |
There was a problem hiding this comment.
P1: Mounting can execute unintended shell fragments when the mount path (or token-derived value) contains a single quote, because build_hotmount_mount_command inserts values into shell assignments without escaping. Escaping shell values before building env assignments would prevent command injection in sandbox.mount_volume.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-ruby/lib/daytona/sandbox.rb, line 712:
<comment>Mounting can execute unintended shell fragments when the mount path (or token-derived value) contains a single quote, because `build_hotmount_mount_command` inserts values into shell assignments without escaping. Escaping shell values before building `env` assignments would prevent command injection in `sandbox.mount_volume`.</comment>
<file context>
@@ -624,6 +692,40 @@ def pause(timeout: DEFAULT_TIMEOUT)
+ 'SEAWEED_VERSION' => mount_token.version
+ }
+ env_assignments = env_vars.reject { |_, value| value.nil? || value.empty? }
+ .map { |key, value| "#{key}='#{value}'" }
+ .join(' ')
+ # The bootstrap (and init.sh itself) requires curl. Fail loudly if it is missing or the
</file context>
| .map { |key, value| "#{key}='#{value}'" } | |
| .map { |key, value| "#{key}='#{value.to_s.gsub("'", %q('"'"'))}'" } |
| * await sandbox.mountVolume(volume, "/mnt/shared"); | ||
| */ | ||
| @WithInstrumentation() | ||
| public async mountVolume(volume: Volume, mountPath: string): Promise<void> { |
There was a problem hiding this comment.
P3: Relative or empty mountPath values are accepted despite the absolute-path contract, so the volume can be mounted under the process working directory instead of the caller's intended location. Validate that mountPath starts with / and raise DaytonaValidationError otherwise.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-typescript/src/Sandbox.ts, line 277:
<comment>Relative or empty `mountPath` values are accepted despite the absolute-path contract, so the volume can be mounted under the process working directory instead of the caller's intended location. Validate that `mountPath` starts with `/` and raise `DaytonaValidationError` otherwise.</comment>
<file context>
@@ -215,6 +253,86 @@ export class Sandbox {
+ * await sandbox.mountVolume(volume, "/mnt/shared");
+ */
+ @WithInstrumentation()
+ public async mountVolume(volume: Volume, mountPath: string): Promise<void> {
+ if (volume.type !== VolumeType.HOTMOUNT) {
+ throw new DaytonaValidationError(
</file context>
| public async mountVolume(volume: Volume, mountPath: string): Promise<void> { | |
| public async mountVolume(volume: Volume, mountPath: string): Promise<void> { | |
| if (!mountPath.startsWith('/')) { | |
| throw new DaytonaValidationError('Hotmount mount path must be absolute.') | |
| } |
| // | ||
| // Parameters: | ||
| // - name: Unique name for the volume | ||
| // - opts: Optional volume type and hotmount region. |
There was a problem hiding this comment.
P3: The new Create parameter docs understate region support and can mislead users selecting blockmount placement. Wording this as a general region option would match current behavior and type docs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-go/pkg/daytona/volume.go, line 118:
<comment>The new Create parameter docs understate region support and can mislead users selecting blockmount placement. Wording this as a general region option would match current behavior and type docs.</comment>
<file context>
@@ -115,6 +115,7 @@ func (v *VolumeService) Get(ctx context.Context, name string) (*types.Volume, er
//
// Parameters:
// - name: Unique name for the volume
+// - opts: Optional volume type and hotmount region.
//
// Example:
</file context>
| // - opts: Optional volume type and hotmount region. | |
| // - opts: Optional volume type and region. |
| OrganizationID string `json:"organizationId"` | ||
| Type string `json:"type"` | ||
| SizeInGb *float32 `json:"sizeInGb,omitempty"` | ||
| // Region is the hotmount region the volume lives in. Set only for hotmount volumes. |
There was a problem hiding this comment.
P3: The SDK comment currently marks Volume.Region as hotmount-only, but the API returns region for blockmount volumes too. Updating this comment avoids consumers treating valid blockmount region metadata as absent/irrelevant.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-go/pkg/types/types.go, line 147:
<comment>The SDK comment currently marks `Volume.Region` as hotmount-only, but the API returns region for blockmount volumes too. Updating this comment avoids consumers treating valid blockmount region metadata as absent/irrelevant.</comment>
<file context>
@@ -139,14 +139,84 @@ type PaginatedSnapshots struct {
+ OrganizationID string `json:"organizationId"`
+ Type string `json:"type"`
+ SizeInGb *float32 `json:"sizeInGb,omitempty"`
+ // Region is the hotmount region the volume lives in. Set only for hotmount volumes.
+ Region *string `json:"region,omitempty"`
+ // Shared is the hotmount sharing mode. Set only for hotmount volumes.
</file context>
| # @example | ||
| # volume = daytona.volume.get('shared-fs') | ||
| # sandbox.mount_volume(volume, '/mnt/shared') | ||
| def mount_volume(volume, mount_path) |
There was a problem hiding this comment.
P3: The new volume APIs are not instrumented, so mount/pull calls won’t emit Sandbox telemetry like other public operations. Adding :mount_volume and :pull_volumes to instrument(...) keeps observability consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdk-ruby/lib/daytona/sandbox.rb, line 360:
<comment>The new volume APIs are not instrumented, so mount/pull calls won’t emit Sandbox telemetry like other public operations. Adding `:mount_volume` and `:pull_volumes` to `instrument(...)` keeps observability consistent.</comment>
<file context>
@@ -338,6 +340,72 @@ def update_env(env: nil, unset: nil)
+ # @example
+ # volume = daytona.volume.get('shared-fs')
+ # sandbox.mount_volume(volume, '/mnt/shared')
+ def mount_volume(volume, mount_path)
+ unless volume.type == DaytonaApiClient::VolumeType::HOTMOUNT
+ raise Sdk::Error,
</file context>
| SortVolumes(&volumeList) | ||
|
|
||
| headers := []string{"Volume", "State", "Size", "Created"} | ||
| headers := []string{"Volume", "Type", "State", "Size", "Created"} |
There was a problem hiding this comment.
P3: The volume list still omits the region even though region is now a key placement attribute and this change is intended to add type/region to list output. Only Type was added to the headers and row data, while RenderInfo does show volume.Region. Consider adding a Region column (using - when the nullable field is absent) so hotmount/blockmount placement is visible without inspecting every volume.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli/views/volume/list.go, line 31:
<comment>The volume list still omits the region even though region is now a key placement attribute and this change is intended to add type/region to list output. Only `Type` was added to the headers and row data, while `RenderInfo` does show `volume.Region`. Consider adding a `Region` column (using `-` when the nullable field is absent) so hotmount/blockmount placement is visible without inspecting every volume.</comment>
<file context>
@@ -27,7 +28,7 @@ func ListVolumes(volumeList []apiclient.VolumeDto, activeOrganizationName *strin
SortVolumes(&volumeList)
- headers := []string{"Volume", "State", "Size", "Created"}
+ headers := []string{"Volume", "Type", "State", "Size", "Created"}
data := [][]string{}
</file context>
b7db49f to
0d86632
Compare
Description
Syncs the clients monorepo with the API's volume-types feature: regenerates all API clients from the updated OpenAPI spec and adds the hand-written SDK, CLI, and example support for the three volume types (
legacy,hotmount,blockmount).Spec
openapi-specs/api.jsonand regenerates all six API clients (api-clientTS,api-client-go,api-client-python,api-client-python-async,api-client-ruby,api-client-java).VolumeType,HotmountRegion,BlockmountConflict,CreateVolumeMountToken,VolumeMountTokenDto;type/region/sizeInGb/sharedfields onVolume/CreateVolume/SandboxVolumeandblockmountEnabledonRegion; new endpointsGET /volumes/hotmount-regions,GET /volumes/blockmount-regions,POST /volumes/{volumeId}/mount-token.SDKs (TypeScript / Python / Go / Ruby / Java)
Volume/VolumeTypesurface and typed create-with-type/region.volume.listHotmountRegions()/listBlockmountRegions().sandbox.mountVolume(volume, path)— mounts a hotmount volume on the fly: mints a scoped mount token and bootstraps the in-sandbox FUSE agent from the region'sinit.sh(fails loudly ifcurl/download is missing).sandbox.pullVolumes(volume?)— explicit blockmount sync over the toolbox channel: commits the sandbox's local writes and applies the volume's latest merged state in place, no restart.CLI
volume createtype/region handling and type/region columns inlist/info.Examples
examples/typescript/blockmount(concurrent two-writer merge + explicit pull demo) andexamples/typescript/hotmount(mount-on-the-fly + multi-writer propagation).Depends on the companion API PR in
daytonaio/daytona-ai(branchfeat/volume-types-refactor-prod), which serves the spec and endpoints these clients target.Documentation
Notes
openapi-specs/api.json— the diff is limited to the volume feature (no version churn or unrelated drift).api-client-go,sdk-go,cli), Python (compile + editable install), Ruby (syntax), Java SDK (compileJavaoffline).Summary by cubic
Adds hotmount and blockmount volume types across API clients, SDKs, and the CLI to enable mount-on-the-fly and explicit sync workflows. Regenerates clients from the updated OpenAPI spec with region discovery and mount token support.
/volumes/hotmount-regions,/volumes/blockmount-regions,/volumes/{volumeId}/mount-token; new models (VolumeType,HotmountRegion,BlockmountConflict,CreateVolumeMountToken,VolumeMountTokenDto); added fields onVolume/CreateVolume/SandboxVolumeandblockmountEnabledonRegion. Regeneratedapi-client(TS),api-client-go,api-client-python,api-client-python-async,api-client-ruby,api-client-java.type/region; list hotmount/blockmount regions; in-sandboxmountVolumefor hotmount (mints token and bootstraps agent);pullVolumesfor blockmount to apply latest state without restart.volume createsupports--typeand--region;volume list/infoshow type, region, size, and shared.examples/typescript/blockmountandexamples/typescript/hotmount.Written for commit 0d86632. Summary will update on new commits.