Class: Daytona::Sandbox

Inherits:
Object
  • Object
show all
Includes:
Instrumentation
Defined in:
lib/daytona/sandbox.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

DEFAULT_TIMEOUT =
60
SANDBOX_METRIC_FIELD_BY_NAME =
{
  'daytona.sandbox.cpu.utilization' => :cpu_used_pct,
  'daytona.sandbox.cpu.limit' => :cpu_count,
  'daytona.sandbox.memory.usage' => :mem_used,
  'daytona.sandbox.memory.limit' => :mem_total,
  'daytona.sandbox.memory.cache' => :mem_cache,
  'daytona.sandbox.filesystem.usage' => :disk_used,
  'daytona.sandbox.filesystem.total' => :disk_total
}.freeze
SANDBOX_METRIC_NAMES =
SANDBOX_METRIC_FIELD_BY_NAME.keys.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Instrumentation

included

Constructor Details

#initialize(sandbox_dto:, config:, sandbox_api:, subscription_manager:, otel_state: nil, analytics_api_url_provider: nil) ⇒ Sandbox

Internal — obtain sandboxes via Daytona::Daytona#create, Daytona::Daytona#get, or Daytona::Daytona#list rather than constructing directly.



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/daytona/sandbox.rb', line 214

def initialize(sandbox_dto:, config:, sandbox_api:, subscription_manager:, otel_state: nil, analytics_api_url_provider: nil) # rubocop:disable Metrics/MethodLength
  @state_waiters = []
  @waiter_mutex = Mutex.new
  process_response(sandbox_dto)
  @config = config
  @sandbox_api = sandbox_api
  @otel_state = otel_state
  @analytics_api_url_provider = analytics_api_url_provider
  @subscription_manager = subscription_manager
  @sub_id = nil
  @signing_key = nil
  @signing_key_fetched_at = 0

  # Create toolbox API clients with dynamic configuration
  @toolbox_api_config = build_toolbox_api_config
  toolbox_api_config = @toolbox_api_config

  # Helper to create API client with authentication header
  create_authenticated_client = lambda do
    client = DaytonaToolboxApiClient::ApiClient.new(toolbox_api_config)
    client.default_headers['Authorization'] = "Bearer #{config.api_key || config.jwt_token}"
    client.default_headers['X-Daytona-Source'] = 'sdk-ruby'
    client.default_headers['X-Daytona-SDK-Version'] = Sdk::VERSION
    client.default_headers['X-Daytona-Organization-ID'] = config.organization_id if config.jwt_token
    client.user_agent = "sdk-ruby/#{Sdk::VERSION}"
    client
  end

  process_api = DaytonaToolboxApiClient::ProcessApi.new(create_authenticated_client.call)
  fs_api = DaytonaToolboxApiClient::FileSystemApi.new(create_authenticated_client.call)
  git_api = DaytonaToolboxApiClient::GitApi.new(create_authenticated_client.call)
  lsp_api = DaytonaToolboxApiClient::LspApi.new(create_authenticated_client.call)
  computer_use_api = DaytonaToolboxApiClient::ComputerUseApi.new(create_authenticated_client.call)
  interpreter_api = DaytonaToolboxApiClient::InterpreterApi.new(create_authenticated_client.call)
  info_api = DaytonaToolboxApiClient::InfoApi.new(create_authenticated_client.call)
  server_api = DaytonaToolboxApiClient::ServerApi.new(create_authenticated_client.call)
  system_api = DaytonaToolboxApiClient::SystemApi.new(create_authenticated_client.call)

  @process = Process.new(
    sandbox_id: id,
    toolbox_api: process_api,
    get_preview_link: proc { |port| preview_url(port) },
    language: (labels || {}).fetch(CODE_TOOLBOX_LANGUAGE_LABEL, 'python'),
    otel_state:
  )
  @fs = FileSystem.new(sandbox_id: id, toolbox_api: fs_api, otel_state:)
  @git = Git.new(sandbox_id: id, toolbox_api: git_api, otel_state:)
  @computer_use = ComputerUse.new(sandbox_id: id, toolbox_api: computer_use_api, otel_state:)
  @code_interpreter = CodeInterpreter.new(
    sandbox_id: id,
    toolbox_api: interpreter_api,
    get_preview_link: proc { |port| preview_url(port) },
    otel_state:
  )
  @lsp_api = lsp_api
  @info_api = info_api
  @server_api = server_api
  @system_api = system_api

  ensure_subscribed
end

Instance Attribute Details

#auto_archive_intervalFloat

Returns Auto-archive interval in minutes.

Returns:

  • (Float)

    Auto-archive interval in minutes



147
148
149
# File 'lib/daytona/sandbox.rb', line 147

def auto_archive_interval
  @auto_archive_interval
end

#auto_delete_intervalFloat

(negative value means disabled, 0 means delete immediately upon stopping)

Returns:

  • (Float)

    Auto-delete interval in minutes



151
152
153
# File 'lib/daytona/sandbox.rb', line 151

def auto_delete_interval
  @auto_delete_interval
end

#auto_destroy_atString? (readonly)

Returns When the sandbox will be automatically destroyed (nil if no TTL is set).

Returns:

  • (String, nil)

    When the sandbox will be automatically destroyed (nil if no TTL is set)



163
164
165
# File 'lib/daytona/sandbox.rb', line 163

def auto_destroy_at
  @auto_destroy_at
end

#auto_pause_intervalFloat

Returns Auto-pause interval in minutes (0 means disabled).

Returns:

  • (Float)

    Auto-pause interval in minutes (0 means disabled)



144
145
146
# File 'lib/daytona/sandbox.rb', line 144

def auto_pause_interval
  @auto_pause_interval
end

#auto_stop_intervalFloat

Returns Auto-stop interval in minutes (0 means disabled).

Returns:

  • (Float)

    Auto-stop interval in minutes (0 means disabled)



141
142
143
# File 'lib/daytona/sandbox.rb', line 141

def auto_stop_interval
  @auto_stop_interval
end

#backup_created_atString? (readonly)

Returns The creation timestamp of the last backup. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (String, nil)

    The creation timestamp of the last backup. Not returned by list results; call #refresh on each item to populate.



138
139
140
# File 'lib/daytona/sandbox.rb', line 138

def backup_created_at
  @backup_created_at
end

#backup_stateString (readonly)

Returns The state of the backup.

Returns:

  • (String)

    The state of the backup



134
135
136
# File 'lib/daytona/sandbox.rb', line 134

def backup_state
  @backup_state
end

#build_infoDaytonaApiClient::BuildInfo? (readonly)

Returns Build information for the sandbox if it was created from a dynamic build. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (DaytonaApiClient::BuildInfo, nil)

    Build information for the sandbox if it was created from a dynamic build. Not returned by list results; call #refresh on each item to populate.



160
161
162
# File 'lib/daytona/sandbox.rb', line 160

def build_info
  @build_info
end

#code_interpreterDaytona::CodeInterpreter (readonly)



205
206
207
# File 'lib/daytona/sandbox.rb', line 205

def code_interpreter
  @code_interpreter
end

#computer_useDaytona::ComputerUse (readonly)



202
203
204
# File 'lib/daytona/sandbox.rb', line 202

def computer_use
  @computer_use
end

#configDaytona::Config (readonly)

Returns:



187
188
189
# File 'lib/daytona/sandbox.rb', line 187

def config
  @config
end

#cpuFloat (readonly)

Returns The CPU quota for the sandbox.

Returns:

  • (Float)

    The CPU quota for the sandbox



103
104
105
# File 'lib/daytona/sandbox.rb', line 103

def cpu
  @cpu
end

#created_atString (readonly)

Returns The creation timestamp of the sandbox.

Returns:

  • (String)

    The creation timestamp of the sandbox



166
167
168
# File 'lib/daytona/sandbox.rb', line 166

def created_at
  @created_at
end

#daemon_versionString (readonly)

Returns The version of the daemon running in the sandbox.

Returns:

  • (String)

    The version of the daemon running in the sandbox



181
182
183
# File 'lib/daytona/sandbox.rb', line 181

def daemon_version
  @daemon_version
end

#desired_stateDaytonaApiClient::SandboxDesiredState (readonly)

Returns The desired state of the sandbox.

Returns:

  • (DaytonaApiClient::SandboxDesiredState)

    The desired state of the sandbox



128
129
130
# File 'lib/daytona/sandbox.rb', line 128

def desired_state
  @desired_state
end

#diskFloat (readonly)

Returns The disk quota for the sandbox.

Returns:

  • (Float)

    The disk quota for the sandbox



122
123
124
# File 'lib/daytona/sandbox.rb', line 122

def disk
  @disk
end

#domain_allow_listString? (readonly)

Returns Comma-separated list of allowed domains for the sandbox. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (String, nil)

    Comma-separated list of allowed domains for the sandbox. Not returned by list results; call #refresh on each item to populate.



89
90
91
# File 'lib/daytona/sandbox.rb', line 89

def domain_allow_list
  @domain_allow_list
end

#envHash<String, String>? (readonly)

Returns Environment variables for the sandbox. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (Hash<String, String>, nil)

    Environment variables for the sandbox. Not returned by list results; call #refresh on each item to populate.



71
72
73
# File 'lib/daytona/sandbox.rb', line 71

def env
  @env
end

#error_reasonString (readonly)

Returns The error reason of the sandbox.

Returns:

  • (String)

    The error reason of the sandbox



131
132
133
# File 'lib/daytona/sandbox.rb', line 131

def error_reason
  @error_reason
end

#fsDaytona::FileSystem (readonly)

Returns:



196
197
198
# File 'lib/daytona/sandbox.rb', line 196

def fs
  @fs
end

#gitDaytona::Git (readonly)

Returns:



199
200
201
# File 'lib/daytona/sandbox.rb', line 199

def git
  @git
end

#gpuFloat (readonly)

Returns The GPU quota for the sandbox.

Returns:

  • (Float)

    The GPU quota for the sandbox



106
107
108
# File 'lib/daytona/sandbox.rb', line 106

def gpu
  @gpu
end

#gpu_typeString? (readonly)

Returns The GPU type assigned to the sandbox.

Returns:

  • (String, nil)

    The GPU type assigned to the sandbox



116
117
118
# File 'lib/daytona/sandbox.rb', line 116

def gpu_type
  @gpu_type
end

#idString (readonly)

Returns The ID of the sandbox.

Returns:

  • (String)

    The ID of the sandbox



58
59
60
# File 'lib/daytona/sandbox.rb', line 58

def id
  @id
end

#labelsHash<String, String>

Returns Labels for the sandbox.

Returns:

  • (Hash<String, String>)

    Labels for the sandbox



74
75
76
# File 'lib/daytona/sandbox.rb', line 74

def labels
  @labels
end

#last_activity_atString (readonly)

Returns The last activity timestamp of the sandbox.

Returns:

  • (String)

    The last activity timestamp of the sandbox



172
173
174
# File 'lib/daytona/sandbox.rb', line 172

def last_activity_at
  @last_activity_at
end

#memoryFloat (readonly)

Returns The memory quota for the sandbox.

Returns:

  • (Float)

    The memory quota for the sandbox



119
120
121
# File 'lib/daytona/sandbox.rb', line 119

def memory
  @memory
end

#network_allow_listString? (readonly)

Returns Comma-separated list of allowed CIDR network addresses for the sandbox. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (String, nil)

    Comma-separated list of allowed CIDR network addresses for the sandbox. Not returned by list results; call #refresh on each item to populate.



85
86
87
# File 'lib/daytona/sandbox.rb', line 85

def network_allow_list
  @network_allow_list
end

#network_block_allBoolean? (readonly)

Returns Whether to block all network access for the sandbox. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (Boolean, nil)

    Whether to block all network access for the sandbox. Not returned by list results; call #refresh on each item to populate.



81
82
83
# File 'lib/daytona/sandbox.rb', line 81

def network_block_all
  @network_block_all
end

#organization_idString (readonly)

Returns The organization ID of the sandbox.

Returns:

  • (String)

    The organization ID of the sandbox



61
62
63
# File 'lib/daytona/sandbox.rb', line 61

def organization_id
  @organization_id
end

#otel_endpoint_overrideString? (readonly)

Returns OpenTelemetry endpoint override for the sandbox. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (String, nil)

    OpenTelemetry endpoint override for the sandbox. Not returned by list results; call #refresh on each item to populate.



97
98
99
# File 'lib/daytona/sandbox.rb', line 97

def otel_endpoint_override
  @otel_endpoint_override
end

#outbound_proxy_urlString? (readonly)

Returns Outbound proxy URL to route the sandbox HTTP(S) traffic through. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (String, nil)

    Outbound proxy URL to route the sandbox HTTP(S) traffic through. Not returned by list results; call #refresh on each item to populate.



93
94
95
# File 'lib/daytona/sandbox.rb', line 93

def outbound_proxy_url
  @outbound_proxy_url
end

#processDaytona::Process (readonly)

Returns:



193
194
195
# File 'lib/daytona/sandbox.rb', line 193

def process
  @process
end

#publicBoolean (readonly)

Returns Whether the sandbox http preview is public.

Returns:

  • (Boolean)

    Whether the sandbox http preview is public



77
78
79
# File 'lib/daytona/sandbox.rb', line 77

def public
  @public
end

#sandbox_apiDaytonaApiClient::SandboxApi (readonly)

Returns:

  • (DaytonaApiClient::SandboxApi)


190
191
192
# File 'lib/daytona/sandbox.rb', line 190

def sandbox_api
  @sandbox_api
end

#sandbox_classString? (readonly)

Returns The class of the sandbox (e.g. "linux-vm", "container").

Returns:

  • (String, nil)

    The class of the sandbox (e.g. "linux-vm", "container")



175
176
177
# File 'lib/daytona/sandbox.rb', line 175

def sandbox_class
  @sandbox_class
end

#snapshotString (readonly)

Returns The snapshot used for the sandbox.

Returns:

  • (String)

    The snapshot used for the sandbox



64
65
66
# File 'lib/daytona/sandbox.rb', line 64

def snapshot
  @snapshot
end

#spotBoolean (readonly)

Returns Whether this is a spot GPU sandbox. Spot sandboxes may be instantly terminated to free capacity for on-demand GPU sandboxes.

Returns:

  • (Boolean)

    Whether this is a spot GPU sandbox. Spot sandboxes may be instantly terminated to free capacity for on-demand GPU sandboxes.



110
111
112
# File 'lib/daytona/sandbox.rb', line 110

def spot
  @spot
end

#spot_evicted_atString? (readonly)

Returns When the sandbox was evicted by spot preemption.

Returns:

  • (String, nil)

    When the sandbox was evicted by spot preemption



113
114
115
# File 'lib/daytona/sandbox.rb', line 113

def spot_evicted_at
  @spot_evicted_at
end

#stateDaytonaApiClient::SandboxState (readonly)

Returns The state of the sandbox.

Returns:

  • (DaytonaApiClient::SandboxState)

    The state of the sandbox



125
126
127
# File 'lib/daytona/sandbox.rb', line 125

def state
  @state
end

#targetString (readonly)

Returns The target environment for the sandbox.

Returns:

  • (String)

    The target environment for the sandbox



100
101
102
# File 'lib/daytona/sandbox.rb', line 100

def target
  @target
end

#toolbox_proxy_urlString (readonly)

Returns The toolbox proxy URL used to reach the sandbox's toolbox API.

Returns:

  • (String)

    The toolbox proxy URL used to reach the sandbox's toolbox API



184
185
186
# File 'lib/daytona/sandbox.rb', line 184

def toolbox_proxy_url
  @toolbox_proxy_url
end

#updated_atString (readonly)

Returns The last update timestamp of the sandbox.

Returns:

  • (String)

    The last update timestamp of the sandbox



169
170
171
# File 'lib/daytona/sandbox.rb', line 169

def updated_at
  @updated_at
end

#userString (readonly)

Returns The user associated with the project.

Returns:

  • (String)

    The user associated with the project



67
68
69
# File 'lib/daytona/sandbox.rb', line 67

def user
  @user
end

#volumesArray<DaytonaApiClient::SandboxVolume>? (readonly)

Returns Volumes attached to the sandbox. Not returned by list results; call #refresh on each item to populate.

Returns:

  • (Array<DaytonaApiClient::SandboxVolume>, nil)

    Volumes attached to the sandbox. Not returned by list results; call #refresh on each item to populate.



155
156
157
# File 'lib/daytona/sandbox.rb', line 155

def volumes
  @volumes
end

#warm_pool_idString? (readonly)

Returns ID of the warm pool this sandbox waits in; set only while it is an unclaimed member.

Returns:

  • (String, nil)

    ID of the warm pool this sandbox waits in; set only while it is an unclaimed member



178
179
180
# File 'lib/daytona/sandbox.rb', line 178

def warm_pool_id
  @warm_pool_id
end

Class Method Details

.with_events(*method_names) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/daytona/sandbox.rb', line 21

def self.with_events(*method_names)
  method_names.each do |method_name|
    original = instance_method(method_name)

    visibility = if private_method_defined?(method_name, false)
                   :private
                 elsif protected_method_defined?(method_name, false)
                   :protected
                 else
                   :public
                 end

    define_method(method_name) do |*args, **kwargs, &blk|
      ensure_subscribed
      original.bind_call(self, *args, **kwargs, &blk)
    end

    case visibility
    when :private then private method_name
    when :protected then protected method_name
    end
  end
end

Instance Method Details

#archivevoid

This method returns an undefined value.

Archives the sandbox, making it inactive and preserving its state. When sandboxes are archived, the entire filesystem state is moved to cost-effective object storage, making it possible to keep sandboxes available for an extended period. The tradeoff between archived and stopped states is that starting an archived sandbox takes more time, depending on its size. Sandbox must be stopped before archiving.



283
284
285
286
# File 'lib/daytona/sandbox.rb', line 283

def archive
  sandbox_api.archive_sandbox(id)
  refresh
end

#create_lsp_server(language_id:, path_to_project:) ⇒ Daytona::LspServer

Creates a new Language Server Protocol (LSP) server instance. The LSP server provides language-specific features like code completion, diagnostics, and more.

Parameters:

  • language_id (Symbol)

    The language server type (e.g., Daytona::LspServer::Language::PYTHON)

  • path_to_project (String)

    Path to the project root directory. Relative paths are resolved based on the sandbox working directory.

Returns:



810
811
812
# File 'lib/daytona/sandbox.rb', line 810

def create_lsp_server(language_id:, path_to_project:)
  LspServer.new(language_id:, path_to_project:, toolbox_api: @lsp_api, sandbox_id: id, otel_state:)
end

#create_signed_preview_url(port, expires_in_seconds = nil) ⇒ DaytonaApiClient::SignedPortPreviewUrl

Creates a signed preview URL for the sandbox at the specified port.

Examples:

signed_url = sandbox.create_signed_preview_url(3000, 120)
puts "Signed URL: #{signed_url.url}"
puts "Token: #{signed_url.token}"

Parameters:

  • port (Integer)

    The port to open the preview link on

  • expires_in_seconds (Integer, nil) (defaults to: nil)

    The number of seconds the signed preview URL will be valid for. Defaults to 60 seconds.

Returns:

  • (DaytonaApiClient::SignedPortPreviewUrl)

    The signed preview URL response object



635
636
637
# File 'lib/daytona/sandbox.rb', line 635

def create_signed_preview_url(port, expires_in_seconds = nil)
  sandbox_api.get_signed_port_preview_url(id, port, { expires_in_seconds: })
end

#create_snapshot(name:, timeout: DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Creates a snapshot from the current state of the Sandbox. The Sandbox will temporarily enter a 'snapshotting' state and return to its previous state when complete.

Parameters:

  • name (String)

    Name for the new snapshot

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s)



894
895
896
897
898
899
900
901
902
903
# File 'lib/daytona/sandbox.rb', line 894

def create_snapshot(name:, timeout: DEFAULT_TIMEOUT)
  with_timeout(
    timeout:,
    message: "Sandbox #{id} snapshot failed within the #{timeout} seconds timeout period",
    setup: proc {
      response = sandbox_api.create_sandbox_snapshot(id, DaytonaApiClient::CreateSandboxSnapshot.new(name:))
      process_response(response)
    }
  ) { wait_for_snapshot_complete }
end

#create_ssh_access(expires_in_minutes) ⇒ DaytonaApiClient::SshAccessDto

Creates an SSH access token for the sandbox.

Parameters:

  • expires_in_minutes (Integer)

    TThe number of minutes the SSH access token will be valid for

Returns:

  • (DaytonaApiClient::SshAccessDto)


404
405
406
# File 'lib/daytona/sandbox.rb', line 404

def create_ssh_access(expires_in_minutes)
  sandbox_api.create_ssh_access(id, { expires_in_minutes: })
end

#delete(timeout = DEFAULT_TIMEOUT, wait: false) ⇒ void

This method returns an undefined value.

Deletes the Sandbox.

By default returns as soon as the deletion request is accepted (matching origin/main behavior). Pass wait: true to block until the Sandbox reaches the destroyed state.

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s). Only meaningful when wait is true.

  • wait (Boolean) (defaults to: false)

    When true, block until the Sandbox is destroyed.



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/daytona/sandbox.rb', line 418

def delete(timeout = DEFAULT_TIMEOUT, wait: false) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
  begin
    process_response(sandbox_api.delete_sandbox(id))
  rescue DaytonaApiClient::ApiError => e
    raise unless e.code == 404

    apply_state(DaytonaApiClient::SandboxState::DESTROYED)
  end

  if wait
    refresh_data_safe
  else
    begin
      refresh
    rescue DaytonaApiClient::ApiError => e
      raise unless e.code == 404

      apply_state(DaytonaApiClient::SandboxState::DESTROYED)
    end
    return
  end
  return if state.to_s == DaytonaApiClient::SandboxState::DESTROYED.to_s

  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to be destroyed within the #{timeout} seconds timeout period",
    setup: nil
  ) do
    wait_for_state(
      target_states: [DaytonaApiClient::SandboxState::DESTROYED],
      error_states: [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED],
      safe_refresh: true
    )
  end
ensure
  unsubscribe_from_events
end

#download_url(path, ttl_seconds: nil) ⇒ String

Creates a pre-signed URL for downloading a file from the Sandbox.

The URL works with any HTTP client without auth headers and stays valid across sandbox restarts (downloads succeed only while the sandbox is running). The signing key is cached locally for up to 15 seconds; if the key was rotated from another client, URLs may be rejected until the cache refreshes.

Examples:

url = sandbox.download_url('/home/user/report.pdf')
# curl "$url" -o report.pdf

Parameters:

  • path (String)

    Path to the file in the Sandbox.

  • ttl_seconds (Integer, nil) (defaults to: nil)

    How long the URL stays valid, in seconds. Defaults to 3600. Zero or negative means the URL never expires.

Returns:

  • (String)

    Pre-signed download URL.

Raises:



570
571
572
573
574
575
576
577
578
579
580
# File 'lib/daytona/sandbox.rb', line 570

def download_url(path, ttl_seconds: nil)
  Utils::FileUrlSigning.build_signed_file_url(
    @toolbox_proxy_url,
    id,
    '/files/download',
    'GET',
    path,
    ensure_signing_key,
    ttl_seconds
  )
end

#experimental_create_snapshot(name:, timeout: DEFAULT_TIMEOUT) ⇒ Object

Deprecated.

Use #create_snapshot instead. This method will be removed in a future version.

Deprecated: Use create_snapshot instead. This method will be removed in a future version.



916
917
918
919
# File 'lib/daytona/sandbox.rb', line 916

def experimental_create_snapshot(name:, timeout: DEFAULT_TIMEOUT)
  warn('[DEPRECATION] `experimental_create_snapshot` is deprecated. Use `create_snapshot` instead.', uplevel: 1)
  create_snapshot(name:, timeout:)
end

#experimental_fork(name: nil, timeout: DEFAULT_TIMEOUT) ⇒ Object

Deprecated.

Use #fork instead. This method will be removed in a future version.

Deprecated: Use fork instead. This method will be removed in a future version.



908
909
910
911
# File 'lib/daytona/sandbox.rb', line 908

def experimental_fork(name: nil, timeout: DEFAULT_TIMEOUT)
  warn('[DEPRECATION] `experimental_fork` is deprecated. Use `fork` instead.', uplevel: 1)
  fork(name:, timeout:)
end

#expire_signed_preview_url(port, token) ⇒ void

This method returns an undefined value.

Expires a signed preview URL for the sandbox at the specified port.

Examples:

sandbox.expire_signed_preview_url(3000, "token-value")

Parameters:

  • port (Integer)

    The port to expire the signed preview URL on

  • token (String)

    The token to expire



647
648
649
# File 'lib/daytona/sandbox.rb', line 647

def expire_signed_preview_url(port, token)
  sandbox_api.expire_signed_port_preview_url(id, port, token)
end

#fork(name: nil, timeout: DEFAULT_TIMEOUT) ⇒ Daytona::Sandbox

Forks the Sandbox, creating a new Sandbox with an identical filesystem. The forked Sandbox is a copy-on-write clone of the original. It starts with the same disk contents but operates independently from that point on.

Parameters:

  • name (String, nil) (defaults to: nil)

    Optional name for the forked Sandbox

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s)

Returns:



866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
# File 'lib/daytona/sandbox.rb', line 866

def fork(name: nil, timeout: DEFAULT_TIMEOUT) # rubocop:disable Metrics/MethodLength
  forked_dto = nil
  with_timeout(
    timeout:,
    message: "Sandbox #{id} fork failed to become ready within the #{timeout} seconds timeout period",
    setup: proc {
      forked_dto = sandbox_api.fork_sandbox(id, DaytonaApiClient::ForkSandbox.new(name:))
    }
  ) do
    forked = Sandbox.new(
      sandbox_dto: forked_dto,
      config:,
      sandbox_api:,
      subscription_manager: @subscription_manager,
      otel_state:,
      analytics_api_url_provider: @analytics_api_url_provider
    )
    forked.wait_for_sandbox_start(timeout)
    return forked
  end
end

#get_metrics(start_time = nil, end_time = nil) ⇒ Array<Daytona::SandboxMetrics>

Gets historical time-series resource usage metrics for the Sandbox.

Examples:

sandbox.get_metrics.each { |m| puts "#{m.timestamp}: #{m.cpu_used_pct}%" }

Parameters:

  • start_time (Time, nil) (defaults to: nil)

    Start of the range. Defaults to the Sandbox creation time.

  • end_time (Time, nil) (defaults to: nil)

    End of the range. Defaults to the current time.

Returns:



524
525
526
527
528
529
530
531
532
533
534
# File 'lib/daytona/sandbox.rb', line 524

def get_metrics(start_time = nil, end_time = nil)
  from, to = metrics_range(start_time, end_time)
  analytics_api_url = fetch_analytics_api_url
  if analytics_api_url && !analytics_api_url.empty?
    pivot_sandbox_metric_points(fetch_analytics_metrics(analytics_api_url, from, to))
  else
    pivot_sandbox_metrics(fetch_proxy_metrics(from, to))
  end
rescue StandardError => e
  raise Sdk::Error, "Failed to get sandbox metrics: #{e.message}"
end

#get_metrics_latestDaytona::SandboxMetrics

Gets the most recent resource usage sample directly from the Sandbox daemon.

Unlike #get_metrics, which returns aggregated historical samples, this returns the single current reading without going through the telemetry backend.

Examples:

m = sandbox.get_metrics_latest
puts "CPU used: #{m.cpu_used_pct}%"

Returns:



510
511
512
513
514
# File 'lib/daytona/sandbox.rb', line 510

def get_metrics_latest
  sandbox_metrics_from_system_metrics(@system_api.get_system_metrics)
rescue StandardError => e
  raise Sdk::Error, "Failed to get system metrics: #{e.message}"
end

#get_user_home_dirString

Gets the user's home directory path for the logged in user inside the Sandbox.

Examples:

user_home_dir = sandbox.get_user_home_dir
puts "Sandbox user home: #{user_home_dir}"

Returns:

  • (String)

    The absolute path to the Sandbox user's home directory for the logged in user



463
464
465
466
467
# File 'lib/daytona/sandbox.rb', line 463

def get_user_home_dir
  @info_api.get_user_home_dir.dir
rescue *Sdk::API_ERROR_CLASSES => e
  raise Sdk.wrap_error(e, 'Failed to get user home directory')
end

#get_work_dirString

Gets the working directory path inside the Sandbox.

Examples:

work_dir = sandbox.get_work_dir
puts "Sandbox working directory: #{work_dir}"

Returns:

  • (String)

    The absolute path to the Sandbox working directory. Uses the WORKDIR specified in the Dockerfile if present, or falling back to the user's home directory if not.



477
478
479
480
481
# File 'lib/daytona/sandbox.rb', line 477

def get_work_dir
  @info_api.get_work_dir.dir
rescue *Sdk::API_ERROR_CLASSES => e
  raise Sdk.wrap_error(e, 'Failed to get working directory path')
end

#pause(timeout: DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Pauses the Sandbox, freezing all running processes. Completes when the Sandbox has left the pausing state (paused, stopped, archived, etc.); error states still raise.

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s)



927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
# File 'lib/daytona/sandbox.rb', line 927

def pause(timeout: DEFAULT_TIMEOUT)
  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to pause within the #{timeout} seconds timeout period",
    setup: proc {
      sandbox_api.pause_sandbox(id)
      refresh
    }
  ) do
    error_states = [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
    exclude = [DaytonaApiClient::SandboxState::PAUSING] + error_states
    target_states = DaytonaApiClient::SandboxState.all_vars.reject { |s| exclude.include?(s) }

    wait_for_state(target_states:, error_states:)
  end
end

#preview_url(port) ⇒ DaytonaApiClient::PortPreviewUrl

Retrieves the preview link for the sandbox at the specified port. If the port is closed, it will be opened automatically. For private sandboxes, a token is included to grant access to the URL.

Parameters:

  • port (Integer)

Returns:

  • (DaytonaApiClient::PortPreviewUrl)


550
551
552
# File 'lib/daytona/sandbox.rb', line 550

def preview_url(port)
  sandbox_api.get_port_preview_url(id, port)
end

#recover(timeout = DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Recovers the Sandbox from a recoverable error and waits for it to be ready.

Examples:

sandbox = daytona.get('my-sandbox-id')
sandbox.recover(timeout: 40)  # Wait up to 40 seconds
puts 'Sandbox recovered successfully'

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s).



708
709
710
711
712
713
714
715
716
717
718
719
720
721
# File 'lib/daytona/sandbox.rb', line 708

def recover(timeout = DEFAULT_TIMEOUT)
  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to recover within the #{timeout} seconds timeout period",
    setup: proc { process_response(sandbox_api.recover_sandbox(id)) }
  ) do
    wait_for_state(
      target_states: [DaytonaApiClient::SandboxState::STARTED],
      error_states: [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
    )
  end
rescue *Sdk::API_ERROR_CLASSES => e
  raise Sdk.wrap_error(e, 'Failed to recover sandbox')
end

#refreshvoid

This method returns an undefined value.

Refresh the Sandbox data from the API.



654
655
656
# File 'lib/daytona/sandbox.rb', line 654

def refresh
  process_response(sandbox_api.get_sandbox(id))
end

#refresh_activityvoid

This method returns an undefined value.

Refreshes the sandbox activity to reset the timer for automated lifecycle management actions.

This method updates the sandbox's last activity timestamp without changing its state. It is useful for keeping long-running sessions alive while there is still user activity.

Examples:

sandbox.refresh_activity


667
668
669
670
671
672
# File 'lib/daytona/sandbox.rb', line 667

def refresh_activity
  sandbox_api.update_last_activity(id)
  nil
rescue *Sdk::API_ERROR_CLASSES => e
  raise Sdk.wrap_error(e, 'Failed to refresh sandbox activity')
end

#resize(resources, timeout = DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Resizes the Sandbox resources.

Changes the CPU, memory, or disk allocation. Resizing a started sandbox accepts only CPU and memory increases. Disk resize requires a stopped sandbox; disk can only grow. GPU is not resizable — to change GPU, create a new sandbox.

Examples:

Resize a started sandbox (CPU and memory can be increased)

sandbox.resize(Daytona::Resources.new(cpu: 4, memory: 8))

Resize a stopped sandbox (CPU, memory, and disk can be changed)

sandbox.stop
sandbox.resize(Daytona::Resources.new(cpu: 2, memory: 4, disk: 30))

Parameters:

  • resources (Daytona::Resources)

    New resource configuration (cpu, memory, disk only)

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s)

Raises:

  • (Sdk::Error)

    If resources.gpu or resources.gpu_type is set



761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
# File 'lib/daytona/sandbox.rb', line 761

def resize(resources, timeout = DEFAULT_TIMEOUT)
  raise Sdk::Error, 'Resources must not be nil' if resources.nil?

  if resources.gpu || resources.gpu_type
    raise Sdk::Error,
          'Resize does not support changes to gpu or gpu_type — to change GPU, create a new sandbox'
  end

  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to resize within the #{timeout} seconds timeout period",
    setup: proc {
      resize_attrs = {}
      resize_attrs[:cpu] = resources.cpu if resources.cpu
      resize_attrs[:memory] = resources.memory if resources.memory
      resize_attrs[:disk] = resources.disk if resources.disk
      resize_request = DaytonaApiClient::ResizeSandbox.new(resize_attrs)
      process_response(sandbox_api.resize_sandbox(id, resize_request))
    }
  ) { wait_for_resize_complete(timeout) }
end

#revoke_ssh_access(token) ⇒ void

This method returns an undefined value.

Revokes an SSH access token for the sandbox.

Parameters:

  • token (String)


678
679
680
# File 'lib/daytona/sandbox.rb', line 678

def revoke_ssh_access(token)
  sandbox_api.revoke_ssh_access(id, token:)
end

#rotate_signing_keyvoid

This method returns an undefined value.

Rotates the sandbox signing key, invalidating all previously signed URLs.

Examples:

sandbox.rotate_signing_key
puts 'All previously signed URLs are now invalid.'

Raises:

  • (DaytonaApiClient::ApiError)

    if the signing key rotation request fails.



618
619
620
621
622
# File 'lib/daytona/sandbox.rb', line 618

def rotate_signing_key
  @signing_key = @sandbox_api.rotate_signing_key(id)
  @signing_key_fetched_at = Time.now.to_f
  nil
end

#start(timeout = DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Starts the Sandbox and waits for it to be ready.

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s).



686
687
688
689
690
691
692
693
694
695
696
697
# File 'lib/daytona/sandbox.rb', line 686

def start(timeout = DEFAULT_TIMEOUT)
  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to become ready within the #{timeout} seconds timeout period",
    setup: proc { process_response(sandbox_api.start_sandbox(id)) }
  ) do
    wait_for_state(
      target_states: [DaytonaApiClient::SandboxState::STARTED],
      error_states: [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
    )
  end
end

#stop(timeout = DEFAULT_TIMEOUT, force: false) ⇒ void

This method returns an undefined value.

Stops the Sandbox and waits for it to be stopped.

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s).

  • force (Boolean) (defaults to: false)

    If true, uses SIGKILL instead of SIGTERM (defaults to false).



728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
# File 'lib/daytona/sandbox.rb', line 728

def stop(timeout = DEFAULT_TIMEOUT, force: false) # rubocop:disable Metrics/MethodLength
  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to become stopped within the #{timeout} seconds timeout period",
    setup: proc {
      sandbox_api.stop_sandbox(id, { force: force })
      refresh
    }
  ) do
    wait_for_state(
      target_states: [DaytonaApiClient::SandboxState::STOPPED, DaytonaApiClient::SandboxState::DESTROYED],
      error_states: [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
    )
  end
end

#ttl_minutes=(minutes) ⇒ void

This method returns an undefined value.

Sets the TTL (time to live) for the Sandbox. The TTL is re-anchored from the current time. When it elapses the Sandbox is destroyed, regardless of its current state. Use 0 to disable.

Parameters:

  • minutes (Integer)

Raises:



394
395
396
397
398
# File 'lib/daytona/sandbox.rb', line 394

def ttl_minutes=(minutes)
  raise Sdk::Error, 'TTL must be a non-negative integer' if minutes.negative?

  sandbox_api.set_ttl(id, minutes)
end

#update_env(env: nil, unset: nil) ⇒ void

This method returns an undefined value.

Updates the Sandbox daemon's process environment. Newly spawned processes, sessions and PTYs inherit the change; already-running processes keep their existing environment.

Examples:

sandbox.update_env(env: { 'FOO' => 'bar' }, unset: ['OLD_VAR'])

Parameters:

  • env (Hash<String, String>, nil) (defaults to: nil)

    Env vars to set in the daemon's process environment

  • unset (Array<String>, nil) (defaults to: nil)

    Environment variable names to remove

Raises:



493
494
495
496
497
498
# File 'lib/daytona/sandbox.rb', line 493

def update_env(env: nil, unset: nil)
  @server_api.update_env(DaytonaToolboxApiClient::UpdateEnvRequest.new(set: env, unset:))
  nil
rescue StandardError => e
  raise Sdk::Error, "Failed to update environment: #{e.message}"
end

#update_network_settings(network_block_all: nil, network_allow_list: nil, domain_allow_list: nil) ⇒ void

This method returns an undefined value.

Updates outbound network policy on the runner (block all, restore access, or CIDR allow list).

Parameters:

  • network_block_all (Boolean, nil) (defaults to: nil)
  • network_allow_list (String, nil) (defaults to: nil)
  • domain_allow_list (String, nil) (defaults to: nil)

Raises:



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/daytona/sandbox.rb', line 319

def update_network_settings(network_block_all: nil, network_allow_list: nil, domain_allow_list: nil)
  if network_block_all.nil? && network_allow_list.nil? && domain_allow_list.nil?
    raise Sdk::Error,
          'At least one of network_block_all, network_allow_list or domain_allow_list must be provided'
  end

  body = DaytonaApiClient::UpdateSandboxNetworkSettings.new(
    network_block_all:,
    network_allow_list:,
    domain_allow_list:
  )
  data = sandbox_api.update_network_settings(id, body)
  @network_block_all = data.network_block_all
  @network_allow_list = data.network_allow_list
  @domain_allow_list = data.domain_allow_list
end

#update_secrets(secrets) ⇒ void

This method returns an undefined value.

Replaces the set of organization vault secrets mounted in the Sandbox. Pass an empty hash to detach all secrets. Rotated, attached or detached secrets take effect for outbound requests within seconds. New environment variables are only visible to processes spawned after the update; a Sandbox created without any secrets must be restarted for newly attached secrets to work.

Examples:

sandbox.update_secrets({ 'API_KEY' => 'my-vault-secret' })

Parameters:

  • secrets (Hash<String, String>)

    Mapping of environment variable name to organization vault secret name

Raises:



349
350
351
352
353
354
355
# File 'lib/daytona/sandbox.rb', line 349

def update_secrets(secrets)
  body = DaytonaApiClient::UpdateSandboxSecrets.new(
    secrets: secrets.map { |env_var, secret_name| { env_var.to_s => secret_name.to_s } }
  )
  data = sandbox_api.update_sandbox_secrets(id, body)
  process_response(data)
end

#upload_url(path, ttl_seconds: nil) ⇒ String

Creates a pre-signed URL for uploading a file to the Sandbox.

Send a POST request with the file as multipart/form-data. The URL works with any HTTP client without auth headers. The signing key is cached locally for up to 15 seconds; if the key was rotated from another client, URLs may be rejected until the cache refreshes.

Examples:

url = sandbox.upload_url('/home/user/data.bin')
# curl -X POST -F "file=@local.bin" "$url"

Parameters:

  • path (String)

    Destination path for the uploaded file in the Sandbox.

  • ttl_seconds (Integer, nil) (defaults to: nil)

    How long the URL stays valid, in seconds. Defaults to 3600. Zero or negative means the URL never expires.

Returns:

  • (String)

    Pre-signed upload URL.

Raises:



598
599
600
601
602
603
604
605
606
607
608
# File 'lib/daytona/sandbox.rb', line 598

def upload_url(path, ttl_seconds: nil)
  Utils::FileUrlSigning.build_signed_file_url(
    @toolbox_proxy_url,
    id,
    '/files/upload-v2',
    'POST',
    path,
    ensure_signing_key,
    ttl_seconds
  )
end

#validate_ssh_access(token) ⇒ DaytonaApiClient::SshAccessValidationDto

Validates an SSH access token for the sandbox.

Parameters:

  • token (String)

Returns:

  • (DaytonaApiClient::SshAccessValidationDto)


818
819
820
# File 'lib/daytona/sandbox.rb', line 818

def validate_ssh_access(token)
  sandbox_api.validate_ssh_access(token)
end

#wait_for_resize_complete(timeout = DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Waits for the Sandbox resize operation to complete. Polls the Sandbox status until the state is no longer 'resizing'.

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s)



788
789
790
791
792
793
794
795
796
797
798
799
800
# File 'lib/daytona/sandbox.rb', line 788

def wait_for_resize_complete(timeout = DEFAULT_TIMEOUT)
  with_timeout(
    timeout:,
    message: "Sandbox #{id} resize did not complete within the #{timeout} seconds timeout period",
    setup: nil
  ) do
    error_states = [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
    exclude = [DaytonaApiClient::SandboxState::RESIZING] + error_states
    target_states = DaytonaApiClient::SandboxState.all_vars.reject { |s| exclude.include?(s) }

    wait_for_state(target_states:, error_states:)
  end
end

#wait_for_sandbox_start(timeout = DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Waits for the Sandbox to reach the 'started' state. Polls the Sandbox status until it reaches the 'started' state or encounters an error.

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s).



827
828
829
830
831
832
833
834
835
836
837
838
# File 'lib/daytona/sandbox.rb', line 827

def wait_for_sandbox_start(timeout = DEFAULT_TIMEOUT)
  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to start within the #{timeout} seconds timeout period",
    setup: nil
  ) do
    wait_for_state(
      target_states: [DaytonaApiClient::SandboxState::STARTED],
      error_states: [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
    )
  end
end

#wait_for_sandbox_stop(timeout = DEFAULT_TIMEOUT) ⇒ void

This method returns an undefined value.

Waits for the Sandbox to reach the 'stopped' state. Polls the Sandbox status until it reaches the 'stopped' state or encounters an error. Treats destroyed as stopped to cover ephemeral sandboxes that are automatically deleted after stopping.

Parameters:

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    Maximum wait time in seconds (defaults to 60 s).



846
847
848
849
850
851
852
853
854
855
856
857
# File 'lib/daytona/sandbox.rb', line 846

def wait_for_sandbox_stop(timeout = DEFAULT_TIMEOUT)
  with_timeout(
    timeout:,
    message: "Sandbox #{id} failed to stop within the #{timeout} seconds timeout period",
    setup: nil
  ) do
    wait_for_state(
      target_states: [DaytonaApiClient::SandboxState::STOPPED, DaytonaApiClient::SandboxState::DESTROYED],
      error_states: [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
    )
  end
end