ExDaytona.Quota (ex_daytona v0.4.0)

Copy Markdown View Source

Normalized read access to organization quotas, limits, and current usage — the primitives an application needs to build its own admission control or per-tenant limit logic on top of Daytona's org-level enforcement.

{:ok, overview} = ExDaytona.Quota.overview(client, org_id)
# => %{snapshots: %{used: 2, total: 10},
#      volumes: %{used: 0.0, total: 100.0},
#      regions: [%{region_id: "us", sandbox_class: "small",
#                  cpu: %{used: 4, total: 64}, memory: %{...}, ...}]}

{:ok, headroom} = ExDaytona.Quota.headroom(client, org_id, region: "us", class: "small")
# => %{cpu: 60, memory: 120, disk: 800, gpu: 0}

{:ok, limits} = ExDaytona.Quota.limits(client, org_id)
# => per-sandbox maxima, secret quota, create/lifecycle rate limits

The SDK deliberately ships no per-user limit enforcement — these are read primitives. An application imposing finer-grained limits than the organization's (e.g. per end-user of a SaaS product) can combine them with label-scoped sandbox listing (ExDaytona.Sandbox.list(client, labels: %{"my-app/tenant" => id})) and the metering rollups in ExDaytona.Platform. Note that any such application-side check is advisory (check-then-act) — Daytona enforces only the organization-level quota.

Summary

Types

A used-vs-total pair for one resource dimension.

Quota and usage for one region × sandbox-class combination.

Functions

Remaining capacity for one region × sandbox-class: {:ok, %{cpu: n, memory: n, disk: n, gpu: n}} (each total - used, nil when the provider reports no quota for the dimension). Returns a NOT_FOUND-coded error when the organization has no quota row for the combination.

The organization's limits: per-sandbox resource maxima, secret quota, and the create/lifecycle/API rate limits, normalized to snake_case.

The organization's quota/usage overview, normalized: snapshot and volume gauges plus one region_class/0 entry per region × sandbox-class.

Types

gauge()

@type gauge() :: %{used: number() | nil, total: number() | nil}

A used-vs-total pair for one resource dimension.

region_class()

@type region_class() :: %{
  region_id: String.t() | nil,
  sandbox_class: String.t() | nil,
  cpu: gauge(),
  memory: gauge(),
  disk: gauge(),
  gpu: gauge(),
  max_per_sandbox: %{
    cpu: number() | nil,
    memory: number() | nil,
    disk: number() | nil
  },
  allowed_gpu_types: [String.t()]
}

Quota and usage for one region × sandbox-class combination.

Functions

headroom(client, organization_id, opts)

@spec headroom(ExDaytona.Client.t(), String.t(), keyword()) ::
  {:ok,
   %{
     cpu: number() | nil,
     memory: number() | nil,
     disk: number() | nil,
     gpu: number() | nil
   }}
  | {:error, ExDaytona.Error.t()}

Remaining capacity for one region × sandbox-class: {:ok, %{cpu: n, memory: n, disk: n, gpu: n}} (each total - used, nil when the provider reports no quota for the dimension). Returns a NOT_FOUND-coded error when the organization has no quota row for the combination.

limits(client, organization_id)

@spec limits(ExDaytona.Client.t(), String.t()) ::
  {:ok,
   %{
     max_cpu_per_sandbox: number() | nil,
     max_memory_per_sandbox: number() | nil,
     max_disk_per_sandbox: number() | nil,
     max_secrets_per_sandbox: number() | nil,
     secret_quota: number() | nil,
     sandbox_create_rate_limit: %{
       limit: number() | nil,
       ttl_seconds: number() | nil
     },
     sandbox_lifecycle_rate_limit: %{
       limit: number() | nil,
       ttl_seconds: number() | nil
     },
     authenticated_rate_limit: %{
       limit: number() | nil,
       ttl_seconds: number() | nil
     }
   }}
  | {:error, ExDaytona.Error.t()}

The organization's limits: per-sandbox resource maxima, secret quota, and the create/lifecycle/API rate limits, normalized to snake_case.

JWT only

The provider rejects API-key authentication on the underlying organization endpoint (401 "Invalid credentials", verified live) — this works with JWT sessions. With an API key, the per-sandbox maxima are still available per region/class from overview/2 (max_per_sandbox).

overview(client, organization_id)

@spec overview(ExDaytona.Client.t(), String.t()) ::
  {:ok, %{snapshots: gauge(), volumes: gauge(), regions: [region_class()]}}
  | {:error, ExDaytona.Error.t()}

The organization's quota/usage overview, normalized: snapshot and volume gauges plus one region_class/0 entry per region × sandbox-class.