From 2e36d7f91c2776d781a3cc34a1126aa60dfa1a81 Mon Sep 17 00:00:00 2001 From: ben-fornefeld <50748440+ben-fornefeld@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:00:41 +0000 Subject: [PATCH] chore: sync infra OpenAPI specs --- spec/openapi.dashboard-api.yaml | 277 ++++++++++++++- spec/openapi.infra.yaml | 138 ++++++++ .../shared/contracts/dashboard-api.types.ts | 314 +++++++++++++++++- src/core/shared/contracts/infra-api.types.ts | 106 ++++++ 4 files changed, 824 insertions(+), 11 deletions(-) diff --git a/spec/openapi.dashboard-api.yaml b/spec/openapi.dashboard-api.yaml index 6e15f856b..09bbedd64 100644 --- a/spec/openapi.dashboard-api.yaml +++ b/spec/openapi.dashboard-api.yaml @@ -6,10 +6,18 @@ info: components: securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key AdminApiKeyAuth: type: apiKey in: header name: X-Admin-Token + AdminJWTAuth: + type: http + scheme: bearer + bearerFormat: JWT # AuthProviderBearerAuth / AuthProviderTeamAuth: B before T in the name # so Bearer is validated before Team. AuthProviderBearerAuth: @@ -194,13 +202,7 @@ components: description: Sort column and direction. schema: type: string - enum: - [ - created_at_asc, - created_at_desc, - updated_at_asc, - updated_at_desc, - ] + enum: [created_at_asc, created_at_desc, updated_at_asc, updated_at_desc] default: created_at_desc tag_assignments_cursor: name: cursor @@ -283,6 +285,12 @@ components: application/json: schema: $ref: "#/components/schemas/Error" + "501": + description: Operation is not implemented + content: + application/json: + schema: + $ref: "#/components/schemas/Error" "502": description: Upstream error content: @@ -471,7 +479,7 @@ components: nextCursor: type: string nullable: true - description: Cursor to pass to the next list request, or `null` if there is no next page. + description: Cursor to pass to the next list request, or `null` if there is no next page. BuildStatusItem: type: object @@ -566,6 +574,7 @@ components: - memoryMB - diskSizeMB - retentionExpired + - eventsRetentionExpired properties: templateID: type: string @@ -597,7 +606,10 @@ components: $ref: "#/components/schemas/DiskSizeMB" retentionExpired: type: boolean - description: Whether the sandbox ended more than the retention window ago, so its monitoring, events, and logs data is no longer available + description: Whether the sandbox ended more than the fixed retention window ago, so its monitoring and logs data is no longer available + eventsRetentionExpired: + type: boolean + description: Whether the sandbox ended more than the team's events retention window ago, so its events data is no longer available HealthResponse: type: object @@ -617,6 +629,7 @@ components: - maxVcpu - maxRamMb - diskMb + - eventsTtlDays properties: maxLengthHours: type: integer @@ -636,6 +649,9 @@ components: diskMb: type: integer format: int32 + eventsTtlDays: + type: integer + format: int32 UserTeam: type: object @@ -1146,11 +1162,89 @@ components: slug: type: string + AdminControlPlaneProjectType: + type: string + enum: [development, staging, production] + + AdminControlPlaneProjectUpsertRequest: + type: object + required: [name, slug, project_type] + properties: + name: + type: string + minLength: 1 + maxLength: 255 + slug: + type: string + minLength: 1 + maxLength: 63 + project_type: + $ref: "#/components/schemas/AdminControlPlaneProjectType" + + AdminControlPlaneProject: + allOf: + - $ref: "#/components/schemas/AdminControlPlaneProjectUpsertRequest" + - type: object + required: [id] + properties: + id: + type: string + format: uuid + + AdminControlPlaneMemberUpsertRequest: + type: object + properties: + added_by: + type: string + format: uuid + + AdminControlPlaneProjectLimits: + type: object + required: + - concurrent_sandboxes + - max_sandbox_length_hours + - max_vcpu + - max_ram_mb + - disk_mb + - concurrent_template_builds + - events_ttl_days + properties: + concurrent_sandboxes: + type: integer + format: int32 + minimum: 1 + max_sandbox_length_hours: + type: integer + format: int32 + minimum: 1 + max_vcpu: + type: integer + format: int32 + minimum: 1 + max_ram_mb: + type: integer + format: int64 + minimum: 1 + disk_mb: + type: integer + format: int64 + minimum: 1 + concurrent_template_builds: + type: integer + format: int32 + minimum: 1 + events_ttl_days: + type: integer + format: int32 + minimum: 1 + tags: - name: builds - name: sandboxes - name: teams - name: templates + - name: workspace-admin + description: Workspace control-plane admin operations authenticated with service JWTs. paths: /health: @@ -1169,6 +1263,7 @@ paths: summary: List team builds tags: [builds] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1197,6 +1292,7 @@ paths: summary: Build statuses tags: [builds] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1223,6 +1319,7 @@ paths: summary: Build details tags: [builds] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1597,6 +1694,7 @@ paths: description: Returns a paginated list of the team's templates (and default templates inline, unless the team is on a dedicated cluster), with filtering, search, and column sorting via keyset cursor pagination. tags: [templates] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1627,6 +1725,7 @@ paths: description: Returns the list of default templates with their latest build info and aliases. tags: [templates] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] responses: "200": @@ -1646,6 +1745,7 @@ paths: description: Returns a single template owned by the current team. Dashboard-shaped read, indexed by template ID. tags: [templates] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1672,6 +1772,7 @@ paths: description: Returns ready template tag assignment groups with bounded per-tag history, paginated over tags with keyset cursor. tags: [templates] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1705,6 +1806,7 @@ paths: description: Returns the total number of distinct ready tags for the template. tags: [templates] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1731,6 +1833,7 @@ paths: description: Checks whether a template tag has at least one ready assignment. tags: [templates] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1760,6 +1863,7 @@ paths: description: Returns ready tag assignment events for a single tag, ordered newest first, with keyset cursor pagination. tags: [templates] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: @@ -1784,3 +1888,158 @@ paths: $ref: "#/components/responses/404" "500": $ref: "#/components/responses/500" + + /admin/v1/projects/{teamID}: + parameters: + - $ref: "#/components/parameters/teamID" + put: + operationId: upsertProject + summary: Create or reconcile a project. + tags: [workspace-admin] + security: + - AdminJWTAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminControlPlaneProjectUpsertRequest" + responses: + "200": + description: Existing project reconciled. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminControlPlaneProject" + "201": + description: Project created. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminControlPlaneProject" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + delete: + operationId: deleteProject + summary: Delete a project and its control-plane state. + tags: [workspace-admin] + security: + - AdminJWTAuth: [] + responses: + "204": + description: Project state is absent. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /admin/v1/projects/{teamID}/members/{userId}: + parameters: + - $ref: "#/components/parameters/teamID" + - $ref: "#/components/parameters/userId" + put: + operationId: upsertProjectMember + summary: Reconcile an opaque user UUID as a project member. + tags: [workspace-admin] + security: + - AdminJWTAuth: [] + requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/AdminControlPlaneMemberUpsertRequest" + responses: + "204": + description: Membership is present. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + delete: + operationId: deleteProjectMember + summary: Remove a project member. + tags: [workspace-admin] + security: + - AdminJWTAuth: [] + responses: + "204": + description: Membership is absent. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /admin/v1/projects/{teamID}/limits: + parameters: + - $ref: "#/components/parameters/teamID" + put: + operationId: upsertProjectLimits + summary: Reconcile effective limits for a project. + tags: [workspace-admin] + security: + - AdminJWTAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminControlPlaneProjectLimits" + responses: + "204": + description: Effective limits are synchronized. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /admin/v1/users/{userId}: + parameters: + - $ref: "#/components/parameters/userId" + delete: + operationId: purgeUser + summary: Purge shard-local membership and access-token state for an opaque user UUID. + tags: [workspace-admin] + security: + - AdminJWTAuth: [] + responses: + "204": + description: User-owned shard state is absent. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" diff --git a/spec/openapi.infra.yaml b/spec/openapi.infra.yaml index ee4fcb539..4a26b3a42 100644 --- a/spec/openapi.infra.yaml +++ b/spec/openapi.infra.yaml @@ -752,11 +752,42 @@ components: $ref: "#/components/schemas/EnvVars" mcp: $ref: "#/components/schemas/Mcp" + iam: + $ref: "#/components/schemas/SandboxIam" volumeMounts: type: array items: $ref: "#/components/schemas/SandboxVolumeMount" + SandboxIam: + type: object + description: >- + Sandbox workload identity configuration. A non-empty, valid tokens map + enables workload identity for the sandbox. + properties: + tokens: + $ref: "#/components/schemas/SandboxIamTokens" + + SandboxIamTokens: + type: object + description: >- + Named workload-token definitions, keyed by a caller-chosen token name. + additionalProperties: + $ref: "#/components/schemas/SandboxIamToken" + + SandboxIamToken: + type: object + required: + - audience + - tokenType + properties: + audience: + type: string + description: Audience of the workload token, stored exactly as provided. + tokenType: + type: string + description: Workload token type. + ResumedSandbox: properties: timeout: @@ -822,6 +853,39 @@ components: resume); auto-resume, which can be triggered by arbitrary traffic, refuses such a sandbox. Defaults to true. + SandboxForkRequest: + type: object + properties: + timeout: + type: integer + format: int32 + minimum: 0 + default: 15 + description: Time to live for the new forked sandboxes in seconds. + count: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 1 + description: >- + Number of forked sandboxes to create. All forks boot from the same + snapshot, so the snapshot is captured once regardless of count. + Each fork succeeds or fails independently; the outcome of each is + reported in its entry of the response list. + + SandboxForkResult: + type: object + description: >- + Result of one requested fork. Exactly one of sandbox or error is set: + sandbox when the fork started successfully, error when it failed to + start. + properties: + sandbox: + $ref: "#/components/schemas/Sandbox" + error: + $ref: "#/components/schemas/Error" + TeamMetric: description: Team metric with timestamp required: @@ -1549,6 +1613,10 @@ components: - cpuCount - memoryTotalBytes - disks + - hugePagesTotal + - hugePagesUsed + - hugePagesReserved + - hugePageSizeBytes properties: allocatedCPU: type: integer @@ -1574,6 +1642,22 @@ components: type: integer format: uint64 description: Total node memory in bytes + hugePagesTotal: + type: integer + format: uint64 + description: Total number of preallocated hugepages on the node + hugePagesUsed: + type: integer + format: uint64 + description: Number of hugepages in use (total - free) + hugePagesReserved: + type: integer + format: uint64 + description: Number of reserved hugepages (committed but not yet faulted) + hugePageSizeBytes: + type: integer + format: uint64 + description: Size of a single hugepage in bytes disks: type: array description: Detailed metrics for each disk/mount point @@ -2512,6 +2596,52 @@ paths: "500": $ref: "#/components/responses/500" + /sandboxes/{sandboxID}/fork: + post: + summary: Fork sandbox + description: >- + Fork the sandbox: checkpoint the running sandbox in place (it is + briefly paused, snapshotted with its full memory state, and resumed on + its node, keeping its ID and expiration untouched) and create count + new sandboxes from that snapshot. Returns one result per requested + fork, each carrying either the created sandbox or the error that + prevented it from starting. A non-201 status means the request failed + before any fork was attempted. + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxForkRequest" + responses: + "201": + description: >- + The sandbox was snapshotted and the forks were attempted; each + entry reports one fork's outcome + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SandboxForkResult" + "409": + $ref: "#/components/responses/409" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + /sandboxes/{sandboxID}/connect: post: summary: Connect sandbox @@ -2691,6 +2821,12 @@ paths: schema: type: string description: Filter snapshots by source sandbox ID + - name: name + in: query + description: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-team/my-snapshot" or "my-snapshot:v1"). + required: false + schema: + type: string - $ref: "#/components/parameters/paginationLimit" - $ref: "#/components/parameters/paginationNextToken" responses: @@ -3411,6 +3547,8 @@ paths: responses: "204": description: The node status was changed successfully + "409": + $ref: "#/components/responses/409" "401": $ref: "#/components/responses/401" "404": diff --git a/src/core/shared/contracts/dashboard-api.types.ts b/src/core/shared/contracts/dashboard-api.types.ts index 9d581f35f..abe271801 100644 --- a/src/core/shared/contracts/dashboard-api.types.ts +++ b/src/core/shared/contracts/dashboard-api.types.ts @@ -1106,6 +1106,90 @@ export interface paths { patch?: never trace?: never } + '/admin/v1/projects/{teamID}': { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + get?: never + /** Create or reconcile a project. */ + put: operations['upsertProject'] + post?: never + /** Delete a project and its control-plane state. */ + delete: operations['deleteProject'] + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/v1/projects/{teamID}/members/{userId}': { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + /** @description Identifier of the user. */ + userId: components['parameters']['userId'] + } + cookie?: never + } + get?: never + /** Reconcile an opaque user UUID as a project member. */ + put: operations['upsertProjectMember'] + post?: never + /** Remove a project member. */ + delete: operations['deleteProjectMember'] + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/v1/projects/{teamID}/limits': { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + get?: never + /** Reconcile effective limits for a project. */ + put: operations['upsertProjectLimits'] + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/v1/users/{userId}': { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the user. */ + userId: components['parameters']['userId'] + } + cookie?: never + } + get?: never + put?: never + post?: never + /** Purge shard-local membership and access-token state for an opaque user UUID. */ + delete: operations['purgeUser'] + options?: never + head?: never + patch?: never + trace?: never + } } export type webhooks = Record export interface components { @@ -1280,8 +1364,10 @@ export interface components { cpuCount: components['schemas']['CPUCount'] memoryMB: components['schemas']['MemoryMB'] diskSizeMB: components['schemas']['DiskSizeMB'] - /** @description Whether the sandbox ended more than the retention window ago, so its monitoring, events, and logs data is no longer available */ + /** @description Whether the sandbox ended more than the fixed retention window ago, so its monitoring and logs data is no longer available */ retentionExpired: boolean + /** @description Whether the sandbox ended more than the team's events retention window ago, so its events data is no longer available */ + eventsRetentionExpired: boolean } HealthResponse: { /** @description Human-readable health check result. */ @@ -1300,6 +1386,8 @@ export interface components { maxRamMb: number /** Format: int32 */ diskMb: number + /** Format: int32 */ + eventsTtlDays: number } UserTeam: { /** Format: uuid */ @@ -1531,6 +1619,37 @@ export interface components { id: string slug: string } + /** @enum {string} */ + AdminControlPlaneProjectType: 'development' | 'staging' | 'production' + AdminControlPlaneProjectUpsertRequest: { + name: string + slug: string + project_type: components['schemas']['AdminControlPlaneProjectType'] + } + AdminControlPlaneProject: components['schemas']['AdminControlPlaneProjectUpsertRequest'] & { + /** Format: uuid */ + id: string + } + AdminControlPlaneMemberUpsertRequest: { + /** Format: uuid */ + added_by?: string + } + AdminControlPlaneProjectLimits: { + /** Format: int32 */ + concurrent_sandboxes: number + /** Format: int32 */ + max_sandbox_length_hours: number + /** Format: int32 */ + max_vcpu: number + /** Format: int64 */ + max_ram_mb: number + /** Format: int64 */ + disk_mb: number + /** Format: int32 */ + concurrent_template_builds: number + /** Format: int32 */ + events_ttl_days: number + } } responses: { /** @description Bad request */ @@ -1587,6 +1706,15 @@ export interface components { 'application/json': components['schemas']['Error'] } } + /** @description Operation is not implemented */ + 501: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } /** @description Upstream error */ 502: { headers: { @@ -1658,4 +1786,186 @@ export interface components { pathItems: never } export type $defs = Record -export type operations = Record +export interface operations { + upsertProject: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminControlPlaneProjectUpsertRequest'] + } + } + responses: { + /** @description Existing project reconciled. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['AdminControlPlaneProject'] + } + } + /** @description Project created. */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['AdminControlPlaneProject'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 409: components['responses']['409'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + deleteProject: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Project state is absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + upsertProjectMember: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + /** @description Identifier of the user. */ + userId: components['parameters']['userId'] + } + cookie?: never + } + requestBody?: { + content: { + 'application/json': components['schemas']['AdminControlPlaneMemberUpsertRequest'] + } + } + responses: { + /** @description Membership is present. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + deleteProjectMember: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + /** @description Identifier of the user. */ + userId: components['parameters']['userId'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Membership is absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + upsertProjectLimits: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminControlPlaneProjectLimits'] + } + } + responses: { + /** @description Effective limits are synchronized. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + purgeUser: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the user. */ + userId: components['parameters']['userId'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description User-owned shard state is absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } +} diff --git a/src/core/shared/contracts/infra-api.types.ts b/src/core/shared/contracts/infra-api.types.ts index f3f508a50..4e306d805 100644 --- a/src/core/shared/contracts/infra-api.types.ts +++ b/src/core/shared/contracts/infra-api.types.ts @@ -676,6 +676,55 @@ export interface paths { patch?: never trace?: never } + '/sandboxes/{sandboxID}/fork': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** + * Fork sandbox + * @description Fork the sandbox: checkpoint the running sandbox in place (it is briefly paused, snapshotted with its full memory state, and resumed on its node, keeping its ID and expiration untouched) and create count new sandboxes from that snapshot. Returns one result per requested fork, each carrying either the created sandbox or the error that prevented it from starting. A non-201 status means the request failed before any fork was attempted. + */ + post: { + parameters: { + query?: never + header?: never + path: { + sandboxID: components['parameters']['sandboxID'] + } + cookie?: never + } + requestBody?: { + content: { + 'application/json': components['schemas']['SandboxForkRequest'] + } + } + responses: { + /** @description The sandbox was snapshotted and the forks were attempted; each entry reports one fork's outcome */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['SandboxForkResult'][] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/sandboxes/{sandboxID}/connect': { parameters: { query?: never @@ -936,6 +985,8 @@ export interface paths { parameters: { query?: { sandboxID?: string + /** @description Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-team/my-snapshot" or "my-snapshot:v1"). */ + name?: string /** @description Maximum number of items to return per page */ limit?: components['parameters']['paginationLimit'] /** @description Cursor to start the list from */ @@ -1874,6 +1925,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 409: components['responses']['409'] 500: components['responses']['500'] } } @@ -2798,8 +2850,23 @@ export interface components { metadata?: components['schemas']['SandboxMetadata'] envVars?: components['schemas']['EnvVars'] mcp?: components['schemas']['Mcp'] + iam?: components['schemas']['SandboxIam'] volumeMounts?: components['schemas']['SandboxVolumeMount'][] } + /** @description Sandbox workload identity configuration. A non-empty, valid tokens map enables workload identity for the sandbox. */ + SandboxIam: { + tokens?: components['schemas']['SandboxIamTokens'] + } + /** @description Named workload-token definitions, keyed by a caller-chosen token name. */ + SandboxIamTokens: { + [key: string]: components['schemas']['SandboxIamToken'] + } + SandboxIamToken: { + /** @description Audience of the workload token, stored exactly as provided. */ + audience: string + /** @description Workload token type. */ + tokenType: string + } ResumedSandbox: { /** * Format: int32 @@ -2842,6 +2909,25 @@ export interface components { */ memory: boolean } + SandboxForkRequest: { + /** + * Format: int32 + * @description Time to live for the new forked sandboxes in seconds. + * @default 15 + */ + timeout: number + /** + * Format: int32 + * @description Number of forked sandboxes to create. All forks boot from the same snapshot, so the snapshot is captured once regardless of count. Each fork succeeds or fails independently; the outcome of each is reported in its entry of the response list. + * @default 1 + */ + count: number + } + /** @description Result of one requested fork. Exactly one of sandbox or error is set: sandbox when the fork started successfully, error when it failed to start. */ + SandboxForkResult: { + sandbox?: components['schemas']['Sandbox'] + error?: components['schemas']['Error'] + } /** @description Team metric with timestamp */ TeamMetric: { /** @@ -3325,6 +3411,26 @@ export interface components { * @description Total node memory in bytes */ memoryTotalBytes: number + /** + * Format: uint64 + * @description Total number of preallocated hugepages on the node + */ + hugePagesTotal: number + /** + * Format: uint64 + * @description Number of hugepages in use (total - free) + */ + hugePagesUsed: number + /** + * Format: uint64 + * @description Number of reserved hugepages (committed but not yet faulted) + */ + hugePagesReserved: number + /** + * Format: uint64 + * @description Size of a single hugepage in bytes + */ + hugePageSizeBytes: number /** @description Detailed metrics for each disk/mount point */ disks: components['schemas']['DiskMetrics'][] }