From f9daf7b501509f2b150934170ca4e855ba5bb74d Mon Sep 17 00:00:00 2001 From: dobrac <4323173+dobrac@users.noreply.github.com> Date: Sat, 3 Oct 2026 16:40:55 +0000 Subject: [PATCH] chore: sync infra OpenAPI specs --- spec/openapi.dashboard-api.yaml | 926 ++++-- spec/openapi.infra.yaml | 2622 ++++++++++++++--- .../shared/contracts/dashboard-api.types.ts | 943 ++++-- src/core/shared/contracts/infra-api.types.ts | 1969 ++++++++++--- 4 files changed, 5048 insertions(+), 1412 deletions(-) diff --git a/spec/openapi.dashboard-api.yaml b/spec/openapi.dashboard-api.yaml index 09bbedd64..9dcb814c7 100644 --- a/spec/openapi.dashboard-api.yaml +++ b/spec/openapi.dashboard-api.yaml @@ -103,6 +103,30 @@ components: schema: type: string format: uuid + clusterID: + name: clusterID + in: path + required: true + description: Identifier of the cluster. + schema: + type: string + format: uuid + projectID: + name: projectID + in: path + required: true + description: Identifier of the project. + schema: + type: string + format: uuid + userID: + name: userID + in: path + required: true + description: Identifier of the user. + schema: + type: string + format: uuid userId: name: userId in: path @@ -291,12 +315,6 @@ components: application/json: schema: $ref: "#/components/schemas/Error" - "502": - description: Upstream error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" schemas: Error: @@ -309,10 +327,27 @@ components: type: integer format: int32 description: Error code. + error_code: + $ref: "#/components/schemas/ErrorCode" message: type: string description: Error message. + ErrorCode: + type: string + description: >- + Stable machine-readable reason for an API error. New codes may be added + as other errors adopt this contract. Clients must handle absent or unknown + codes without interpreting the human-readable message. + enum: + - cluster_registration_invalid + - cluster_registration_conflict + - cluster_assignment_invalid + - cluster_assignment_project_not_found + - cluster_assignment_cluster_not_found + - cluster_assignment_requires_enterprise + - cluster_assignment_already_assigned + AdminAuthProviderProfile: type: object required: @@ -361,44 +396,79 @@ components: type: string format: email - AdminAuthProviderUserBootstrapRequest: + AdminTeamBootstrapRequest: + type: object + required: + - name + - email + properties: + name: + type: string + minLength: 1 + description: Team name. + email: + type: string + format: email + description: Billing/contact email for the team. + + AdminClusterCreateRequest: type: object required: - - oidc_issuer - - oidc_user_id - - oidc_user_email + - name + - endpoint + - endpoint_tls + - token properties: - oidc_issuer: + cluster_id: + type: string + format: uuid + description: Optional stable identifier for idempotent creation. Reuse succeeds only when the immutable configuration is identical. + name: type: string minLength: 1 - oidc_user_id: + endpoint: type: string minLength: 1 - oidc_user_email: + endpoint_tls: + type: boolean + token: type: string - format: email - oidc_user_name: + minLength: 1 + sandbox_proxy_domain: type: string nullable: true - signup_ip: + auth_org_id: type: string - signup_user_agent: + nullable: true + + AdminClusterCreateResponse: + type: object + required: + - cluster_id + properties: + cluster_id: type: string + format: uuid - AdminTeamBootstrapRequest: + AdminTeamBlockRequest: type: object required: - - name - - email + - reason properties: - name: + reason: type: string minLength: 1 - description: Team name. - email: + maxLength: 1000 + description: Shown to the team as the reason it is blocked. + + AdminTeamClusterAssignmentResponse: + type: object + required: + - cluster_id + properties: + cluster_id: type: string - format: email - description: Billing/contact email for the team. + format: uuid BuildStatus: type: string @@ -636,53 +706,42 @@ components: format: int64 concurrentSandboxes: type: integer - format: int32 + format: int64 concurrentTemplateBuilds: type: integer - format: int32 + format: int64 maxVcpu: type: integer - format: int32 + format: int64 maxRamMb: type: integer - format: int32 + format: int64 diskMb: type: integer - format: int32 + format: int64 eventsTtlDays: type: integer - format: int32 + format: int64 - UserTeam: + TeamLimitsResponse: type: object required: - - id - - name - - slug - tier - - email - - profilePictureUrl - - isBlocked - - isBanned - - blockedReason - - isDefault - limits - - createdAt properties: - id: - type: string - format: uuid - name: - type: string - slug: - type: string tier: type: string - email: - type: string - profilePictureUrl: - type: string - nullable: true + description: The team's raw tier identifier, exactly as stored (not normalized to a catalog plan). + limits: + $ref: "#/components/schemas/UserTeamLimits" + + TeamStatusResponse: + type: object + required: + - isBlocked + - isBanned + - blockedReason + properties: isBlocked: type: boolean isBanned: @@ -690,23 +749,6 @@ components: blockedReason: type: string nullable: true - isDefault: - type: boolean - limits: - $ref: "#/components/schemas/UserTeamLimits" - createdAt: - type: string - format: date-time - - UserTeamsResponse: - type: object - required: - - teams - properties: - teams: - type: array - items: - $ref: "#/components/schemas/UserTeam" TeamMember: type: object @@ -754,52 +796,6 @@ components: items: $ref: "#/components/schemas/TeamMember" - UpdateTeamRequest: - type: object - minProperties: 1 - properties: - name: - type: string - minLength: 1 - maxLength: 255 - profilePictureUrl: - type: string - nullable: true - - UpdateTeamResponse: - type: object - required: - - id - - name - properties: - id: - type: string - format: uuid - name: - type: string - profilePictureUrl: - type: string - nullable: true - - AddTeamMemberRequest: - type: object - required: - - email - properties: - email: - type: string - format: email - - CreateTeamRequest: - type: object - required: - - name - properties: - name: - type: string - minLength: 1 - maxLength: 255 - DefaultTemplateAlias: type: object required: @@ -1162,13 +1158,19 @@ components: slug: type: string - AdminControlPlaneProjectType: - type: string - enum: [development, staging, production] - - AdminControlPlaneProjectUpsertRequest: + ManagementProjectUpsertRequest: type: object - required: [name, slug, project_type] + description: >- + The properties of a project this side stores. Every one is synchronized + by the caller and sent on every push, so a reconcile is a complete + statement of the project rather than a patch. + + + A project's tier is not among them. It is assigned once, at creation, + from this side's own default, and no push moves it — limits arrive + separately and in full through upsertProjectLimits, which takes + precedence over the tier anyway. + required: [name, slug, email] properties: name: type: string @@ -1178,12 +1180,21 @@ components: type: string minLength: 1 maxLength: 63 - project_type: - $ref: "#/components/schemas/AdminControlPlaneProjectType" + description: >- + Changing it renames the project, and nothing follows it. Template + names embed the slug they were built under, so a renamed project + keeps its existing template names and only new ones carry the new + slug. A slug already held on this control plane is a 409, on a + rename as much as on a create. + email: + type: string + minLength: 1 + maxLength: 255 + description: Contact address recorded on the project. - AdminControlPlaneProject: + ManagementProject: allOf: - - $ref: "#/components/schemas/AdminControlPlaneProjectUpsertRequest" + - $ref: "#/components/schemas/ManagementProjectUpsertRequest" - type: object required: [id] properties: @@ -1191,16 +1202,120 @@ components: type: string format: uuid - AdminControlPlaneMemberUpsertRequest: + ManagementClusterRegistrationRequest: type: object + required: + - name + - endpoint + - endpoint_tls + - token properties: - added_by: + name: type: string - format: uuid + minLength: 1 + endpoint: + type: string + minLength: 1 + endpoint_tls: + type: boolean + token: + type: string + minLength: 1 + sandbox_proxy_domain: + type: string + nullable: true + auth_org_id: + type: string + nullable: true + + ManagementProjectMemberIdentity: + type: object + required: [issuer, subject] + properties: + issuer: + type: string + minLength: 1 + maxLength: 2048 + subject: + type: string + minLength: 1 + maxLength: 2048 + + ManagementProjectMemberApplyRequest: + type: object + required: [revision, present] + properties: + revision: + type: integer + format: int64 + minimum: 1 + present: + type: boolean + is_default: + type: boolean + default: false + description: Whether this membership is the user's default team. Omitted requests from older sources remain non-default. + identities: + type: array + maxItems: 16 + items: + $ref: "#/components/schemas/ManagementProjectMemberIdentity" + + ManagementProjectBlockRequest: + type: object + description: >- + Desired project block state. A newer revision replaces the state, + including any intervening manual block or unblock. + required: [revision, blocked] + properties: + revision: + type: integer + format: int64 + minimum: 1 + description: >- + Monotonic per-project revision. Older or duplicate revisions + succeed without changing stored state. + blocked: + type: boolean + description: Whether the project is refused billable work. + reason: + type: string + description: >- + Reason included in blocked-request errors. Cleared when blocked is false. + decided_at: + $ref: "#/components/schemas/ManagementDecidedAt" - AdminControlPlaneProjectLimits: + ManagementDecidedAt: + type: string + format: date-time + description: >- + When the caller decided this revision, in RFC 3339. Stored with the + revision and used to measure how long the decision took to apply here. + Omit it when the caller does not know; the delivery is then counted as + origin unknown rather than as zero lag. + + ManagementProjectLimits: type: object + description: >- + A project's effective limits, already resolved by the caller. Every + field is absolute: this side stores what it is given and performs no + arithmetic of its own. + + + The minimums below track the CHECK constraints on tiers, which is the + contract for what a limit may be. project_limits stores the same values + under looser constraints on purpose — it is a push target, and a floor + that only rejects the impossible keeps a future decision about what is + allowed a change to this schema rather than a migration. + + + `max_disk_size_mb` and `max_free_disk_size_mb` are one ceiling under two + names, and either name alone carries it. A caller that sends both must + send them equal; a caller that sends neither is refused. Sending both is + what a caller does while receivers older than the second name are still + running. required: + - revision - concurrent_sandboxes - max_sandbox_length_hours - max_vcpu @@ -1208,7 +1323,26 @@ components: - disk_mb - concurrent_template_builds - events_ttl_days + - default_free_disk_size_mb + - api_team_rps_list + anyOf: + - required: [max_free_disk_size_mb] + - required: [max_disk_size_mb] properties: + revision: + type: integer + format: int64 + minimum: 1 + description: >- + The caller's version of this answer, raised whenever the limits it + resolved for the project change. Delivery is over a network, so two + pushes can be in flight at once and arrive in either order: this + side stores the revision it accepted and drops a delivery at or + below it, which is what keeps a delayed retry from putting the + project back on limits it has already left. + + + Comparable only against earlier revisions for the same project. concurrent_sandboxes: type: integer format: int32 @@ -1237,14 +1371,47 @@ components: type: integer format: int32 minimum: 1 + default_free_disk_size_mb: + type: integer + format: int64 + minimum: 0 + description: >- + The default free-space growth target when a template build request + omits one. May sit anywhere at or below the maximum free-space + growth target, and usually sits well below it; a delivery whose + default exceeds the maximum is rejected. + max_free_disk_size_mb: + type: integer + format: int64 + minimum: 1 + description: >- + The most a template build may request as its free-space growth + target. + max_disk_size_mb: + type: integer + format: int64 + minimum: 1 + description: >- + The same ceiling as max_free_disk_size_mb, under the name it was + first published with. Kept until every deployed sender and receiver + speaks the other name. + api_team_rps_list: + type: integer + format: int64 + minimum: 0 + description: >- + Requests per second a project may make to each list endpoint. Zero + disables the limit. + decided_at: + $ref: "#/components/schemas/ManagementDecidedAt" tags: - name: builds + - name: control-plane-management + description: Workspace control-plane operations authenticated with service JWTs. - name: sandboxes - name: teams - name: templates - - name: workspace-admin - description: Workspace control-plane admin operations authenticated with service JWTs. paths: /health: @@ -1365,42 +1532,26 @@ paths: "500": $ref: "#/components/responses/500" - /teams: - get: - summary: List user teams - description: Returns all teams the authenticated user belongs to, with limits and default flag. - tags: [teams] - security: - - AuthProviderBearerAuth: [] - responses: - "200": - description: Successfully returned user teams. - content: - application/json: - schema: - $ref: "#/components/schemas/UserTeamsResponse" - "401": - $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" + /admin/teams/bootstrap: post: - summary: Create team - tags: [teams] + summary: Bootstrap team (retired) + description: >- + Retired: the operation never creates a team and always fails with 500. + The route and its request schema remain registered only until the Stripe + projects coordinator migrates to the workspace API. Create teams through + the workspace API instead. + deprecated: true + tags: [admin] security: - - AuthProviderBearerAuth: [] + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/CreateTeamRequest" + $ref: "#/components/schemas/AdminTeamBootstrapRequest" responses: - "200": - description: Successfully created team. - content: - application/json: - schema: - $ref: "#/components/schemas/TeamResolveResponse" "400": $ref: "#/components/responses/400" "401": @@ -1408,58 +1559,190 @@ paths: "500": $ref: "#/components/responses/500" - /admin/users/bootstrap: + /admin/clusters: post: - summary: Bootstrap auth provider user - tags: [teams] + summary: Create a cluster + description: Creates a cluster whose configuration cannot be modified. + tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/AdminAuthProviderUserBootstrapRequest" + $ref: "#/components/schemas/AdminClusterCreateRequest" responses: - "200": - description: Successfully bootstrapped user. + "201": + description: Cluster created. content: application/json: schema: - $ref: "#/components/schemas/TeamResolveResponse" + $ref: "#/components/schemas/AdminClusterCreateResponse" "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" "500": $ref: "#/components/responses/500" - /admin/teams/bootstrap: - post: - summary: Bootstrap team - description: Creates and provisions a team for an admin-authenticated bootstrap workflow. + /admin/clusters/{clusterID}: + delete: + summary: Delete an unreferenced cluster + description: Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + responses: + "204": + description: Cluster deleted or already absent. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + + /v1/management/clusters/{clusterID}/destroy-readiness: + get: + operationId: managementClusterDestroyReadiness + summary: Check cluster destroy readiness + description: Checks whether this exact cluster has active templates or snapshots. Soft-deleted history and team assignments do not block this check. Returns success if the cluster is absent. This read does not change resources or prevent later template creation. + tags: [control-plane-management] + security: + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + responses: + "204": + description: No active templates or snapshots reference the cluster. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "409": + description: Active templates or snapshots must be deleted before destroying the cluster. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + $ref: "#/components/responses/500" + + /admin/teams/{teamID}/cluster: + get: + summary: Get a team's assigned cluster + description: Returns the current cluster assignment without exposing cluster credentials. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "200": + description: Cluster assignment returned. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminTeamClusterAssignmentResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + /admin/teams/{teamID}/ban: + put: + summary: Ban a team + description: Marks the team as banned so its API keys stop authenticating. Idempotent; running workloads are not touched. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "204": + description: Team banned. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + delete: + summary: Unban a team + description: Clears the team's ban. Idempotent. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "204": + description: Team unbanned. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + + /admin/teams/{teamID}/block: + put: + summary: Block a team + description: Marks the team as blocked with the given reason, so it can no longer start sandboxes or builds. Idempotent; a repeated call replaces the reason. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/AdminTeamBootstrapRequest" + $ref: "#/components/schemas/AdminTeamBlockRequest" responses: - "200": - description: Successfully bootstrapped team. - content: - application/json: - schema: - $ref: "#/components/schemas/TeamResolveResponse" + "204": + description: Team blocked. "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" - "502": - $ref: "#/components/responses/502" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + delete: + summary: Unblock a team + description: Clears the team's block and its recorded reason. Idempotent. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "204": + description: Team unblocked. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" "500": $ref: "#/components/responses/500" @@ -1469,6 +1752,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: @@ -1495,6 +1779,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: @@ -1521,6 +1806,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - $ref: "#/components/parameters/userId" responses: @@ -1537,29 +1823,6 @@ paths: "500": $ref: "#/components/responses/500" - /admin/users/{userId}: - delete: - summary: Delete user - description: Deletes a user by removing the identity provider record, user_identities mapping, and public.users row. - tags: [admin] - security: - - AdminApiKeyAuth: [] - parameters: - - $ref: "#/components/parameters/userId" - responses: - "204": - description: Successfully deleted user. - "400": - $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" - "404": - $ref: "#/components/responses/404" - "409": - $ref: "#/components/responses/409" - "500": - $ref: "#/components/responses/500" - /teams/resolve: get: summary: Resolve team identity @@ -1585,109 +1848,80 @@ paths: "500": $ref: "#/components/responses/500" - /teams/{teamID}: - patch: - summary: Update team + /teams/{teamID}/status: + get: + summary: Get team access status + description: Returns whether the team is blocked or banned and its recorded blocked reason. Team-authenticated requests may read only the team they are scoped to. tags: [teams] security: - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - $ref: "#/components/parameters/teamID" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/UpdateTeamRequest" responses: "200": - description: Successfully updated team. + description: Successfully returned team access status. content: application/json: schema: - $ref: "#/components/schemas/UpdateTeamResponse" - "400": - $ref: "#/components/responses/400" + $ref: "#/components/schemas/TeamStatusResponse" "401": $ref: "#/components/responses/401" - "403": - $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" "500": $ref: "#/components/responses/500" - /teams/{teamID}/members: + /teams/{teamID}/limits: get: - summary: List team members + summary: Get team limits + description: Returns the team's tier and effective resource limits. Team-authenticated requests may read only the team they are scoped to. tags: [teams] security: - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - $ref: "#/components/parameters/teamID" responses: "200": - description: Successfully returned team members. + description: Successfully returned team limits. content: application/json: schema: - $ref: "#/components/schemas/TeamMembersResponse" + $ref: "#/components/schemas/TeamLimitsResponse" "401": $ref: "#/components/responses/401" - "403": - $ref: "#/components/responses/403" - "500": - $ref: "#/components/responses/500" - post: - summary: Add team member - tags: [teams] - security: - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - parameters: - - $ref: "#/components/parameters/teamID" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/AddTeamMemberRequest" - responses: - "201": - description: Successfully added team member. - "400": - $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" - "403": - $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" "500": $ref: "#/components/responses/500" - /teams/{teamID}/members/{userId}: - delete: - summary: Remove team member + /teams/{teamID}/members: + get: + summary: List team members tags: [teams] security: - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" - - $ref: "#/components/parameters/userId" responses: - "204": - description: Successfully removed team member. - "400": - $ref: "#/components/responses/400" + "200": + description: Successfully returned team members. + content: + application/json: + schema: + $ref: "#/components/schemas/TeamMembersResponse" "401": $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" "500": $ref: "#/components/responses/500" - /templates: get: summary: List team templates @@ -1889,13 +2123,13 @@ paths: "500": $ref: "#/components/responses/500" - /admin/v1/projects/{teamID}: + /v1/management/projects/{projectID}: parameters: - - $ref: "#/components/parameters/teamID" + - $ref: "#/components/parameters/projectID" put: - operationId: upsertProject - summary: Create or reconcile a project. - tags: [workspace-admin] + operationId: managementUpsertProject + summary: Create or reconcile a project (v1). + tags: [control-plane-management] security: - AdminJWTAuth: [] requestBody: @@ -1903,20 +2137,20 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProjectUpsertRequest" + $ref: "#/components/schemas/ManagementProjectUpsertRequest" responses: "200": description: Existing project reconciled. content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProject" + $ref: "#/components/schemas/ManagementProject" "201": description: Project created. content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProject" + $ref: "#/components/schemas/ManagementProject" "400": $ref: "#/components/responses/400" "401": @@ -1928,9 +2162,15 @@ paths: "501": $ref: "#/components/responses/501" delete: - operationId: deleteProject - summary: Delete a project and its control-plane state. - tags: [workspace-admin] + operationId: managementDeleteProject + summary: Delete a project and its control-plane state (v1). + description: >- + Declared, and answered with 501 by every control plane. Deleting a + project means reclaiming templates, snapshots, volumes, running + sandboxes and their stored artifacts, and no single service can reach + all of them today. Callers should not depend on this operation until + that changes. + tags: [control-plane-management] security: - AdminJWTAuth: [] responses: @@ -1945,44 +2185,57 @@ paths: "501": $ref: "#/components/responses/501" - /admin/v1/projects/{teamID}/members/{userId}: + /v1/management/projects/{projectID}/members/{userID}: parameters: - - $ref: "#/components/parameters/teamID" - - $ref: "#/components/parameters/userId" + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/userID" put: - operationId: upsertProjectMember - summary: Reconcile an opaque user UUID as a project member. - tags: [workspace-admin] + operationId: managementApplyProjectMember + summary: Apply one versioned project member projection (v1). + description: >- + Applies the newest desired presence for one project member. An older + or duplicate revision is accepted without changing target state. + tags: [control-plane-management] security: - AdminJWTAuth: [] requestBody: - required: false + required: true content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneMemberUpsertRequest" + $ref: "#/components/schemas/ManagementProjectMemberApplyRequest" responses: "204": - description: Membership is present. + description: Membership projection is applied or already superseded. "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" "500": $ref: "#/components/responses/500" - "501": - $ref: "#/components/responses/501" - delete: - operationId: deleteProjectMember - summary: Remove a project member. - tags: [workspace-admin] + + /v1/management/projects/{projectID}/limits: + parameters: + - $ref: "#/components/parameters/projectID" + put: + operationId: managementUpsertProjectLimits + summary: Reconcile effective limits for a project (v1). + tags: [control-plane-management] security: - AdminJWTAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ManagementProjectLimits" responses: "204": - description: Membership is absent. + description: Effective limits are synchronized. "400": $ref: "#/components/responses/400" "401": @@ -1994,13 +2247,17 @@ paths: "501": $ref: "#/components/responses/501" - /admin/v1/projects/{teamID}/limits: + /v1/management/projects/{projectID}/block: parameters: - - $ref: "#/components/parameters/teamID" + - $ref: "#/components/parameters/projectID" put: - operationId: upsertProjectLimits - summary: Reconcile effective limits for a project. - tags: [workspace-admin] + operationId: managementApplyProjectBlock + summary: Apply a project's block state (v1). + description: >- + Records whether the project is refused billable work, which the auth + path reads before it lets a sandbox start. An older or duplicate + revision is accepted without changing target state. + tags: [control-plane-management] security: - AdminJWTAuth: [] requestBody: @@ -2008,10 +2265,10 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProjectLimits" + $ref: "#/components/schemas/ManagementProjectBlockRequest" responses: "204": - description: Effective limits are synchronized. + description: Block state is applied or already superseded. "400": $ref: "#/components/responses/400" "401": @@ -2023,23 +2280,90 @@ paths: "501": $ref: "#/components/responses/501" - /admin/v1/users/{userId}: + /v1/management/clusters/{clusterID}: parameters: - - $ref: "#/components/parameters/userId" + - $ref: "#/components/parameters/clusterID" + put: + operationId: managementRegisterCluster + summary: Register a cluster (v1). + tags: [control-plane-management] + security: + - AdminJWTAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ManagementClusterRegistrationRequest" + responses: + "204": + description: Cluster registration is present. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" delete: - operationId: purgeUser - summary: Purge shard-local membership and access-token state for an opaque user UUID. - tags: [workspace-admin] + operationId: managementDeleteCluster + summary: Delete an unreferenced cluster (v1). + description: Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. + tags: [control-plane-management] security: - AdminJWTAuth: [] responses: "204": - description: User-owned shard state is absent. + description: Cluster is absent. + "401": + $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + + /v1/management/projects/{projectID}/cluster/{clusterID}: + parameters: + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/clusterID" + put: + operationId: managementAssignProjectCluster + summary: Assign a cluster to a project (v1). + description: >- + Assigns the cluster only when the project's tier identifier contains `enterprise`, + case-insensitively. Replaying the identical assignment succeeds even if the project's + tier later changes. + tags: [control-plane-management] + security: + - AdminJWTAuth: [] + responses: + "204": + description: Cluster is assigned to the project. "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + delete: + operationId: managementDetachProjectCluster + summary: Detach a cluster assignment from a project (v1). + tags: [control-plane-management] + security: + - AdminJWTAuth: [] + responses: + "204": + description: The matching assignment is absent. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" "500": $ref: "#/components/responses/500" - "501": - $ref: "#/components/responses/501" diff --git a/spec/openapi.infra.yaml b/spec/openapi.infra.yaml index 4a26b3a42..e10df0430 100644 --- a/spec/openapi.infra.yaml +++ b/spec/openapi.infra.yaml @@ -12,14 +12,6 @@ components: type: apiKey in: header name: X-API-Key - AccessTokenAuth: - type: http - scheme: bearer - bearerFormat: access_token - description: | - **Deprecated.** Access token authentication is deprecated and will be - removed in a future release. Use API key authentication (`X-API-Key`) - instead. # AuthProviderBearerAuth / AuthProviderTeamAuth: B before T in the name # so Bearer is validated before Team. AuthProviderBearerAuth: @@ -36,12 +28,31 @@ components: type: apiKey in: header name: X-Admin-Token + AdminJWTAuth: + type: http + scheme: bearer + bearerFormat: JWT AdminTeamAuth: type: apiKey in: header name: X-Team-ID parameters: + clusterID: + name: clusterID + in: path + required: true + schema: + type: string + format: uuid + description: Identifier of the cluster + rigID: + name: rigID + in: path + required: true + schema: + type: string + description: Rig identifier (e.g. "default") templateID: name: templateID in: path @@ -64,6 +75,7 @@ components: name: teamID in: path required: true + description: Identifier of the team, as its UUID or its public project ID (prj_) schema: type: string nodeID: @@ -78,12 +90,6 @@ components: required: true schema: type: string - accessTokenID: - name: accessTokenID - in: path - required: true - schema: - type: string snapshotID: name: snapshotID in: path @@ -122,6 +128,35 @@ components: required: true schema: type: string + secretID: + name: secretID + in: path + required: true + schema: + type: string + description: > + Identifier of the secret (sec_ prefixed), or its canonical + lower-case name + + webhookID: + name: webhookID + in: path + required: true + schema: + type: string + format: uuid + headers: + XNextToken: + description: Cursor to fetch the next page of results, if more exist + schema: + type: string + XTotalRunning: + description: > + Number of running sandboxes matching the filters, before pagination is applied. + Only present when running sandboxes were requested. + schema: + type: integer + format: int32 responses: "400": @@ -154,8 +189,16 @@ components: application/json: schema: $ref: "#/components/schemas/Error" - "410": - description: Gone + "429": + description: Too many requests + headers: + Retry-After: + description: When present, the number of seconds to wait before retrying the request. + required: false + schema: + type: integer + minimum: 0 + example: 30 content: application/json: schema: @@ -166,8 +209,129 @@ components: application/json: schema: $ref: "#/components/schemas/Error" + "501": + description: Not implemented by this deployment + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "502": + description: Backend error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "503": + description: Service unavailable + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "504": + description: Backend timeout + content: + application/json: + schema: + $ref: "#/components/schemas/Error" schemas: + Rig: + description: An orchestrator node pool backed by one cloud scaling group + required: + - id + - provider + - resourceID + - capacityDesired + - capacityCurrent + properties: + id: + type: string + description: Rig identifier (e.g. "default") + provider: + type: string + description: Cloud provider backing the rig ("aws" or "gcp") + resourceID: + type: string + description: Canonical cloud resource ID of the scaling group backing the rig (ARN on AWS, self-link on GCP) + capacityDesired: + type: integer + format: int32 + description: Desired number of instances in the rig + capacityMin: + type: integer + format: int32 + description: > + Minimum capacity enforced on the rig's scaling group. Omitted when + nothing enforces bounds (GCP MIG without an active autoscaler). + capacityMax: + type: integer + format: int32 + description: > + Maximum capacity enforced on the rig's scaling group. Omitted when + nothing enforces bounds (GCP MIG without an active autoscaler). + capacityCurrent: + type: integer + format: int32 + description: Number of instances currently attached to the rig + + RigCapacityChange: + description: Desired capacity to set on the rig's scaling group + required: + - desired + properties: + desired: + type: integer + format: int32 + minimum: 0 + description: Absolute desired number of instances in the rig + + RigInstance: + description: An instance attached to a rig's scaling group + required: + - id + - transitioning + - terminating + properties: + id: + type: string + description: Provider instance ID (EC2 instance ID on AWS, instance name on GCP), also the node ID the orchestrator reports + createdAt: + type: string + format: date-time + description: When the provider created the instance. Omitted while the instance is transitioning. + transitioning: + type: boolean + description: The provider is creating, deleting, recreating or otherwise mutating the instance + terminating: + type: boolean + description: The instance is on its way out of the group and can never become healthy again + + RigError: + description: > + Scaling error on the rig's scaling group, e.g. a failed instance + creation due to resource exhaustion + required: + - timestamp + - code + - message + properties: + timestamp: + type: string + format: date-time + description: When the error occurred + code: + type: string + description: Provider-specific error code (e.g. ZONE_RESOURCE_POOL_EXHAUSTED, Failed) + message: + type: string + description: Human-readable error message + instance: + type: string + description: Instance the error relates to, if any + action: + type: string + description: Action being performed when the error occurred (e.g. CREATING) + Team: required: - teamID @@ -238,6 +402,17 @@ components: minimum: 0 description: Disk size for the sandbox in MiB + MinFreeDiskMb: + type: integer + format: int32 + minimum: 0 + description: >- + Requested minimum free space after the template's build steps, in MiB. + Omit to use the team's default. Set to 0 to request no minimum free-disk + growth. The filesystem is never shrunk, including inherited or already-larger + filesystems. Growth is best effort, so filesystem metadata can leave the + available space slightly below the requested minimum. + EnvdVersion: type: string description: Version of the envd running in the sandbox @@ -254,6 +429,14 @@ components: - running - paused + OrderDirection: + type: string + description: Sort direction + default: desc + enum: + - asc + - desc + SnapshotInfo: type: object required: @@ -302,12 +485,27 @@ components: maskRequestHost: type: string description: Specify host mask which will be used for all sandbox requests + httpsPorts: + type: array + description: Sandbox ports that serve HTTPS rather than plaintext HTTP. Affects how the proxy reaches the service inside the sandbox; the public URL is HTTPS either way. Certificates are not verified, so self-signed ones work. The envd port (49983) cannot be listed. + maxItems: 128 + uniqueItems: true + items: + type: integer + format: uint32 + minimum: 1 + maximum: 65535 rules: type: object description: > - Per-domain transform rules applied to matching egress HTTP/HTTPS requests. - Keys are domains (e.g. "api.example.com", "example.com"). - A domain listed here is not automatically allowed - use allowOut to permit the traffic. + Per-domain transform rules applied to matching outbound HTTPS requests. + Keys may be exact DNS names (for example, "api.example.com") or a leading wildcard + (for example, "*.example.com"), and are normalized to lowercase on write. + Wildcards match subdomains at any depth but not the apex domain; a bare "*" is invalid. + Exact rules take precedence, followed by the longest matching wildcard suffix, and + matching rule sets are not merged. Broad wildcards such as "*.com" are allowed and may + expose transformed credentials to every matching destination the sandbox contacts. + Rules do not grant network access; configure allowOut separately to permit the destination. additionalProperties: type: array items: @@ -331,7 +529,15 @@ components: $ref: "#/components/schemas/SandboxEgressProxyConfig" rules: type: object - description: Per-domain transform rules. Replaces all existing rules when provided. + description: > + Per-domain transform rules applied to matching outbound HTTPS requests. Replaces all + existing rules when provided. Keys may be exact DNS names or a single leading wildcard + (for example, "*.example.com"), and are normalized to lowercase on write. Wildcards match + subdomains at any depth but not the apex domain; a bare "*" is invalid. Exact rules take + precedence, followed by the longest matching wildcard suffix, and matching rule sets are + not merged. Broad wildcards such as "*.com" are allowed and may expose transformed + credentials to every matching destination the sandbox contacts. Rules do not grant + network access; configure allowOut separately to permit the destination. additionalProperties: type: array items: @@ -386,6 +592,43 @@ components: maxLength: 255 description: >- Optional SOCKS5 password (RFC 1929), max 255 bytes. + tls: + $ref: "#/components/schemas/SandboxEgressProxyTLSConfig" + + SandboxEgressProxyTLSConfig: + type: object + nullable: true + description: >- + TLS for the connection to the SOCKS5 proxy. The SOCKS5 negotiation and + the tunneled traffic both run inside the TLS session, so the proxy + credentials are not sent in the clear. This secures only the hop to the + proxy; what the proxy does onward is its own concern. A half-close from + the sandbox reaches the proxy as a TLS close_notify, not a TCP FIN, and + a proxy that treats close_notify as a full close cuts the reply short. + required: + - enabled + properties: + enabled: + type: boolean + description: >- + Connect to the proxy over TLS. When false, no other field in this + object may be set. + serverName: + type: string + maxLength: 253 + description: >- + Name to verify the proxy certificate against, and to send as SNI. + Defaults to the host part of address. Set this only when the + certificate does not match the address the proxy is reached at. + caCert: + type: string + maxLength: 8192 + description: >- + One or more PEM-encoded certificates to verify the proxy against, + for a proxy fronted by a private CA. These replace the system trust + store, which is what is used when this is omitted. The system trust + store depends on the host the orchestrator runs on, so set this to + get the same verification everywhere. SandboxAutoResumeEnabled: type: boolean @@ -759,6 +1002,59 @@ components: items: $ref: "#/components/schemas/SandboxVolumeMount" + NewSandboxV2: + description: >- + Sandbox creation request. All system communication with the sandbox is + always secured; the template's envd version must support secured access. + required: + - templateID + properties: + templateID: + type: string + description: Identifier of the required template + timeout: + type: integer + format: int32 + minimum: 1 + default: 300 + description: Time to live for the sandbox in seconds. + autoPause: + type: boolean + default: false + description: Automatically pauses the sandbox after the timeout + autoPauseMemory: + type: boolean + default: true + description: >- + Controls the snapshot kind taken when the sandbox auto-pauses on + timeout (only relevant when autoPause is true). When false, the + auto-pause drops the in-memory state and persists only the + filesystem (a filesystem-only snapshot); resuming it cold-boots + (reboots) the sandbox from disk. Such a snapshot cannot be + auto-resumed by traffic and must be resumed explicitly, so it cannot + be combined with autoResume. Defaults to true (full memory snapshot). + autoResume: + $ref: "#/components/schemas/SandboxAutoResumeConfig" + allow_internet_access: + type: boolean + description: + Allow sandbox to access the internet. When set to false, it behaves the same as specifying denyOut + to 0.0.0.0/0 in the network config. + network: + $ref: "#/components/schemas/SandboxNetworkConfig" + metadata: + $ref: "#/components/schemas/SandboxMetadata" + envVars: + $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: >- @@ -800,6 +1096,15 @@ components: type: boolean deprecated: true description: Automatically pauses the sandbox after the timeout + memory: + type: boolean + description: >- + Defaults to true. When false, resume from disk state only: the sandbox cold-boots fresh and + any memory in the snapshot is ignored, never modified or deleted. Disk + state has crash-recovery semantics — writes not flushed before the pause + may be lost. A no-op for snapshots that contain no memory. Rejected with + an error in environments where this capability is not enabled, never + silently downgraded to a memory restore. ConnectSandbox: type: object @@ -811,6 +1116,34 @@ components: type: integer format: int32 minimum: 0 + memory: + type: boolean + description: >- + Defaults to true. When false and the sandbox is paused, resume from disk state only: the + sandbox cold-boots fresh and any memory in the snapshot is ignored, never + modified or deleted. Disk state has crash-recovery semantics — writes not + flushed before the pause may be lost. A no-op for snapshots that contain + no memory. Rejected with an error in environments where this capability + is not enabled, never silently downgraded to a memory restore. + + ConnectSandboxV2: + type: object + properties: + timeout: + description: Timeout in seconds from the current time after which the sandbox should expire + type: integer + format: int32 + minimum: 1 + default: 300 + memory: + type: boolean + description: >- + Defaults to true. When false and the sandbox is paused, resume from disk state only: the + sandbox cold-boots fresh and any memory in the snapshot is ignored, never + modified or deleted. Disk state has crash-recovery semantics — writes not + flushed before the pause may be lost. A no-op for snapshots that contain + no memory. Rejected with an error in environments where this capability + is not enabled, never silently downgraded to a memory restore. SandboxTimeoutRequest: type: object @@ -838,6 +1171,15 @@ components: name: type: string description: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one. + memory: + type: boolean + description: >- + Whether to capture a full memory snapshot. When false, only the + filesystem is persisted: the snapshot is smaller and faster to take, + and sandboxes created from it cold-boot (start fresh from disk) + instead of restoring memory, so they begin without the source + sandbox's running processes, in-memory state, and open connections. + The source sandbox keeps running in both cases. Defaults to true. SandboxPauseRequest: type: object @@ -944,6 +1286,17 @@ components: type: integer description: Number of sandboxes that failed to kill + AdminTeamRunningSandboxCounts: + type: object + description: | + Cached live sandbox index count keyed by team ID. Counts may briefly + include sandboxes transitioning out of running; teams without indexed + sandboxes are omitted. + additionalProperties: + type: integer + format: int64 + minimum: 1 + AdminBuildCancelResult: required: - cancelledCount @@ -1074,122 +1427,57 @@ components: items: type: string - TemplateLegacy: + TemplateBuild: required: - - templateID - buildID - - cpuCount - - memoryMB - - diskSizeMB - - public + - status - createdAt - updatedAt - - createdBy - - lastSpawnedAt - - spawnCount - - buildCount - - envdVersion - - aliases + - cpuCount + - memoryMB properties: - templateID: - type: string - description: Identifier of the template buildID: type: string - description: Identifier of the last successful build for given template + format: uuid + description: Identifier of the build + status: + $ref: "#/components/schemas/TemplateBuildStatus" + createdAt: + type: string + format: date-time + description: Time when the build was created + updatedAt: + type: string + format: date-time + description: Time when the build was last updated + finishedAt: + type: string + format: date-time + description: Time when the build was finished cpuCount: $ref: "#/components/schemas/CPUCount" memoryMB: $ref: "#/components/schemas/MemoryMB" diskSizeMB: $ref: "#/components/schemas/DiskSizeMB" - public: - type: boolean - description: Whether the template is public or only accessible by the team - aliases: - type: array - description: Aliases of the template - items: - type: string - createdAt: - type: string - format: date-time - description: Time when the template was created - updatedAt: - type: string - format: date-time - description: Time when the template was last updated - createdBy: - allOf: - - $ref: "#/components/schemas/TeamUser" - nullable: true - lastSpawnedAt: - type: string - nullable: true - format: date-time - description: Time when the template was last used - spawnCount: - type: integer - format: int64 - description: Number of times the template was used - buildCount: - type: integer - format: int32 - description: Number of times the template was built - envdVersion: - $ref: "#/components/schemas/EnvdVersion" - - TemplateBuild: - required: - - buildID - - status - - createdAt - - updatedAt - - cpuCount - - memoryMB - properties: - buildID: - type: string - format: uuid - description: Identifier of the build - status: - $ref: "#/components/schemas/TemplateBuildStatus" - createdAt: - type: string - format: date-time - description: Time when the build was created - updatedAt: - type: string - format: date-time - description: Time when the build was last updated - finishedAt: - type: string - format: date-time - description: Time when the build was finished - cpuCount: - $ref: "#/components/schemas/CPUCount" - memoryMB: - $ref: "#/components/schemas/MemoryMB" - diskSizeMB: - $ref: "#/components/schemas/DiskSizeMB" - envdVersion: - $ref: "#/components/schemas/EnvdVersion" - - TemplateWithBuilds: - required: - - templateID - - public - - aliases - - names - - createdAt - - updatedAt - - lastSpawnedAt - - spawnCount - - builds - properties: - templateID: - type: string - description: Identifier of the template + envdVersion: + $ref: "#/components/schemas/EnvdVersion" + + TemplateWithBuilds: + required: + - templateID + - public + - aliases + - names + - createdAt + - updatedAt + - lastSpawnedAt + - spawnCount + - builds + properties: + templateID: + type: string + description: Identifier of the template public: type: boolean description: Whether the template is public or only accessible by the team @@ -1239,30 +1527,6 @@ components: type: boolean description: Whether the template is public or only accessible by the team - TemplateBuildRequest: - required: - - dockerfile - properties: - alias: - description: Alias of the template - type: string - dockerfile: - description: Dockerfile for the template - type: string - teamID: - type: string - description: Identifier of the team - startCmd: - description: Start command to execute in the template after the build - type: string - readyCmd: - description: Ready check command to execute in the template after the build - type: string - cpuCount: - $ref: "#/components/schemas/CPUCount" - memoryMB: - $ref: "#/components/schemas/MemoryMB" - TemplateStep: description: Step in the template build process required: @@ -1279,7 +1543,8 @@ components: type: string filesHash: type: string - description: Hash of the files used in the step + pattern: "^[0-9a-f]{64}$" + description: Hash of the files used in the step (lowercase hex SHA-256) force: default: false type: boolean @@ -1304,27 +1569,13 @@ components: teamID: deprecated: true type: string - description: Identifier of the team - cpuCount: - $ref: "#/components/schemas/CPUCount" - memoryMB: - $ref: "#/components/schemas/MemoryMB" - - TemplateBuildRequestV2: - required: - - alias - properties: - alias: - description: Alias of the template - type: string - teamID: - deprecated: true - type: string - description: Identifier of the team + description: Identifier of the team, as its UUID or its public project ID (prj_) cpuCount: $ref: "#/components/schemas/CPUCount" memoryMB: $ref: "#/components/schemas/MemoryMB" + minFreeDiskMb: + $ref: "#/components/schemas/MinFreeDiskMb" FromImageRegistry: oneOf: @@ -1394,12 +1645,15 @@ components: TemplateBuildStartV2: type: object + description: Exactly one of fromImage or fromTemplate must be given and non-empty. properties: fromImage: type: string + minLength: 1 description: Image to use as a base for the template build fromTemplate: type: string + minLength: 1 description: Template to use as a base for the template build fromImageRegistry: $ref: "#/components/schemas/FromImageRegistry" @@ -1430,6 +1684,11 @@ components: url: description: Url where the file should be uploaded to type: string + headers: + description: Request headers that must be sent with the upload request + type: object + additionalProperties: + type: string LogLevel: type: string @@ -1495,7 +1754,8 @@ components: properties: logs: default: [] - description: Build logs + deprecated: true + description: Build logs (always empty since the V1 build path was removed, use logEntries) type: array items: type: string @@ -1551,7 +1811,6 @@ components: type: string description: | Status of the node. - - draining: the node is bound to be shut down. It will not accept new sandboxes and will stop once all existing sandboxes are done. - standby: the node is not actively used, but it can return to ready and continue serving traffic. enum: - ready @@ -1559,12 +1818,14 @@ components: - connecting - unhealthy - standby + - shutting_down x-enum-varnames: - NodeStatusReady - NodeStatusDraining - NodeStatusConnecting - NodeStatusUnhealthy - NodeStatusStandby + - NodeStatusShuttingDown NodeStatusChange: required: @@ -1691,6 +1952,8 @@ components: - status - statusChangedAt - sandboxCount + - outstandingWork + - maxSandboxes - metrics - createSuccesses - createFails @@ -1726,6 +1989,15 @@ components: type: integer format: uint32 description: Number of sandboxes running on the node + maxSandboxes: + type: integer + format: int64 + description: Cached node-scoped sandbox admission limit. Nonpositive values reject creation. + outstandingWork: + type: integer + format: uint64 + minimum: 0 + description: Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. metrics: $ref: "#/components/schemas/NodeMetrics" createSuccesses: @@ -1749,7 +2021,8 @@ components: - status - statusChangedAt - sandboxCount - - cachedBuilds + - outstandingWork + - maxSandboxes - createSuccesses - createFails - version @@ -1784,13 +2057,17 @@ components: type: integer format: uint32 description: Number of sandboxes running on the node + maxSandboxes: + type: integer + format: int64 + description: Cached node-scoped sandbox admission limit. Nonpositive values reject creation. + outstandingWork: + type: integer + format: uint64 + minimum: 0 + description: Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. metrics: $ref: "#/components/schemas/NodeMetrics" - cachedBuilds: - type: array - description: List of cached builds id on the node - items: - type: string createSuccesses: type: integer format: uint64 @@ -1800,39 +2077,6 @@ components: format: uint64 description: Number of sandbox create fails - CreatedAccessToken: - required: - - id - - name - - token - - mask - - createdAt - properties: - id: - type: string - format: uuid - description: Identifier of the access token - name: - type: string - description: Name of the access token - token: - type: string - description: The fully created access token - mask: - $ref: "#/components/schemas/IdentifierMaskingDetails" - createdAt: - type: string - format: date-time - description: Timestamp of access token creation - - NewAccessToken: - required: - - name - properties: - name: - type: string - description: Name of the access token - TeamAPIKey: required: - id @@ -1983,6 +2227,13 @@ components: type: integer format: int32 description: Error code + error_code: + type: string + description: >- + Machine-readable semantic error code. Not a closed set; initial values: + sandbox_capacity_unavailable, sandbox_placement_timeout, + sandbox_no_compatible_node, sandbox_create_failed, internal_server_error, + secret_limit_reached. message: type: string description: Error @@ -2032,6 +2283,13 @@ components: token: type: string description: Auth token to use for interacting with volume content + domain: + type: string + description: | + Domain to use as the destination for volume content requests, + replacing the default `api.`. Only returned when the + team is connected to a custom (BYOC) cluster; absent otherwise, in + which case the default domain is used. required: - volumeID - name @@ -2047,64 +2305,489 @@ components: required: - name -tags: - - name: templates - - name: sandboxes - - name: auth - - name: access-tokens - - name: api-keys - - name: tags - - name: volumes + SecretMetadata: + type: object + description: > + Customer metadata of the secret. Always present, empty when unset. + At most 32 entries; keys are limited to 128 bytes, values to 1024 + bytes, and a secret's metadata to 8192 bytes in total. + maxProperties: 32 + additionalProperties: + type: string + maxLength: 1024 -paths: - /health: - get: - summary: Health check - description: Health check - responses: - "204": - description: The service is healthy - "401": - $ref: "#/components/responses/401" + Secret: + type: object + description: Metadata of a secret. It never carries the secret value. + required: + - secretID + - name + - currentVersion + - metadata + - createdAt + - updatedAt + properties: + secretID: + type: string + description: Identifier of the secret + name: + type: string + description: Name of the secret, unique within the project + currentVersion: + type: integer + format: int64 + description: Version served to readers that do not name one + metadata: + $ref: "#/components/schemas/SecretMetadata" + createdAt: + type: string + format: date-time + description: Time when the secret was created + updatedAt: + type: string + format: date-time + description: Time when the secret was last updated - /teams: - get: - summary: List teams - description: List all teams - tags: [auth] - security: - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - responses: - "200": - description: Successfully returned all teams - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Team" - "401": - $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" + NewSecret: + type: object + required: + - name + - value + properties: + name: + type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-Z0-9_-]+$' + description: > + Name of the secret, unique within the project. Names are + lower-cased before storage and returned in that canonical form; + the sec_ prefix is reserved for secret identifiers. + value: + type: string + description: Runtime marker stored as the secret's first version. The runtime resolves it to a value at sandbox egress. + metadata: + $ref: "#/components/schemas/SecretMetadata" - /teams/{teamID}/metrics: - get: - summary: Team metrics - description: Get metrics for the team - tags: [auth] - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/teamID" - - in: query - name: start - schema: + SecretUpdate: + type: object + required: + - value + properties: + value: + type: string + description: Runtime marker stored as the secret's new version. The runtime resolves it to a value at sandbox egress. + metadata: + $ref: "#/components/schemas/SecretMetadata" + + SandboxEvent: + description: Sandbox event + required: + - id + - version + - type + - timestamp + - sandboxId + - sandboxExecutionId + - sandboxTemplateId + - sandboxBuildId + - sandboxTeamId + properties: + id: + type: string + format: uuid + description: Event unique identifier + version: + type: string + description: Event structure version + type: + type: string + description: Event name + eventCategory: + type: string + deprecated: true + description: Category of the event (e.g., 'lifecycle', 'process', etc.) + eventLabel: + type: string + deprecated: true + description: Label for the specific event type (e.g., 'sandbox_started', 'process_oom', etc.) + eventData: + type: object + nullable: true + description: Optional JSON data associated with the event + + timestamp: + type: string + format: date-time + description: Timestamp of the event + sandboxId: + type: string + format: string + description: Unique identifier for the sandbox + sandboxExecutionId: + type: string + format: string + description: Unique identifier for the sandbox execution + sandboxTemplateId: + type: string + format: string + description: Unique identifier for the sandbox template + sandboxBuildId: + type: string + format: string + description: Unique identifier for the sandbox build + sandboxTeamId: + type: string + format: uuid + description: Team identifier associated with the sandbox + WebhookCreate: + description: Configuration for registering new webhooks + required: + - name + - url + - events + - signatureSecret + properties: + name: + type: string + url: + type: string + format: uri + events: + type: array + items: + type: string + enabled: + type: boolean + default: true + signatureSecret: + type: string + description: Secret used to sign the webhook payloads + WebhookCreation: + description: Webhook creation response + required: + - id + - name + - createdAt + - teamId + - url + - enabled + - events + properties: + id: + type: string + description: Webhook unique identifier + name: + type: string + description: Webhook user friendly name + createdAt: + type: string + format: date-time + description: Time when the template was created + teamId: + type: string + description: Unique identifier for the team + url: + type: string + format: uri + enabled: + type: boolean + events: + type: array + items: + type: string + WebhookDetail: + description: Webhook detail response + required: + - id + - teamId + - name + - createdAt + - url + - enabled + - events + properties: + id: + type: string + description: Webhook unique identifier + teamId: + type: string + description: Unique identifier for the team + name: + type: string + description: Webhook user friendly name + createdAt: + type: string + format: date-time + description: Time when the template was created + url: + type: string + format: uri + enabled: + type: boolean + events: + type: array + items: + type: string + WebhookConfiguration: + description: Configuration for updating existing webhooks + properties: + enabled: + type: boolean + name: + type: string + description: Webhook user friendly name + url: + type: string + format: uri + events: + type: array + items: + type: string + signatureSecret: + type: string + description: Secret used to sign the webhook payloads + WebhookDelivery: + description: Webhook delivery attempt + required: + - id + - teamId + - webhookId + - eventId + - sandboxId + - eventType + - status + - durationMs + - requestBody + - requestHeaders + - requestUrl + - errorClass + - timestamp + properties: + id: + type: string + format: uuid + description: Delivery attempt identifier + teamId: + type: string + format: uuid + description: Team identifier + webhookId: + type: string + format: uuid + description: Webhook configuration identifier + eventId: + type: string + format: uuid + description: Sandbox event identifier + sandboxId: + type: string + description: Sandbox identifier + eventType: + type: string + description: Sandbox event type + status: + type: string + enum: [success, failed] + description: Delivery attempt status + durationMs: + type: integer + format: int32 + description: Delivery request duration in milliseconds + requestBody: + type: string + description: Serialized webhook request body + requestHeaders: + type: string + description: JSON-encoded request headers with sensitive values redacted + requestUrl: + type: string + format: uri + description: URL attempted for this delivery + responseBody: + type: string + nullable: true + description: Truncated response body, if a response was received + responseHeaders: + type: string + nullable: true + description: JSON-encoded response headers, if a response was received + responseHttpStatusCode: + type: integer + format: int32 + nullable: true + description: HTTP response status code, if a response was received + errorClass: + type: string + nullable: true + enum: + - http_error + - dns_error + - timeout + - transport_error + - request_error + - signature_error + - canceled + description: Machine-readable non-HTTP or HTTP failure class + errorMessage: + type: string + nullable: true + description: Error message for failures without a useful response body + timestamp: + type: string + format: date-time + description: Time when the delivery attempt started + WebhookDeliveryStats: + description: Webhook delivery aggregate stats + required: + - buckets + - total + - failed + - durationMs + properties: + buckets: + type: array + items: + $ref: "#/components/schemas/WebhookDeliveryStatsBucket" + total: + type: integer + format: int64 + failed: + type: integer + format: int64 + durationMs: + $ref: "#/components/schemas/WebhookDeliveryDurationStats" + WebhookDeliveryDurationStats: + description: Webhook delivery duration statistics in milliseconds + required: + - minimum + - average + - maximum + properties: + minimum: + type: number + format: double + average: + type: number + format: double + maximum: + type: number + format: double + WebhookDeliveryStatsBucket: + description: Webhook delivery stats for a time bucket + required: + - timestamp + - total + - failed + - durationMs + properties: + timestamp: + type: string + format: date-time + total: + type: integer + format: int64 + failed: + type: integer + format: int64 + durationMs: + $ref: "#/components/schemas/WebhookDeliveryDurationStats" + WebhookDeliveryGroup: + description: Webhook delivery attempts grouped by sandbox event + required: + - eventId + - eventType + - sandboxId + - attempts + properties: + eventId: + type: string + format: uuid + eventType: + type: string + sandboxId: + type: string + attempts: + type: array + items: + $ref: "#/components/schemas/WebhookDelivery" + WebhookDeliveriesListPayload: + description: Paginated webhook delivery attempts grouped by event + required: + - data + - nextCursor + properties: + data: + type: array + items: + $ref: "#/components/schemas/WebhookDeliveryGroup" + nextCursor: + type: string + nullable: true + description: Cursor to pass to the next list request, or null when there is no next page. +tags: + - name: templates + - name: sandboxes + - name: auth + - name: api-keys + - name: tags + - name: volumes + - name: secrets + +paths: + /health: + get: + summary: Health check + description: Health check + responses: + "204": + description: The service is healthy + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + + /teams: + get: + summary: List teams + description: List all teams + tags: [auth] + security: + - AuthProviderBearerAuth: [] + responses: + "200": + description: Successfully returned all teams + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Team" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /teams/{teamID}/metrics: + get: + summary: Team metrics + description: Get metrics for the team + tags: [auth] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + - in: query + name: start + schema: type: integer format: int64 minimum: 0 @@ -2131,6 +2814,8 @@ paths: $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2145,6 +2830,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" - in: query @@ -2181,12 +2868,15 @@ paths: $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /sandboxes: get: summary: List running sandboxes + x-api-group: list description: List all running sandboxes. Use GET /v2/sandboxes instead. deprecated: true tags: [sandboxes] @@ -2196,6 +2886,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - name: metadata in: query @@ -2216,11 +2908,14 @@ paths: $ref: "#/components/responses/401" "400": $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" post: summary: Create sandbox - description: Create a sandbox from the template + description: Create a sandbox from the template. Use POST /v2/sandboxes instead. + deprecated: true tags: [sandboxes] security: - ApiKeyAuth: [] @@ -2228,6 +2923,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: @@ -2245,13 +2942,19 @@ paths: $ref: "#/components/responses/401" "400": $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" /v2/sandboxes: - get: - summary: List sandboxes (v2) - description: List all sandboxes + post: + summary: Create sandbox (v2) + description: Create a sandbox from the template. All system communication with the sandbox is secured. tags: [sandboxes] security: - ApiKeyAuth: [] @@ -2259,28 +2962,92 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - parameters: - - name: metadata - in: query - description: Metadata query used to filter the sandboxes (e.g. "user=abc&app=prod"). Each key and values must be URL encoded. - required: false - schema: - type: string - - name: state - in: query - description: Filter sandboxes by one or more states - required: false + - AdminJWTAuth: [] + AdminTeamAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewSandboxV2" + responses: + "201": + description: The sandbox was created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" + get: + summary: List sandboxes (v2) + x-api-group: list + description: List all sandboxes + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - name: metadata + in: query + description: Metadata query used to filter the sandboxes (e.g. "user=abc&app=prod"). Each key and values must be URL encoded. + required: false + schema: + type: string + - name: state + in: query + description: Filter sandboxes by one or more states + required: false schema: type: array items: $ref: "#/components/schemas/SandboxState" style: form explode: false + - name: order + in: query + description: Sort direction by sandbox start time. Defaults to desc (newest first). + required: false + schema: + $ref: "#/components/schemas/OrderDirection" + - name: startedAfter + in: query + description: Return sandboxes started at or after this timestamp. + required: false + schema: + type: string + format: date-time + - name: template + in: query + description: Filter sandboxes by a template ID or alias. + required: false + schema: + type: string - $ref: "#/components/parameters/paginationNextToken" - $ref: "#/components/parameters/paginationLimit" responses: "200": description: Successfully returned all running sandboxes + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" + X-Total-Running: + $ref: "#/components/headers/XTotalRunning" content: application/json: schema: @@ -2291,12 +3058,15 @@ paths: $ref: "#/components/responses/401" "400": $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /sandboxes/metrics: get: summary: List sandbox metrics + x-api-group: list description: List metrics for given sandboxes tags: [sandboxes] security: @@ -2305,6 +3075,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - name: sandbox_ids in: query @@ -2328,6 +3100,8 @@ paths: $ref: "#/components/responses/401" "400": $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2343,6 +3117,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" - in: query @@ -2371,6 +3147,8 @@ paths: $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2385,6 +3163,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" - in: query @@ -2430,6 +3210,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2444,6 +3226,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2457,6 +3241,8 @@ paths: $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2470,6 +3256,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2479,6 +3267,8 @@ paths: $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2493,6 +3283,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" - in: query @@ -2525,6 +3317,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2540,6 +3334,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: @@ -2557,8 +3353,12 @@ paths: $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" /sandboxes/{sandboxID}/resume: post: @@ -2572,10 +3372,12 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: - required: true + required: false content: application/json: schema: @@ -2591,10 +3393,18 @@ paths: $ref: "#/components/responses/409" "404": $ref: "#/components/responses/404" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" /sandboxes/{sandboxID}/fork: post: @@ -2614,6 +3424,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: @@ -2639,13 +3451,18 @@ paths: $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" /sandboxes/{sandboxID}/connect: post: summary: Connect sandbox - description: Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. + description: Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. Use POST /v2/sandboxes/{sandboxID}/connect instead. + deprecated: true tags: [sandboxes] security: - ApiKeyAuth: [] @@ -2653,6 +3470,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: @@ -2680,8 +3499,70 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" + + /v2/sandboxes/{sandboxID}/connect: + post: + summary: Connect sandbox (v2) + description: >- + Returns sandbox details. If the sandbox is paused, it will be resumed. + TTL is only extended. The request body is optional; an omitted timeout + defaults to 300 seconds. + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectSandboxV2" + responses: + "200": + description: The sandbox was already running + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" + "201": + description: The sandbox was resumed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" /sandboxes/{sandboxID}/timeout: post: @@ -2693,6 +3574,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] tags: [sandboxes] requestBody: content: @@ -2708,6 +3591,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2721,6 +3606,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] tags: [sandboxes] requestBody: required: true @@ -2739,6 +3626,8 @@ paths: $ref: "#/components/responses/404" "409": $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2752,6 +3641,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] tags: [sandboxes] requestBody: content: @@ -2767,6 +3658,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" /sandboxes/{sandboxID}/snapshots: post: @@ -2779,6 +3672,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: @@ -2800,12 +3695,15 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /snapshots: get: summary: List snapshots + x-api-group: list description: List all snapshots for the team tags: [snapshots] security: @@ -2814,6 +3712,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - name: sandboxID in: query @@ -2832,6 +3732,9 @@ paths: responses: "200": description: Successfully returned snapshots + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" content: application/json: schema: @@ -2840,6 +3743,8 @@ paths: $ref: "#/components/schemas/SnapshotInfo" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2854,6 +3759,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: @@ -2874,28 +3781,34 @@ paths: $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /v2/templates: get: summary: List templates (v2) + x-api-group: list description: List all templates tags: [templates] security: - ApiKeyAuth: [] - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - in: query required: false name: teamID schema: type: string - description: Identifier of the team + description: Identifier of the team, as its UUID or its public project ID (prj_) - $ref: "#/components/parameters/paginationNextToken" - $ref: "#/components/parameters/paginationLimit" responses: @@ -2903,9 +3816,7 @@ paths: description: Successfully returned all templates headers: X-Next-Token: - description: Cursor to fetch the next page of results, if more exist - schema: - type: string + $ref: "#/components/headers/XNextToken" content: application/json: schema: @@ -2918,37 +3829,8 @@ paths: $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" - "500": - $ref: "#/components/responses/500" - post: - summary: Create template (v2) - description: Create a new template - deprecated: true - tags: [templates] - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateBuildRequestV2" - - responses: - "202": - description: The build was requested successfully - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateLegacy" - "400": - $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -2958,12 +3840,13 @@ paths: description: Get an upload link for a tar file containing build layer files tags: [templates] security: - - AccessTokenAuth: [] - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - in: path @@ -2971,7 +3854,8 @@ paths: required: true schema: type: string - description: Hash of the files + pattern: "^[0-9a-f]{64}$" + description: Hash of the files (lowercase hex SHA-256) responses: "201": @@ -2986,29 +3870,33 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /templates: get: summary: List templates + x-api-group: list description: List all templates deprecated: true tags: [templates] security: - ApiKeyAuth: [] - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - in: query required: false name: teamID schema: type: string - description: Identifier of the team + description: Identifier of the team, as its UUID or its public project ID (prj_) responses: "200": description: Successfully returned all templates @@ -3020,41 +3908,15 @@ paths: $ref: "#/components/schemas/Template" "401": $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" - post: - summary: Create template - description: Create a new template - deprecated: true - tags: [templates] - security: - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateBuildRequest" - - responses: - "202": - description: The build was accepted - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateLegacy" - "400": - $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /templates/{templateID}: get: summary: List template builds + x-api-group: list description: List all builds for a template tags: [templates] security: @@ -3063,6 +3925,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/paginationNextToken" @@ -3070,41 +3934,17 @@ paths: responses: "200": description: Successfully returned the template with its builds + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" content: application/json: schema: $ref: "#/components/schemas/TemplateWithBuilds" "401": $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" - post: - summary: Rebuild template - description: Rebuild an template - deprecated: true - tags: [templates] - security: - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - parameters: - - $ref: "#/components/parameters/templateID" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateBuildRequest" - - responses: - "202": - description: The build was accepted - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateLegacy" - "401": - $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" delete: @@ -3113,11 +3953,12 @@ paths: tags: [templates] security: - ApiKeyAuth: [] - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" responses: @@ -3125,6 +3966,8 @@ paths: description: The template was deleted successfully "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" patch: @@ -3134,11 +3977,12 @@ paths: tags: [templates] security: - ApiKeyAuth: [] - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" requestBody: @@ -3154,27 +3998,8 @@ paths: $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" - - /templates/{templateID}/builds/{buildID}: - post: - summary: Start template build - description: Start the build - deprecated: true - tags: [templates] - security: - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - parameters: - - $ref: "#/components/parameters/templateID" - - $ref: "#/components/parameters/buildID" - responses: - "202": - description: The build has started - "401": - $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3189,6 +4014,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/buildID" @@ -3201,8 +4028,12 @@ paths: responses: "202": description: The build has started + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3213,11 +4044,12 @@ paths: tags: [templates] security: - ApiKeyAuth: [] - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" requestBody: @@ -3237,6 +4069,8 @@ paths: $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3246,12 +4080,13 @@ paths: description: Get template build info tags: [templates] security: - - AccessTokenAuth: [] - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/buildID" @@ -3287,6 +4122,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3296,12 +4133,13 @@ paths: description: Get template build logs tags: [templates] security: - - AccessTokenAuth: [] - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/buildID" @@ -3345,6 +4183,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3359,6 +4199,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: @@ -3378,6 +4220,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" delete: @@ -3390,6 +4234,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: @@ -3405,12 +4251,15 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /templates/{templateID}/tags: get: summary: List template tags + x-api-group: list description: List all tags for a template tags: [tags] security: @@ -3419,6 +4268,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" responses: @@ -3436,6 +4287,8 @@ paths: $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3450,6 +4303,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - name: alias in: path @@ -3470,6 +4325,8 @@ paths: $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3480,6 +4337,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - in: query name: clusterID @@ -3499,6 +4357,8 @@ paths: $ref: "#/components/schemas/Node" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3509,6 +4369,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - $ref: "#/components/parameters/nodeID" - in: query @@ -3529,6 +4390,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" post: @@ -3537,6 +4400,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - $ref: "#/components/parameters/nodeID" requestBody: @@ -3553,6 +4417,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3563,6 +4429,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - name: teamID in: path @@ -3582,6 +4449,32 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /admin/sandboxes/running-counts: + get: + summary: Count running sandboxes by team + description: | + Returns a shared snapshot normally refreshed after five seconds. A + sandbox transitioning out of running can remain counted until removal. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + responses: + "200": + description: Running sandbox counts keyed by team ID + content: + application/json: + schema: + $ref: "#/components/schemas/AdminTeamRunningSandboxCounts" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3592,6 +4485,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - name: teamID in: path @@ -3611,6 +4505,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3621,6 +4517,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - name: teamID in: path @@ -3650,6 +4547,8 @@ paths: $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3660,6 +4559,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - name: teamID in: path @@ -3678,66 +4578,24 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /access-tokens: - post: - summary: Create access token - description: Create a new access token. Deprecated; use an API key (E2B_API_KEY) instead. - deprecated: true - tags: [access-tokens] - security: - - AuthProviderBearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewAccessToken" - responses: - "201": - description: Access token created successfully - content: - application/json: - schema: - $ref: "#/components/schemas/CreatedAccessToken" - "401": - $ref: "#/components/responses/401" - "410": - $ref: "#/components/responses/410" - "500": - $ref: "#/components/responses/500" - - /access-tokens/{accessTokenID}: - delete: - summary: Delete access token - description: Delete an access token - tags: [access-tokens] - security: - - AuthProviderBearerAuth: [] - parameters: - - $ref: "#/components/parameters/accessTokenID" - responses: - "204": - description: Access token deleted successfully - "401": - $ref: "#/components/responses/401" - "404": - $ref: "#/components/responses/404" - "500": - $ref: "#/components/responses/500" - - /api-keys: - get: - summary: List team API keys - description: List all team API keys - tags: [api-keys] + /api-keys: + get: + summary: List team API keys + x-api-group: list + description: List all team API keys + tags: [api-keys] security: - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] responses: "200": description: Successfully returned all team API keys @@ -3749,6 +4607,8 @@ paths: $ref: "#/components/schemas/TeamAPIKey" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" post: @@ -3773,6 +4633,8 @@ paths: $ref: "#/components/schemas/CreatedTeamAPIKey" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3786,6 +4648,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/apiKeyID" requestBody: @@ -3801,6 +4665,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" delete: @@ -3812,6 +4678,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/apiKeyID" responses: @@ -3821,12 +4689,15 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" /volumes: get: summary: List team volumes + x-api-group: list description: List all team volumes tags: [volumes] security: @@ -3835,6 +4706,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] responses: "200": description: Successfully listed all team volumes @@ -3846,6 +4719,8 @@ paths: $ref: "#/components/schemas/Volume" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3859,6 +4734,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: @@ -3876,6 +4753,8 @@ paths: $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3890,6 +4769,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/volumeID" responses: @@ -3903,6 +4784,8 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" @@ -3916,6 +4799,8 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - $ref: "#/components/parameters/volumeID" responses: @@ -3925,5 +4810,834 @@ paths: $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /secrets: + get: + summary: List project secrets + x-api-group: list + description: List the project's secrets. No response carries a secret value. + tags: [secrets] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/paginationLimit" + responses: + "200": + description: Successfully listed the project's secrets + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Secret" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" + + post: + summary: Create a secret + description: Create a secret by storing a runtime marker as its first version. The response carries metadata only. + tags: [secrets] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewSecret" + responses: + "201": + description: Successfully created the secret + content: + application/json: + schema: + $ref: "#/components/schemas/Secret" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "409": + description: >- + Secret name conflict or project live-secret limit reached. A quota + response has error_code secret_limit_reached and, when reported by + the backend, the effective cap in message. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + secretLimitReached: + value: + code: 409 + message: Project has reached its limit of 50 live secrets + error_code: secret_limit_reached + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" + + /secrets/{secretID}: + get: + summary: Get a secret + description: Get one secret's metadata, selected by identifier or name. + tags: [secrets] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/secretID" + responses: + "200": + description: Successfully retrieved the secret + content: + application/json: + schema: + $ref: "#/components/schemas/Secret" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" + + post: + summary: Update a secret + description: Replace the secret's stored marker by appending a new version. The response carries metadata only. + tags: [secrets] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/secretID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SecretUpdate" + responses: + "200": + description: Successfully updated the secret + content: + application/json: + schema: + $ref: "#/components/schemas/Secret" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" + + delete: + summary: Delete a secret + description: Revoke the secret and schedule its versions for cleanup. + tags: [secrets] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/secretID" + responses: + "204": + description: Successfully deleted the secret + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" + + /clusters/{clusterID}/rigs: + get: + summary: List rigs of a cluster + description: > + List the orchestrator node pools ("rigs") of a cluster with a snapshot + of their scaling groups. Forwarded to the cluster's edge service; a + cluster with no rig management configured returns an empty list, and + the local cluster answers 501. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + responses: + "200": + description: Successfully returned the rigs of the cluster + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Rig" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /clusters/{clusterID}/rigs/{rigID}/capacity: + put: + summary: Set the capacity of a rig + description: > + Set the desired instance count on the rig's scaling group. The value is + passed to the cloud provider unchanged; violations of the group's bounds + or conflicting concurrent operations surface as errors. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + - $ref: "#/components/parameters/rigID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RigCapacityChange" + responses: + "202": + description: Capacity change accepted + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /clusters/{clusterID}/rigs/instances/{instanceID}: + delete: + summary: Terminate an instance of a rig + description: > + Terminate an instance in whichever rig's scaling group it belongs to. + The caller chooses whether the rig shrinks or the instance is replaced. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + - name: instanceID + in: path + required: true + schema: + type: string + description: Provider instance ID + - name: decrementDesired + in: query + required: true + schema: + type: boolean + description: > + When true, desired capacity is decremented (rig shrinks); + when false, the scaling group launches a replacement instance + responses: + "202": + description: Instance termination accepted + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /clusters/{clusterID}/rigs/{rigID}/instances: + get: + summary: List the instances attached to a rig + description: > + List the instances attached to the rig's scaling group with their + creation time and transition state, sorted by instance ID. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + - $ref: "#/components/parameters/rigID" + responses: + "200": + description: Successfully returned the instances of the rig + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/RigInstance" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /clusters/{clusterID}/rigs/{rigID}/errors: + get: + summary: List recent scaling errors of a rig + description: > + List recent scaling errors on the rig's scaling group (e.g. failed + instance creations due to resource exhaustion), newest first. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + - $ref: "#/components/parameters/rigID" + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + minimum: 1 + maximum: 50 + default: 20 + description: Maximum number of errors to return + responses: + "200": + description: Successfully returned the scaling errors of the rig + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/RigError" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" + + /events/sandboxes/{sandboxID}: + get: + description: Get sandbox events + tags: [events] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + minimum: 0 + default: 0 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 10 + - name: orderAsc + in: query + required: false + schema: + type: boolean + default: false + - name: types + in: query + required: false + style: form + explode: true + schema: + type: array + items: + type: string + description: Filter events to the provided event types + responses: + "200": + description: Successfully returned the sandbox events + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SandboxEvent" + "400": + $ref: "#/components/responses/400" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /events/sandboxes: + get: + description: Get all sandbox events for the team associated with the API key + tags: [events] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + minimum: 0 + default: 0 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 10 + - name: orderAsc + in: query + required: false + schema: + type: boolean + default: false + - name: types + in: query + required: false + style: form + explode: true + schema: + type: array + items: + type: string + description: Filter events to the provided event types + responses: + "200": + description: Successfully returned the sandbox events + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SandboxEvent" + "400": + $ref: "#/components/responses/400" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /events/webhooks: + post: + description: Register events webhook. + tags: [webhooks] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookCreate" + responses: + "201": + description: Successfully created webhook. + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookCreation" + "400": + $ref: "#/components/responses/400" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + get: + description: List registered webhooks. + tags: [webhooks] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + responses: + "200": + description: List of registered webhooks. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/WebhookDetail" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /events/webhooks/{webhookID}: + get: + description: Get a registered webhook. + tags: [webhooks] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" + responses: + "200": + description: Successfully returned the webhook configuration. + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookDetail" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + patch: + description: Update a registered webhook configuration. + tags: [webhooks] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookConfiguration" + responses: + "200": + description: Successfully updated webhook. + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookDetail" + "400": + $ref: "#/components/responses/400" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + delete: + description: Delete a registered webhook. + tags: [webhooks] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" + responses: + "200": + description: Successfully deleted webhook. + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /events/webhooks/{webhookID}/deliveries: + get: + description: List webhook delivery attempts. + tags: [webhooks] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" + - name: cursor + in: query + required: false + schema: + type: string + description: Opaque cursor from the previous response's nextCursor field. + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 25 + - name: orderAsc + in: query + required: false + schema: + type: boolean + default: false + - name: start + in: query + required: false + schema: + type: string + format: date-time + description: Include deliveries at or after this timestamp. + - name: end + in: query + required: false + schema: + type: string + format: date-time + description: Include deliveries before this timestamp. + - name: deliveryStatus + in: query + required: false + style: form + explode: false + schema: + type: array + items: + type: string + enum: [success, failed] + description: Filter deliveries by delivery status + - name: eventType + in: query + required: false + style: form + explode: false + schema: + type: array + items: + type: string + description: Filter deliveries by event type + responses: + "200": + description: List of webhook delivery attempts grouped by event. + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookDeliveriesListPayload" + "400": + $ref: "#/components/responses/400" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /events/webhooks/{webhookID}/stats: + get: + description: Get webhook delivery aggregate stats. + tags: [webhooks] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" + - name: start + in: query + required: false + schema: + type: string + format: date-time + description: Inclusive stats range start. Defaults to 24 hours ago. + - name: end + in: query + required: false + schema: + type: string + format: date-time + description: Exclusive stats range end. Defaults to now. + responses: + "200": + description: Webhook delivery stats. + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookDeliveryStats" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" diff --git a/src/core/shared/contracts/dashboard-api.types.ts b/src/core/shared/contracts/dashboard-api.types.ts index abe271801..f91ab5c5b 100644 --- a/src/core/shared/contracts/dashboard-api.types.ts +++ b/src/core/shared/contracts/dashboard-api.types.ts @@ -218,41 +218,57 @@ export interface paths { patch?: never trace?: never } - '/teams': { + '/admin/teams/bootstrap': { parameters: { query?: never header?: never path?: never cookie?: never } + get?: never + put?: never /** - * List user teams - * @description Returns all teams the authenticated user belongs to, with limits and default flag. + * Bootstrap team (retired) + * @deprecated + * @description Retired: the operation never creates a team and always fails with 500. The route and its request schema remain registered only until the Stripe projects coordinator migrates to the workspace API. Create teams through the workspace API instead. */ - get: { + post: { parameters: { query?: never header?: never path?: never cookie?: never } - requestBody?: never - responses: { - /** @description Successfully returned user teams. */ - 200: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['UserTeamsResponse'] - } + requestBody: { + content: { + 'application/json': components['schemas']['AdminTeamBootstrapRequest'] } + } + responses: { + 400: components['responses']['400'] 401: components['responses']['401'] 500: components['responses']['500'] } } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/clusters': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never put?: never - /** Create team */ + /** + * Create a cluster + * @description Creates a cluster whose configuration cannot be modified. + */ post: { parameters: { query?: never @@ -262,21 +278,22 @@ export interface paths { } requestBody: { content: { - 'application/json': components['schemas']['CreateTeamRequest'] + 'application/json': components['schemas']['AdminClusterCreateRequest'] } } responses: { - /** @description Successfully created team. */ - 200: { + /** @description Cluster created. */ + 201: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['TeamResolveResponse'] + 'application/json': components['schemas']['AdminClusterCreateResponse'] } } 400: components['responses']['400'] 401: components['responses']['401'] + 409: components['responses']['409'] 500: components['responses']['500'] } } @@ -286,7 +303,7 @@ export interface paths { patch?: never trace?: never } - '/admin/users/bootstrap': { + '/admin/clusters/{clusterID}': { parameters: { query?: never header?: never @@ -295,41 +312,108 @@ export interface paths { } get?: never put?: never - /** Bootstrap auth provider user */ - post: { + post?: never + /** + * Delete an unreferenced cluster + * @description Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. + */ + delete: { parameters: { query?: never header?: never - path?: never + path: { + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } cookie?: never } - requestBody: { - content: { - 'application/json': components['schemas']['AdminAuthProviderUserBootstrapRequest'] + requestBody?: never + responses: { + /** @description Cluster deleted or already absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/v1/management/clusters/{clusterID}/destroy-readiness': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * Check cluster destroy readiness + * @description Checks whether this exact cluster has active templates or snapshots. Soft-deleted history and team assignments do not block this check. Returns success if the cluster is absent. This read does not change resources or prevent later template creation. + */ + get: operations['managementClusterDestroyReadiness'] + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/teams/{teamID}/cluster': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * Get a team's assigned cluster + * @description Returns the current cluster assignment without exposing cluster credentials. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] } + cookie?: never } + requestBody?: never responses: { - /** @description Successfully bootstrapped user. */ + /** @description Cluster assignment returned. */ 200: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['TeamResolveResponse'] + 'application/json': components['schemas']['AdminTeamClusterAssignmentResponse'] } } 400: components['responses']['400'] 401: components['responses']['401'] + 404: components['responses']['404'] 500: components['responses']['500'] } } + put?: never + post?: never delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/teams/bootstrap': { + '/admin/teams/{teamID}/ban': { parameters: { query?: never header?: never @@ -337,46 +421,69 @@ export interface paths { cookie?: never } get?: never - put?: never /** - * Bootstrap team - * @description Creates and provisions a team for an admin-authenticated bootstrap workflow. + * Ban a team + * @description Marks the team as banned so its API keys stop authenticating. Idempotent; running workloads are not touched. */ - post: { + put: { parameters: { query?: never header?: never - path?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } cookie?: never } - requestBody: { - content: { - 'application/json': components['schemas']['AdminTeamBootstrapRequest'] + requestBody?: never + responses: { + /** @description Team banned. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + post?: never + /** + * Unban a team + * @description Clears the team's ban. Idempotent. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] } + cookie?: never } + requestBody?: never responses: { - /** @description Successfully bootstrapped team. */ - 200: { + /** @description Team unbanned. */ + 204: { headers: { [name: string]: unknown } - content: { - 'application/json': components['schemas']['TeamResolveResponse'] - } + content?: never } - 400: components['responses']['400'] 401: components['responses']['401'] + 404: components['responses']['404'] 500: components['responses']['500'] - 502: components['responses']['502'] } } - delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/user-profiles/resolve': { + '/admin/teams/{teamID}/block': { parameters: { query?: never header?: never @@ -384,42 +491,74 @@ export interface paths { cookie?: never } get?: never - put?: never - /** Resolve user profiles */ - post: { + /** + * Block a team + * @description Marks the team as blocked with the given reason, so it can no longer start sandboxes or builds. Idempotent; a repeated call replaces the reason. + */ + put: { parameters: { query?: never header?: never - path?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } cookie?: never } requestBody: { content: { - 'application/json': components['schemas']['AdminAuthProviderProfilesResolveRequest'] + 'application/json': components['schemas']['AdminTeamBlockRequest'] } } responses: { - /** @description Successfully resolved profiles. */ - 200: { + /** @description Team blocked. */ + 204: { headers: { [name: string]: unknown } - content: { - 'application/json': components['schemas']['AdminAuthProviderProfilesResponse'] - } + content?: never } 400: components['responses']['400'] 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + post?: never + /** + * Unblock a team + * @description Clears the team's block and its recorded reason. Idempotent. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Team unblocked. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] 500: components['responses']['500'] } } - delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/user-profiles/by-email': { + '/admin/user-profiles/resolve': { parameters: { query?: never header?: never @@ -428,7 +567,7 @@ export interface paths { } get?: never put?: never - /** Lookup user profiles by email */ + /** Resolve user profiles */ post: { parameters: { query?: never @@ -438,11 +577,11 @@ export interface paths { } requestBody: { content: { - 'application/json': components['schemas']['AdminAuthProviderProfilesLookupEmailRequest'] + 'application/json': components['schemas']['AdminAuthProviderProfilesResolveRequest'] } } responses: { - /** @description Successfully found matching profiles. */ + /** @description Successfully resolved profiles. */ 200: { headers: { [name: string]: unknown @@ -462,27 +601,30 @@ export interface paths { patch?: never trace?: never } - '/admin/user-profiles/{userId}': { + '/admin/user-profiles/by-email': { parameters: { query?: never header?: never path?: never cookie?: never } - /** User profile */ - get: { + get?: never + put?: never + /** Lookup user profiles by email */ + post: { parameters: { query?: never header?: never - path: { - /** @description Identifier of the user. */ - userId: components['parameters']['userId'] - } + path?: never cookie?: never } - requestBody?: never + requestBody: { + content: { + 'application/json': components['schemas']['AdminAuthProviderProfilesLookupEmailRequest'] + } + } responses: { - /** @description Successfully found profile. */ + /** @description Successfully found matching profiles. */ 200: { headers: { [name: string]: unknown @@ -496,29 +638,21 @@ export interface paths { 500: components['responses']['500'] } } - put?: never - post?: never delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/users/{userId}': { + '/admin/user-profiles/{userId}': { parameters: { query?: never header?: never path?: never cookie?: never } - get?: never - put?: never - post?: never - /** - * Delete user - * @description Deletes a user by removing the identity provider record, user_identities mapping, and public.users row. - */ - delete: { + /** User profile */ + get: { parameters: { query?: never header?: never @@ -530,20 +664,23 @@ export interface paths { } requestBody?: never responses: { - /** @description Successfully deleted user. */ - 204: { + /** @description Successfully found profile. */ + 200: { headers: { [name: string]: unknown } - content?: never + content: { + 'application/json': components['schemas']['AdminAuthProviderProfilesResponse'] + } } 400: components['responses']['400'] 401: components['responses']['401'] - 404: components['responses']['404'] - 409: components['responses']['409'] 500: components['responses']['500'] } } + put?: never + post?: never + delete?: never options?: never head?: never patch?: never @@ -595,21 +732,18 @@ export interface paths { patch?: never trace?: never } - '/teams/{teamID}': { + '/teams/{teamID}/status': { parameters: { query?: never header?: never path?: never cookie?: never } - get?: never - put?: never - post?: never - delete?: never - options?: never - head?: never - /** Update team */ - patch: { + /** + * Get team access status + * @description Returns whether the team is blocked or banned and its recorded blocked reason. Team-authenticated requests may read only the team they are scoped to. + */ + get: { parameters: { query?: never header?: never @@ -619,37 +753,41 @@ export interface paths { } cookie?: never } - requestBody: { - content: { - 'application/json': components['schemas']['UpdateTeamRequest'] - } - } + requestBody?: never responses: { - /** @description Successfully updated team. */ + /** @description Successfully returned team access status. */ 200: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['UpdateTeamResponse'] + 'application/json': components['schemas']['TeamStatusResponse'] } } - 400: components['responses']['400'] 401: components['responses']['401'] - 403: components['responses']['403'] + 404: components['responses']['404'] 500: components['responses']['500'] } } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never trace?: never } - '/teams/{teamID}/members': { + '/teams/{teamID}/limits': { parameters: { query?: never header?: never path?: never cookie?: never } - /** List team members */ + /** + * Get team limits + * @description Returns the team's tier and effective resource limits. Team-authenticated requests may read only the team they are scoped to. + */ get: { parameters: { query?: never @@ -662,96 +800,65 @@ export interface paths { } requestBody?: never responses: { - /** @description Successfully returned team members. */ + /** @description Successfully returned team limits. */ 200: { headers: { [name: string]: unknown } - content: { - 'application/json': components['schemas']['TeamMembersResponse'] - } - } - 401: components['responses']['401'] - 403: components['responses']['403'] - 500: components['responses']['500'] - } - } - put?: never - /** Add team member */ - post: { - parameters: { - query?: never - header?: never - path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] - } - cookie?: never - } - requestBody: { - content: { - 'application/json': components['schemas']['AddTeamMemberRequest'] - } - } - responses: { - /** @description Successfully added team member. */ - 201: { - headers: { - [name: string]: unknown + content: { + 'application/json': components['schemas']['TeamLimitsResponse'] } - content?: never } - 400: components['responses']['400'] 401: components['responses']['401'] - 403: components['responses']['403'] 404: components['responses']['404'] 500: components['responses']['500'] } } + put?: never + post?: never delete?: never options?: never head?: never patch?: never trace?: never } - '/teams/{teamID}/members/{userId}': { + '/teams/{teamID}/members': { parameters: { query?: never header?: never path?: never cookie?: never } - get?: never - put?: never - post?: never - /** Remove team member */ - delete: { + /** List team members */ + get: { 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 Successfully removed team member. */ - 204: { + /** @description Successfully returned team members. */ + 200: { headers: { [name: string]: unknown } - content?: never + content: { + 'application/json': components['schemas']['TeamMembersResponse'] + } } - 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] 500: components['responses']['500'] } } + put?: never + post?: never + delete?: never options?: never head?: never patch?: never @@ -1106,63 +1213,68 @@ export interface paths { patch?: never trace?: never } - '/admin/v1/projects/{teamID}': { + '/v1/management/projects/{projectID}': { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } get?: never - /** Create or reconcile a project. */ - put: operations['upsertProject'] + /** Create or reconcile a project (v1). */ + put: operations['managementUpsertProject'] post?: never - /** Delete a project and its control-plane state. */ - delete: operations['deleteProject'] + /** + * Delete a project and its control-plane state (v1). + * @description Declared, and answered with 501 by every control plane. Deleting a project means reclaiming templates, snapshots, volumes, running sandboxes and their stored artifacts, and no single service can reach all of them today. Callers should not depend on this operation until that changes. + */ + delete: operations['managementDeleteProject'] options?: never head?: never patch?: never trace?: never } - '/admin/v1/projects/{teamID}/members/{userId}': { + '/v1/management/projects/{projectID}/members/{userID}': { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + userID: components['parameters']['userID'] } cookie?: never } get?: never - /** Reconcile an opaque user UUID as a project member. */ - put: operations['upsertProjectMember'] + /** + * Apply one versioned project member projection (v1). + * @description Applies the newest desired presence for one project member. An older or duplicate revision is accepted without changing target state. + */ + put: operations['managementApplyProjectMember'] post?: never - /** Remove a project member. */ - delete: operations['deleteProjectMember'] + delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/v1/projects/{teamID}/limits': { + '/v1/management/projects/{projectID}/limits': { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } get?: never - /** Reconcile effective limits for a project. */ - put: operations['upsertProjectLimits'] + /** Reconcile effective limits for a project (v1). */ + put: operations['managementUpsertProjectLimits'] post?: never delete?: never options?: never @@ -1170,21 +1282,74 @@ export interface paths { patch?: never trace?: never } - '/admin/v1/users/{userId}': { + '/v1/management/projects/{projectID}/block': { parameters: { query?: never header?: never path: { - /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } get?: never - put?: never + /** + * Apply a project's block state (v1). + * @description Records whether the project is refused billable work, which the auth path reads before it lets a sandbox start. An older or duplicate revision is accepted without changing target state. + */ + put: operations['managementApplyProjectBlock'] + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/v1/management/clusters/{clusterID}': { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + get?: never + /** Register a cluster (v1). */ + put: operations['managementRegisterCluster'] + post?: never + /** + * Delete an unreferenced cluster (v1). + * @description Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. + */ + delete: operations['managementDeleteCluster'] + options?: never + head?: never + patch?: never + trace?: never + } + '/v1/management/projects/{projectID}/cluster/{clusterID}': { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + get?: never + /** + * Assign a cluster to a project (v1). + * @description Assigns the cluster only when the project's tier identifier contains `enterprise`, case-insensitively. Replaying the identical assignment succeeds even if the project's tier later changes. + */ + put: operations['managementAssignProjectCluster'] post?: never - /** Purge shard-local membership and access-token state for an opaque user UUID. */ - delete: operations['purgeUser'] + /** Detach a cluster assignment from a project (v1). */ + delete: operations['managementDetachProjectCluster'] options?: never head?: never patch?: never @@ -1200,9 +1365,22 @@ export interface components { * @description Error code. */ code: number + error_code?: components['schemas']['ErrorCode'] /** @description Error message. */ message: string } + /** + * @description Stable machine-readable reason for an API error. New codes may be added as other errors adopt this contract. Clients must handle absent or unknown codes without interpreting the human-readable message. + * @enum {string} + */ + ErrorCode: + | 'cluster_registration_invalid' + | 'cluster_registration_conflict' + | 'cluster_assignment_invalid' + | 'cluster_assignment_project_not_found' + | 'cluster_assignment_cluster_not_found' + | 'cluster_assignment_requires_enterprise' + | 'cluster_assignment_already_assigned' AdminAuthProviderProfile: { /** * Format: uuid @@ -1222,15 +1400,6 @@ export interface components { /** Format: email */ email: string } - AdminAuthProviderUserBootstrapRequest: { - oidc_issuer: string - oidc_user_id: string - /** Format: email */ - oidc_user_email: string - oidc_user_name?: string | null - signup_ip?: string - signup_user_agent?: string - } AdminTeamBootstrapRequest: { /** @description Team name. */ name: string @@ -1240,6 +1409,31 @@ export interface components { */ email: string } + AdminClusterCreateRequest: { + /** + * Format: uuid + * @description Optional stable identifier for idempotent creation. Reuse succeeds only when the immutable configuration is identical. + */ + cluster_id?: string + name: string + endpoint: string + endpoint_tls: boolean + token: string + sandbox_proxy_domain?: string | null + auth_org_id?: string | null + } + AdminClusterCreateResponse: { + /** Format: uuid */ + cluster_id: string + } + AdminTeamBlockRequest: { + /** @description Shown to the team as the reason it is blocked. */ + reason: string + } + AdminTeamClusterAssignmentResponse: { + /** Format: uuid */ + cluster_id: string + } /** * @description Build status mapped for dashboard clients. * @enum {string} @@ -1376,37 +1570,28 @@ export interface components { UserTeamLimits: { /** Format: int64 */ maxLengthHours: number - /** Format: int32 */ + /** Format: int64 */ concurrentSandboxes: number - /** Format: int32 */ + /** Format: int64 */ concurrentTemplateBuilds: number - /** Format: int32 */ + /** Format: int64 */ maxVcpu: number - /** Format: int32 */ + /** Format: int64 */ maxRamMb: number - /** Format: int32 */ + /** Format: int64 */ diskMb: number - /** Format: int32 */ + /** Format: int64 */ eventsTtlDays: number } - UserTeam: { - /** Format: uuid */ - id: string - name: string - slug: string + TeamLimitsResponse: { + /** @description The team's raw tier identifier, exactly as stored (not normalized to a catalog plan). */ tier: string - email: string - profilePictureUrl: string | null + limits: components['schemas']['UserTeamLimits'] + } + TeamStatusResponse: { isBlocked: boolean isBanned: boolean blockedReason: string | null - isDefault: boolean - limits: components['schemas']['UserTeamLimits'] - /** Format: date-time */ - createdAt: string - } - UserTeamsResponse: { - teams: components['schemas']['UserTeam'][] } TeamMember: { /** Format: uuid */ @@ -1425,23 +1610,6 @@ export interface components { TeamMembersResponse: { members: components['schemas']['TeamMember'][] } - UpdateTeamRequest: { - name?: string - profilePictureUrl?: string | null - } - UpdateTeamResponse: { - /** Format: uuid */ - id: string - name: string - profilePictureUrl?: string | null - } - AddTeamMemberRequest: { - /** Format: email */ - email: string - } - CreateTeamRequest: { - name: string - } DefaultTemplateAlias: { alias: string namespace?: string | null @@ -1619,37 +1787,117 @@ export interface components { id: string slug: string } - /** @enum {string} */ - AdminControlPlaneProjectType: 'development' | 'staging' | 'production' - AdminControlPlaneProjectUpsertRequest: { + /** + * @description The properties of a project this side stores. Every one is synchronized by the caller and sent on every push, so a reconcile is a complete statement of the project rather than a patch. + * + * A project's tier is not among them. It is assigned once, at creation, from this side's own default, and no push moves it — limits arrive separately and in full through upsertProjectLimits, which takes precedence over the tier anyway. + */ + ManagementProjectUpsertRequest: { name: string + /** @description Changing it renames the project, and nothing follows it. Template names embed the slug they were built under, so a renamed project keeps its existing template names and only new ones carry the new slug. A slug already held on this control plane is a 409, on a rename as much as on a create. */ slug: string - project_type: components['schemas']['AdminControlPlaneProjectType'] + /** @description Contact address recorded on the project. */ + email: string } - AdminControlPlaneProject: components['schemas']['AdminControlPlaneProjectUpsertRequest'] & { + ManagementProject: components['schemas']['ManagementProjectUpsertRequest'] & { /** Format: uuid */ id: string } - AdminControlPlaneMemberUpsertRequest: { - /** Format: uuid */ - added_by?: string + ManagementClusterRegistrationRequest: { + name: string + endpoint: string + endpoint_tls: boolean + token: string + sandbox_proxy_domain?: string | null + auth_org_id?: string | null } - AdminControlPlaneProjectLimits: { - /** Format: int32 */ - concurrent_sandboxes: number - /** Format: int32 */ - max_sandbox_length_hours: number - /** Format: int32 */ - max_vcpu: number - /** Format: int64 */ - max_ram_mb: number + ManagementProjectMemberIdentity: { + issuer: string + subject: string + } + ManagementProjectMemberApplyRequest: { /** Format: int64 */ - disk_mb: number - /** Format: int32 */ - concurrent_template_builds: number - /** Format: int32 */ - events_ttl_days: number + revision: number + present: boolean + /** + * @description Whether this membership is the user's default team. Omitted requests from older sources remain non-default. + * @default false + */ + is_default: boolean + identities?: components['schemas']['ManagementProjectMemberIdentity'][] + } + /** @description Desired project block state. A newer revision replaces the state, including any intervening manual block or unblock. */ + ManagementProjectBlockRequest: { + /** + * Format: int64 + * @description Monotonic per-project revision. Older or duplicate revisions succeed without changing stored state. + */ + revision: number + /** @description Whether the project is refused billable work. */ + blocked: boolean + /** @description Reason included in blocked-request errors. Cleared when blocked is false. */ + reason?: string + decided_at?: components['schemas']['ManagementDecidedAt'] } + /** + * Format: date-time + * @description When the caller decided this revision, in RFC 3339. Stored with the revision and used to measure how long the decision took to apply here. Omit it when the caller does not know; the delivery is then counted as origin unknown rather than as zero lag. + */ + ManagementDecidedAt: string + /** + * @description A project's effective limits, already resolved by the caller. Every field is absolute: this side stores what it is given and performs no arithmetic of its own. + * + * The minimums below track the CHECK constraints on tiers, which is the contract for what a limit may be. project_limits stores the same values under looser constraints on purpose — it is a push target, and a floor that only rejects the impossible keeps a future decision about what is allowed a change to this schema rather than a migration. + * + * `max_disk_size_mb` and `max_free_disk_size_mb` are one ceiling under two names, and either name alone carries it. A caller that sends both must send them equal; a caller that sends neither is refused. Sending both is what a caller does while receivers older than the second name are still running. + */ + ManagementProjectLimits: + | { + /** + * Format: int64 + * @description The caller's version of this answer, raised whenever the limits it resolved for the project change. Delivery is over a network, so two pushes can be in flight at once and arrive in either order: this side stores the revision it accepted and drops a delivery at or below it, which is what keeps a delayed retry from putting the project back on limits it has already left. + * + * Comparable only against earlier revisions for the same project. + */ + revision: number + /** 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 + /** + * Format: int64 + * @description The default free-space growth target when a template build request omits one. May sit anywhere at or below the maximum free-space growth target, and usually sits well below it; a delivery whose default exceeds the maximum is rejected. + */ + default_free_disk_size_mb: number + /** + * Format: int64 + * @description The most a template build may request as its free-space growth target. + */ + max_free_disk_size_mb?: number + /** + * Format: int64 + * @description The same ceiling as max_free_disk_size_mb, under the name it was first published with. Kept until every deployed sender and receiver speaks the other name. + */ + max_disk_size_mb?: number + /** + * Format: int64 + * @description Requests per second a project may make to each list endpoint. Zero disables the limit. + */ + api_team_rps_list: number + decided_at?: components['schemas']['ManagementDecidedAt'] + } + | unknown + | unknown } responses: { /** @description Bad request */ @@ -1715,15 +1963,6 @@ export interface components { 'application/json': components['schemas']['Error'] } } - /** @description Upstream error */ - 502: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['Error'] - } - } } parameters: { /** @description Identifier of the build. */ @@ -1742,6 +1981,12 @@ export interface components { build_ids: string[] /** @description Identifier of the team. */ teamID: string + /** @description Identifier of the cluster. */ + clusterID: string + /** @description Identifier of the project. */ + projectID: string + /** @description Identifier of the user. */ + userID: string /** @description Identifier of the user. */ userId: string /** @description Team slug to resolve. */ @@ -1787,19 +2032,52 @@ export interface components { } export type $defs = Record export interface operations { - upsertProject: { + managementClusterDestroyReadiness: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description No active templates or snapshots reference the cluster. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + /** @description Active templates or snapshots must be deleted before destroying the cluster. */ + 409: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + 500: components['responses']['500'] + } + } + managementUpsertProject: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } requestBody: { content: { - 'application/json': components['schemas']['AdminControlPlaneProjectUpsertRequest'] + 'application/json': components['schemas']['ManagementProjectUpsertRequest'] } } responses: { @@ -1809,7 +2087,7 @@ export interface operations { [name: string]: unknown } content: { - 'application/json': components['schemas']['AdminControlPlaneProject'] + 'application/json': components['schemas']['ManagementProject'] } } /** @description Project created. */ @@ -1818,7 +2096,7 @@ export interface operations { [name: string]: unknown } content: { - 'application/json': components['schemas']['AdminControlPlaneProject'] + 'application/json': components['schemas']['ManagementProject'] } } 400: components['responses']['400'] @@ -1828,13 +2106,13 @@ export interface operations { 501: components['responses']['501'] } } - deleteProject: { + managementDeleteProject: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } @@ -1853,25 +2131,25 @@ export interface operations { 501: components['responses']['501'] } } - upsertProjectMember: { + managementApplyProjectMember: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + userID: components['parameters']['userID'] } cookie?: never } - requestBody?: { + requestBody: { content: { - 'application/json': components['schemas']['AdminControlPlaneMemberUpsertRequest'] + 'application/json': components['schemas']['ManagementProjectMemberApplyRequest'] } } responses: { - /** @description Membership is present. */ + /** @description Membership projection is applied or already superseded. */ 204: { headers: { [name: string]: unknown @@ -1881,25 +2159,27 @@ export interface operations { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 409: components['responses']['409'] 500: components['responses']['500'] - 501: components['responses']['501'] } } - deleteProjectMember: { + managementUpsertProjectLimits: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] - /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } - requestBody?: never + requestBody: { + content: { + 'application/json': components['schemas']['ManagementProjectLimits'] + } + } responses: { - /** @description Membership is absent. */ + /** @description Effective limits are synchronized. */ 204: { headers: { [name: string]: unknown @@ -1913,23 +2193,23 @@ export interface operations { 501: components['responses']['501'] } } - upsertProjectLimits: { + managementApplyProjectBlock: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } requestBody: { content: { - 'application/json': components['schemas']['AdminControlPlaneProjectLimits'] + 'application/json': components['schemas']['ManagementProjectBlockRequest'] } } responses: { - /** @description Effective limits are synchronized. */ + /** @description Block state is applied or already superseded. */ 204: { headers: { [name: string]: unknown @@ -1943,19 +2223,74 @@ export interface operations { 501: components['responses']['501'] } } - purgeUser: { + managementRegisterCluster: { parameters: { query?: never header?: never path: { - /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['ManagementClusterRegistrationRequest'] + } + } + responses: { + /** @description Cluster registration is present. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + managementDeleteCluster: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Cluster is absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + managementAssignProjectCluster: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] } cookie?: never } requestBody?: never responses: { - /** @description User-owned shard state is absent. */ + /** @description Cluster is assigned to the project. */ 204: { headers: { [name: string]: unknown @@ -1964,8 +2299,36 @@ export interface operations { } 400: components['responses']['400'] 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + managementDetachProjectCluster: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description The matching assignment is absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] 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 4e306d805..d8f1a0e2f 100644 --- a/src/core/shared/contracts/infra-api.types.ts +++ b/src/core/shared/contracts/infra-api.types.ts @@ -32,6 +32,7 @@ export interface paths { content?: never } 401: components['responses']['401'] + 429: components['responses']['429'] } } put?: never @@ -72,6 +73,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -103,6 +105,7 @@ export interface paths { } header?: never path: { + /** @description Identifier of the team, as its UUID or its public project ID (prj_) */ teamID: components['parameters']['teamID'] } cookie?: never @@ -121,6 +124,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -154,6 +158,7 @@ export interface paths { } header?: never path: { + /** @description Identifier of the team, as its UUID or its public project ID (prj_) */ teamID: components['parameters']['teamID'] } cookie?: never @@ -172,6 +177,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -218,13 +224,15 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never /** * Create sandbox - * @description Create a sandbox from the template + * @deprecated + * @description Create a sandbox from the template. Use POST /v2/sandboxes instead. */ post: { parameters: { @@ -250,7 +258,10 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] } } delete?: never @@ -277,6 +288,12 @@ export interface paths { metadata?: string /** @description Filter sandboxes by one or more states */ state?: components['schemas']['SandboxState'][] + /** @description Sort direction by sandbox start time. Defaults to desc (newest first). */ + order?: components['schemas']['OrderDirection'] + /** @description Return sandboxes started at or after this timestamp. */ + startedAfter?: string + /** @description Filter sandboxes by a template ID or alias. */ + template?: string /** @description Cursor to start the list from */ nextToken?: components['parameters']['paginationNextToken'] /** @description Maximum number of items to return per page */ @@ -291,6 +308,8 @@ export interface paths { /** @description Successfully returned all running sandboxes */ 200: { headers: { + 'X-Next-Token': components['headers']['XNextToken'] + 'X-Total-Running': components['headers']['XTotalRunning'] [name: string]: unknown } content: { @@ -299,11 +318,45 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - post?: never + /** + * Create sandbox (v2) + * @description Create a sandbox from the template. All system communication with the sandbox is secured. + */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['NewSandboxV2'] + } + } + responses: { + /** @description The sandbox was created successfully */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Sandbox'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] + } + } delete?: never options?: never head?: never @@ -344,6 +397,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -394,6 +448,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -449,6 +504,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -493,6 +549,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -522,6 +579,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -568,6 +626,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -617,7 +676,9 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] } } delete?: never @@ -649,7 +710,7 @@ export interface paths { } cookie?: never } - requestBody: { + requestBody?: { content: { 'application/json': components['schemas']['ResumedSandbox'] } @@ -664,10 +725,14 @@ export interface paths { 'application/json': components['schemas']['Sandbox'] } } + 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] } } delete?: never @@ -716,7 +781,9 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] } } delete?: never @@ -736,7 +803,8 @@ export interface paths { put?: never /** * Connect sandbox - * @description Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. + * @deprecated + * @description Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. Use POST /v2/sandboxes/{sandboxID}/connect instead. */ post: { parameters: { @@ -774,7 +842,73 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/v2/sandboxes/{sandboxID}/connect': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** + * Connect sandbox (v2) + * @description Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. The request body is optional; an omitted timeout defaults to 300 seconds. + */ + post: { + parameters: { + query?: never + header?: never + path: { + sandboxID: components['parameters']['sandboxID'] + } + cookie?: never + } + requestBody?: { + content: { + 'application/json': components['schemas']['ConnectSandboxV2'] + } + } + responses: { + /** @description The sandbox was already running */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Sandbox'] + } + } + /** @description The sandbox was resumed successfully */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Sandbox'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] } } delete?: never @@ -820,6 +954,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -866,6 +1001,7 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -913,6 +1049,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] } } delete?: never @@ -961,6 +1098,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1001,6 +1139,7 @@ export interface paths { /** @description Successfully returned snapshots */ 200: { headers: { + 'X-Next-Token': components['headers']['XNextToken'] [name: string]: unknown } content: { @@ -1008,6 +1147,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1057,6 +1197,8 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1095,8 +1237,7 @@ export interface paths { /** @description Successfully returned all templates */ 200: { headers: { - /** @description Cursor to fetch the next page of results, if more exist */ - 'X-Next-Token'?: string + 'X-Next-Token': components['headers']['XNextToken'] [name: string]: unknown } content: { @@ -1106,42 +1247,12 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - /** - * Create template (v2) - * @deprecated - * @description Create a new template - */ - post: { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - requestBody: { - content: { - 'application/json': components['schemas']['TemplateBuildRequestV2'] - } - } - responses: { - /** @description The build was requested successfully */ - 202: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['TemplateLegacy'] - } - } - 400: components['responses']['400'] - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } + post?: never delete?: never options?: never head?: never @@ -1183,6 +1294,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1227,42 +1339,12 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - /** - * Create template - * @deprecated - * @description Create a new template - */ - post: { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - requestBody: { - content: { - 'application/json': components['schemas']['TemplateBuildRequest'] - } - } - responses: { - /** @description The build was accepted */ - 202: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['TemplateLegacy'] - } - } - 400: components['responses']['400'] - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } + post?: never delete?: never options?: never head?: never @@ -1299,6 +1381,7 @@ export interface paths { /** @description Successfully returned the template with its builds */ 200: { headers: { + 'X-Next-Token': components['headers']['XNextToken'] [name: string]: unknown } content: { @@ -1306,43 +1389,12 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - /** - * Rebuild template - * @deprecated - * @description Rebuild an template - */ - post: { - parameters: { - query?: never - header?: never - path: { - templateID: components['parameters']['templateID'] - } - cookie?: never - } - requestBody: { - content: { - 'application/json': components['schemas']['TemplateBuildRequest'] - } - } - responses: { - /** @description The build was accepted */ - 202: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['TemplateLegacy'] - } - } - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } + post?: never /** * Delete template * @description Delete a template @@ -1366,6 +1418,7 @@ export interface paths { content?: never } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1400,54 +1453,12 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } trace?: never } - '/templates/{templateID}/builds/{buildID}': { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - get?: never - put?: never - /** - * Start template build - * @deprecated - * @description Start the build - */ - post: { - parameters: { - query?: never - header?: never - path: { - templateID: components['parameters']['templateID'] - buildID: components['parameters']['buildID'] - } - cookie?: never - } - requestBody?: never - responses: { - /** @description The build has started */ - 202: { - headers: { - [name: string]: unknown - } - content?: never - } - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } - delete?: never - options?: never - head?: never - patch?: never - trace?: never - } '/v2/templates/{templateID}/builds/{buildID}': { parameters: { query?: never @@ -1484,7 +1495,9 @@ export interface paths { } content?: never } + 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1537,6 +1550,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1582,6 +1596,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1636,6 +1651,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1685,6 +1701,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1715,6 +1732,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1757,6 +1775,7 @@ export interface paths { 401: components['responses']['401'] 403: components['responses']['403'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1802,6 +1821,7 @@ export interface paths { 400: components['responses']['400'] 403: components['responses']['403'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1846,6 +1866,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1893,6 +1914,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1926,6 +1948,7 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1971,6 +1994,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1980,52 +2004,50 @@ export interface paths { patch?: never trace?: never } - '/admin/teams/{teamID}/builds/cancel': { + '/admin/sandboxes/running-counts': { parameters: { query?: never header?: never path?: never cookie?: never } - get?: never - put?: never /** - * Cancel all builds for a team - * @description Cancels all in-progress and pending builds for the specified team + * Count running sandboxes by team + * @description Returns a shared snapshot normally refreshed after five seconds. A + * sandbox transitioning out of running can remain counted until removal. */ - post: { + get: { parameters: { query?: never header?: never - path: { - /** @description Team ID */ - teamID: string - } + path?: never cookie?: never } requestBody?: never responses: { - /** @description Successfully cancelled builds */ + /** @description Running sandbox counts keyed by team ID */ 200: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['AdminBuildCancelResult'] + 'application/json': components['schemas']['AdminTeamRunningSandboxCounts'] } } 401: components['responses']['401'] - 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } + put?: never + post?: never delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/teams/{teamID}/api-keys': { + '/admin/teams/{teamID}/builds/cancel': { parameters: { query?: never header?: never @@ -2035,8 +2057,8 @@ export interface paths { get?: never put?: never /** - * Create team API key as admin - * @description Creates a team API key for internal service workflows. + * Cancel all builds for a team + * @description Cancels all in-progress and pending builds for the specified team */ post: { parameters: { @@ -2048,25 +2070,20 @@ export interface paths { } cookie?: never } - requestBody: { - content: { - 'application/json': components['schemas']['NewTeamAPIKey'] - } - } + requestBody?: never responses: { - /** @description Team API key created successfully */ - 201: { + /** @description Successfully cancelled builds */ + 200: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['CreatedTeamAPIKey'] + 'application/json': components['schemas']['AdminBuildCancelResult'] } } - 400: components['responses']['400'] 401: components['responses']['401'] - 403: components['responses']['403'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2076,7 +2093,7 @@ export interface paths { patch?: never trace?: never } - '/admin/teams/{teamID}/api-keys/{apiKeyID}': { + '/admin/teams/{teamID}/api-keys': { parameters: { query?: never header?: never @@ -2085,80 +2102,40 @@ export interface paths { } get?: never put?: never - post?: never /** - * Delete team API key as admin - * @description Deletes a team API key for internal service workflows. + * Create team API key as admin + * @description Creates a team API key for internal service workflows. */ - delete: { + post: { parameters: { query?: never header?: never path: { /** @description Team ID */ teamID: string - apiKeyID: components['parameters']['apiKeyID'] - } - cookie?: never - } - requestBody?: never - responses: { - /** @description Team API key deleted successfully */ - 204: { - headers: { - [name: string]: unknown - } - content?: never } - 400: components['responses']['400'] - 401: components['responses']['401'] - 404: components['responses']['404'] - 500: components['responses']['500'] - } - } - options?: never - head?: never - patch?: never - trace?: never - } - '/access-tokens': { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - get?: never - put?: never - /** - * Create access token - * @deprecated - * @description Create a new access token. Deprecated; use an API key (E2B_API_KEY) instead. - */ - post: { - parameters: { - query?: never - header?: never - path?: never cookie?: never } requestBody: { content: { - 'application/json': components['schemas']['NewAccessToken'] + 'application/json': components['schemas']['NewTeamAPIKey'] } } responses: { - /** @description Access token created successfully */ + /** @description Team API key created successfully */ 201: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['CreatedAccessToken'] + 'application/json': components['schemas']['CreatedTeamAPIKey'] } } + 400: components['responses']['400'] 401: components['responses']['401'] - 410: components['responses']['410'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2168,7 +2145,7 @@ export interface paths { patch?: never trace?: never } - '/access-tokens/{accessTokenID}': { + '/admin/teams/{teamID}/api-keys/{apiKeyID}': { parameters: { query?: never header?: never @@ -2179,29 +2156,33 @@ export interface paths { put?: never post?: never /** - * Delete access token - * @description Delete an access token + * Delete team API key as admin + * @description Deletes a team API key for internal service workflows. */ delete: { parameters: { query?: never header?: never path: { - accessTokenID: components['parameters']['accessTokenID'] + /** @description Team ID */ + teamID: string + apiKeyID: components['parameters']['apiKeyID'] } cookie?: never } requestBody?: never responses: { - /** @description Access token deleted successfully */ + /** @description Team API key deleted successfully */ 204: { headers: { [name: string]: unknown } content?: never } + 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2240,6 +2221,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2271,6 +2253,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2314,6 +2297,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2347,6 +2331,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2382,6 +2367,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2414,6 +2400,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2456,6 +2443,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2485,6 +2473,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2493,56 +2482,957 @@ export interface paths { patch?: never trace?: never } -} -export type webhooks = Record -export interface components { - schemas: { - Team: { - /** @description Identifier of the team */ - teamID: string - /** @description Name of the team */ - name: string - /** @description API key for the team */ - apiKey: string - /** @description Whether the team is the default team */ - isDefault: boolean + '/secrets': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never } - TeamUser: { - /** - * Format: uuid - * @description Identifier of the user - */ - id: string - /** - * @deprecated - * @description Email of the user - * @default null - */ - email: string | null + /** + * List project secrets + * @description List the project's secrets. No response carries a secret value. + */ + get: { + parameters: { + query?: { + /** @description Cursor to start the list from */ + nextToken?: components['parameters']['paginationNextToken'] + /** @description Maximum number of items to return per page */ + limit?: components['parameters']['paginationLimit'] + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully listed the project's secrets */ + 200: { + headers: { + 'X-Next-Token': components['headers']['XNextToken'] + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } } - TemplateUpdateRequest: { - /** @description Whether the template is public or only accessible by the team */ - public?: boolean + put?: never + /** + * Create a secret + * @description Create a secret by storing a runtime marker as its first version. The response carries metadata only. + */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['NewSecret'] + } + } + responses: { + /** @description Successfully created the secret */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + /** @description Secret name conflict or project live-secret limit reached. A quota response has error_code secret_limit_reached and, when reported by the backend, the effective cap in message. */ + 409: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } } - TemplateUpdateResponse: { - /** @description Names of the template (namespace/alias format when namespaced) */ - names: string[] + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/secrets/{secretID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never } /** - * Format: int32 - * @description CPU cores for the sandbox + * Get a secret + * @description Get one secret's metadata, selected by identifier or name. */ - CPUCount: number + get: { + parameters: { + query?: never + header?: never + path: { + secretID: components['parameters']['secretID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully retrieved the secret */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } + } + put?: never /** - * Format: int32 - * @description Memory for the sandbox in MiB + * Update a secret + * @description Replace the secret's stored marker by appending a new version. The response carries metadata only. */ - MemoryMB: number + post: { + parameters: { + query?: never + header?: never + path: { + secretID: components['parameters']['secretID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['SecretUpdate'] + } + } + responses: { + /** @description Successfully updated the secret */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } + } /** - * Format: int32 - * @description Disk size for the sandbox in MiB + * Delete a secret + * @description Revoke the secret and schedule its versions for cleanup. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + secretID: components['parameters']['secretID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully deleted the secret */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * List rigs of a cluster + * @description List the orchestrator node pools ("rigs") of a cluster with a snapshot of their scaling groups. Forwarded to the cluster's edge service; a cluster with no rig management configured returns an empty list, and the local cluster answers 501. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the rigs of the cluster */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Rig'][] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/{rigID}/capacity': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + /** + * Set the capacity of a rig + * @description Set the desired instance count on the rig's scaling group. The value is passed to the cloud provider unchanged; violations of the group's bounds or conflicting concurrent operations surface as errors. + */ + put: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Rig identifier (e.g. "default") */ + rigID: components['parameters']['rigID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['RigCapacityChange'] + } + } + responses: { + /** @description Capacity change accepted */ + 202: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/instances/{instanceID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + post?: never + /** + * Terminate an instance of a rig + * @description Terminate an instance in whichever rig's scaling group it belongs to. The caller chooses whether the rig shrinks or the instance is replaced. + */ + delete: { + parameters: { + query: { + /** @description When true, desired capacity is decremented (rig shrinks); when false, the scaling group launches a replacement instance */ + decrementDesired: boolean + } + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Provider instance ID */ + instanceID: string + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Instance termination accepted */ + 202: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/{rigID}/instances': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * List the instances attached to a rig + * @description List the instances attached to the rig's scaling group with their creation time and transition state, sorted by instance ID. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Rig identifier (e.g. "default") */ + rigID: components['parameters']['rigID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the instances of the rig */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['RigInstance'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/{rigID}/errors': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * List recent scaling errors of a rig + * @description List recent scaling errors on the rig's scaling group (e.g. failed instance creations due to resource exhaustion), newest first. + */ + get: { + parameters: { + query?: { + /** @description Maximum number of errors to return */ + limit?: number + } + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Rig identifier (e.g. "default") */ + rigID: components['parameters']['rigID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the scaling errors of the rig */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['RigError'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/sandboxes/{sandboxID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get sandbox events */ + get: { + parameters: { + query?: { + offset?: number + limit?: number + orderAsc?: boolean + /** @description Filter events to the provided event types */ + types?: string[] + } + header?: never + path: { + sandboxID: components['parameters']['sandboxID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the sandbox events */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['SandboxEvent'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/sandboxes': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get all sandbox events for the team associated with the API key */ + get: { + parameters: { + query?: { + offset?: number + limit?: number + orderAsc?: boolean + /** @description Filter events to the provided event types */ + types?: string[] + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the sandbox events */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['SandboxEvent'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/webhooks': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description List registered webhooks. */ + get: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description List of registered webhooks. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDetail'][] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + /** @description Register events webhook. */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['WebhookCreate'] + } + } + responses: { + /** @description Successfully created webhook. */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookCreation'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/webhooks/{webhookID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get a registered webhook. */ + get: { + parameters: { + query?: never + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the webhook configuration. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDetail'] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + /** @description Delete a registered webhook. */ + delete: { + parameters: { + query?: never + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully deleted webhook. */ + 200: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + options?: never + head?: never + /** @description Update a registered webhook configuration. */ + patch: { + parameters: { + query?: never + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['WebhookConfiguration'] + } + } + responses: { + /** @description Successfully updated webhook. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDetail'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + trace?: never + } + '/events/webhooks/{webhookID}/deliveries': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description List webhook delivery attempts. */ + get: { + parameters: { + query?: { + /** @description Opaque cursor from the previous response's nextCursor field. */ + cursor?: string + limit?: number + orderAsc?: boolean + /** @description Include deliveries at or after this timestamp. */ + start?: string + /** @description Include deliveries before this timestamp. */ + end?: string + /** @description Filter deliveries by delivery status */ + deliveryStatus?: ('success' | 'failed')[] + /** @description Filter deliveries by event type */ + eventType?: string[] + } + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description List of webhook delivery attempts grouped by event. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDeliveriesListPayload'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/webhooks/{webhookID}/stats': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get webhook delivery aggregate stats. */ + get: { + parameters: { + query?: { + /** @description Inclusive stats range start. Defaults to 24 hours ago. */ + start?: string + /** @description Exclusive stats range end. Defaults to now. */ + end?: string + } + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Webhook delivery stats. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDeliveryStats'] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } +} +export type webhooks = Record +export interface components { + schemas: { + /** @description An orchestrator node pool backed by one cloud scaling group */ + Rig: { + /** @description Rig identifier (e.g. "default") */ + id: string + /** @description Cloud provider backing the rig ("aws" or "gcp") */ + provider: string + /** @description Canonical cloud resource ID of the scaling group backing the rig (ARN on AWS, self-link on GCP) */ + resourceID: string + /** + * Format: int32 + * @description Desired number of instances in the rig + */ + capacityDesired: number + /** + * Format: int32 + * @description Minimum capacity enforced on the rig's scaling group. Omitted when nothing enforces bounds (GCP MIG without an active autoscaler). + */ + capacityMin?: number + /** + * Format: int32 + * @description Maximum capacity enforced on the rig's scaling group. Omitted when nothing enforces bounds (GCP MIG without an active autoscaler). + */ + capacityMax?: number + /** + * Format: int32 + * @description Number of instances currently attached to the rig + */ + capacityCurrent: number + } + /** @description Desired capacity to set on the rig's scaling group */ + RigCapacityChange: { + /** + * Format: int32 + * @description Absolute desired number of instances in the rig + */ + desired: number + } + /** @description An instance attached to a rig's scaling group */ + RigInstance: { + /** @description Provider instance ID (EC2 instance ID on AWS, instance name on GCP), also the node ID the orchestrator reports */ + id: string + /** + * Format: date-time + * @description When the provider created the instance. Omitted while the instance is transitioning. + */ + createdAt?: string + /** @description The provider is creating, deleting, recreating or otherwise mutating the instance */ + transitioning: boolean + /** @description The instance is on its way out of the group and can never become healthy again */ + terminating: boolean + } + /** @description Scaling error on the rig's scaling group, e.g. a failed instance creation due to resource exhaustion */ + RigError: { + /** + * Format: date-time + * @description When the error occurred + */ + timestamp: string + /** @description Provider-specific error code (e.g. ZONE_RESOURCE_POOL_EXHAUSTED, Failed) */ + code: string + /** @description Human-readable error message */ + message: string + /** @description Instance the error relates to, if any */ + instance?: string + /** @description Action being performed when the error occurred (e.g. CREATING) */ + action?: string + } + Team: { + /** @description Identifier of the team */ + teamID: string + /** @description Name of the team */ + name: string + /** @description API key for the team */ + apiKey: string + /** @description Whether the team is the default team */ + isDefault: boolean + } + TeamUser: { + /** + * Format: uuid + * @description Identifier of the user + */ + id: string + /** + * @deprecated + * @description Email of the user + * @default null + */ + email: string | null + } + TemplateUpdateRequest: { + /** @description Whether the template is public or only accessible by the team */ + public?: boolean + } + TemplateUpdateResponse: { + /** @description Names of the template (namespace/alias format when namespaced) */ + names: string[] + } + /** + * Format: int32 + * @description CPU cores for the sandbox + */ + CPUCount: number + /** + * Format: int32 + * @description Memory for the sandbox in MiB + */ + MemoryMB: number + /** + * Format: int32 + * @description Disk size for the sandbox in MiB + */ + DiskSizeMB: number + /** + * Format: int32 + * @description Requested minimum free space after the template's build steps, in MiB. Omit to use the team's default. Set to 0 to request no minimum free-disk growth. The filesystem is never shrunk, including inherited or already-larger filesystems. Growth is best effort, so filesystem metadata can leave the available space slightly below the requested minimum. */ - DiskSizeMB: number + MinFreeDiskMb: number /** @description Version of the envd running in the sandbox */ EnvdVersion: string SandboxMetadata: { @@ -2553,6 +3443,12 @@ export interface components { * @enum {string} */ SandboxState: 'running' | 'paused' + /** + * @description Sort direction + * @default desc + * @enum {string} + */ + OrderDirection: 'asc' | 'desc' SnapshotInfo: { /** @description Identifier of the snapshot template including the tag. Uses namespace/alias when a name was provided (e.g. team-slug/my-snapshot:default), otherwise falls back to the raw template ID (e.g. abc123:default). */ snapshotID: string @@ -2579,7 +3475,9 @@ export interface components { egressProxy?: components['schemas']['SandboxEgressProxyConfig'] /** @description Specify host mask which will be used for all sandbox requests */ maskRequestHost?: string - /** @description Per-domain transform rules applied to matching egress HTTP/HTTPS requests. Keys are domains (e.g. "api.example.com", "example.com"). A domain listed here is not automatically allowed - use allowOut to permit the traffic. */ + /** @description Sandbox ports that serve HTTPS rather than plaintext HTTP. Affects how the proxy reaches the service inside the sandbox; the public URL is HTTPS either way. Certificates are not verified, so self-signed ones work. The envd port (49983) cannot be listed. */ + httpsPorts?: number[] + /** @description Per-domain transform rules applied to matching outbound HTTPS requests. Keys may be exact DNS names (for example, "api.example.com") or a leading wildcard (for example, "*.example.com"), and are normalized to lowercase on write. Wildcards match subdomains at any depth but not the apex domain; a bare "*" is invalid. Exact rules take precedence, followed by the longest matching wildcard suffix, and matching rule sets are not merged. Broad wildcards such as "*.com" are allowed and may expose transformed credentials to every matching destination the sandbox contacts. Rules do not grant network access; configure allowOut separately to permit the destination. */ rules?: { [key: string]: components['schemas']['SandboxNetworkRule'][] } @@ -2591,7 +3489,7 @@ export interface components { /** @description List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. */ denyOut?: string[] egressProxy?: components['schemas']['SandboxEgressProxyConfig'] - /** @description Per-domain transform rules. Replaces all existing rules when provided. */ + /** @description Per-domain transform rules applied to matching outbound HTTPS requests. Replaces all existing rules when provided. Keys may be exact DNS names or a single leading wildcard (for example, "*.example.com"), and are normalized to lowercase on write. Wildcards match subdomains at any depth but not the apex domain; a bare "*" is invalid. Exact rules take precedence, followed by the longest matching wildcard suffix, and matching rule sets are not merged. Broad wildcards such as "*.com" are allowed and may expose transformed credentials to every matching destination the sandbox contacts. Rules do not grant network access; configure allowOut separately to permit the destination. */ rules?: { [key: string]: components['schemas']['SandboxNetworkRule'][] } @@ -2617,6 +3515,16 @@ export interface components { username?: string /** @description Optional SOCKS5 password (RFC 1929), max 255 bytes. */ password?: string + tls?: components['schemas']['SandboxEgressProxyTLSConfig'] + } | null + /** @description TLS for the connection to the SOCKS5 proxy. The SOCKS5 negotiation and the tunneled traffic both run inside the TLS session, so the proxy credentials are not sent in the clear. This secures only the hop to the proxy; what the proxy does onward is its own concern. A half-close from the sandbox reaches the proxy as a TLS close_notify, not a TCP FIN, and a proxy that treats close_notify as a full close cuts the reply short. */ + SandboxEgressProxyTLSConfig: { + /** @description Connect to the proxy over TLS. When false, no other field in this object may be set. */ + enabled: boolean + /** @description Name to verify the proxy certificate against, and to send as SNI. Defaults to the host part of address. Set this only when the certificate does not match the address the proxy is reached at. */ + serverName?: string + /** @description One or more PEM-encoded certificates to verify the proxy against, for a proxy fronted by a private CA. These replace the system trust store, which is what is used when this is omitted. The system trust store depends on the host the orchestrator runs on, so set this to get the same verification everywhere. */ + caCert?: string } | null /** * @description Auto-resume enabled flag for paused sandboxes. Default false. @@ -2853,6 +3761,36 @@ export interface components { iam?: components['schemas']['SandboxIam'] volumeMounts?: components['schemas']['SandboxVolumeMount'][] } + /** @description Sandbox creation request. All system communication with the sandbox is always secured; the template's envd version must support secured access. */ + NewSandboxV2: { + /** @description Identifier of the required template */ + templateID: string + /** + * Format: int32 + * @description Time to live for the sandbox in seconds. + * @default 300 + */ + timeout: number + /** + * @description Automatically pauses the sandbox after the timeout + * @default false + */ + autoPause: boolean + /** + * @description Controls the snapshot kind taken when the sandbox auto-pauses on timeout (only relevant when autoPause is true). When false, the auto-pause drops the in-memory state and persists only the filesystem (a filesystem-only snapshot); resuming it cold-boots (reboots) the sandbox from disk. Such a snapshot cannot be auto-resumed by traffic and must be resumed explicitly, so it cannot be combined with autoResume. Defaults to true (full memory snapshot). + * @default true + */ + autoPauseMemory: boolean + autoResume?: components['schemas']['SandboxAutoResumeConfig'] + /** @description Allow sandbox to access the internet. When set to false, it behaves the same as specifying denyOut to 0.0.0.0/0 in the network config. */ + allow_internet_access?: boolean + network?: components['schemas']['SandboxNetworkConfig'] + 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'] @@ -2879,6 +3817,8 @@ export interface components { * @description Automatically pauses the sandbox after the timeout */ autoPause?: boolean + /** @description Defaults to true. When false, resume from disk state only: the sandbox cold-boots fresh and any memory in the snapshot is ignored, never modified or deleted. Disk state has crash-recovery semantics — writes not flushed before the pause may be lost. A no-op for snapshots that contain no memory. Rejected with an error in environments where this capability is not enabled, never silently downgraded to a memory restore. */ + memory?: boolean } ConnectSandbox: { /** @@ -2886,6 +3826,18 @@ export interface components { * @description Timeout in seconds from the current time after which the sandbox should expire */ timeout: number + /** @description Defaults to true. When false and the sandbox is paused, resume from disk state only: the sandbox cold-boots fresh and any memory in the snapshot is ignored, never modified or deleted. Disk state has crash-recovery semantics — writes not flushed before the pause may be lost. A no-op for snapshots that contain no memory. Rejected with an error in environments where this capability is not enabled, never silently downgraded to a memory restore. */ + memory?: boolean + } + ConnectSandboxV2: { + /** + * Format: int32 + * @description Timeout in seconds from the current time after which the sandbox should expire + * @default 300 + */ + timeout: number + /** @description Defaults to true. When false and the sandbox is paused, resume from disk state only: the sandbox cold-boots fresh and any memory in the snapshot is ignored, never modified or deleted. Disk state has crash-recovery semantics — writes not flushed before the pause may be lost. A no-op for snapshots that contain no memory. Rejected with an error in environments where this capability is not enabled, never silently downgraded to a memory restore. */ + memory?: boolean } SandboxTimeoutRequest: { /** @@ -2901,6 +3853,8 @@ export interface components { SandboxSnapshotRequest: { /** @description Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one. */ name?: string + /** @description Whether to capture a full memory snapshot. When false, only the filesystem is persisted: the snapshot is smaller and faster to take, and sandboxes created from it cold-boot (start fresh from disk) instead of restoring memory, so they begin without the source sandbox's running processes, in-memory state, and open connections. The source sandbox keeps running in both cases. Defaults to true. */ + memory?: boolean } SandboxPauseRequest: { /** @@ -2974,6 +3928,14 @@ export interface components { /** @description Number of sandboxes that failed to kill */ failedCount: number } + /** + * @description Cached live sandbox index count keyed by team ID. Counts may briefly + * include sandboxes transitioning out of running; teams without indexed + * sandboxes are omitted. + */ + AdminTeamRunningSandboxCounts: { + [key: string]: number + } AdminBuildCancelResult: { /** @description Number of builds successfully cancelled */ cancelledCount: number @@ -3046,46 +4008,6 @@ export interface components { */ aliases: string[] } - TemplateLegacy: { - /** @description Identifier of the template */ - templateID: string - /** @description Identifier of the last successful build for given template */ - buildID: string - cpuCount: components['schemas']['CPUCount'] - memoryMB: components['schemas']['MemoryMB'] - diskSizeMB: components['schemas']['DiskSizeMB'] - /** @description Whether the template is public or only accessible by the team */ - public: boolean - /** @description Aliases of the template */ - aliases: string[] - /** - * Format: date-time - * @description Time when the template was created - */ - createdAt: string - /** - * Format: date-time - * @description Time when the template was last updated - */ - updatedAt: string - createdBy: components['schemas']['TeamUser'] | null - /** - * Format: date-time - * @description Time when the template was last used - */ - lastSpawnedAt: string | null - /** - * Format: int64 - * @description Number of times the template was used - */ - spawnCount: number - /** - * Format: int32 - * @description Number of times the template was built - */ - buildCount: number - envdVersion: components['schemas']['EnvdVersion'] - } TemplateBuild: { /** * Format: uuid @@ -3154,20 +4076,6 @@ export interface components { /** @description Whether the template is public or only accessible by the team */ public: boolean } - TemplateBuildRequest: { - /** @description Alias of the template */ - alias?: string - /** @description Dockerfile for the template */ - dockerfile: string - /** @description Identifier of the team */ - teamID?: string - /** @description Start command to execute in the template after the build */ - startCmd?: string - /** @description Ready check command to execute in the template after the build */ - readyCmd?: string - cpuCount?: components['schemas']['CPUCount'] - memoryMB?: components['schemas']['MemoryMB'] - } /** @description Step in the template build process */ TemplateStep: { /** @description Type of the step */ @@ -3177,7 +4085,7 @@ export interface components { * @default [] */ args: string[] - /** @description Hash of the files used in the step */ + /** @description Hash of the files used in the step (lowercase hex SHA-256) */ filesHash?: string /** * @description Whether the step should be forced to run regardless of the cache @@ -3197,22 +4105,12 @@ export interface components { alias?: string /** * @deprecated - * @description Identifier of the team - */ - teamID?: string - cpuCount?: components['schemas']['CPUCount'] - memoryMB?: components['schemas']['MemoryMB'] - } - TemplateBuildRequestV2: { - /** @description Alias of the template */ - alias: string - /** - * @deprecated - * @description Identifier of the team + * @description Identifier of the team, as its UUID or its public project ID (prj_) */ teamID?: string cpuCount?: components['schemas']['CPUCount'] memoryMB?: components['schemas']['MemoryMB'] + minFreeDiskMb?: components['schemas']['MinFreeDiskMb'] } FromImageRegistry: | components['schemas']['AWSRegistry'] @@ -3251,6 +4149,7 @@ export interface components { /** @description Password to use for the registry */ password: string } + /** @description Exactly one of fromImage or fromTemplate must be given and non-empty. */ TemplateBuildStartV2: { /** @description Image to use as a base for the template build */ fromImage?: string @@ -3277,6 +4176,10 @@ export interface components { present: boolean /** @description Url where the file should be uploaded to */ url?: string + /** @description Request headers that must be sent with the upload request */ + headers?: { + [key: string]: string + } } /** * @description State of the sandbox @@ -3313,7 +4216,8 @@ export interface components { TemplateBuildStatus: 'building' | 'waiting' | 'ready' | 'error' TemplateBuildInfo: { /** - * @description Build logs + * @deprecated + * @description Build logs (always empty since the V1 build path was removed, use logEntries) * @default [] */ logs: string[] @@ -3348,11 +4252,16 @@ export interface components { LogsSource: 'temporary' | 'persistent' /** * @description Status of the node. - * - draining: the node is bound to be shut down. It will not accept new sandboxes and will stop once all existing sandboxes are done. * - standby: the node is not actively used, but it can return to ready and continue serving traffic. * @enum {string} */ - NodeStatus: 'ready' | 'draining' | 'connecting' | 'unhealthy' | 'standby' + NodeStatus: + | 'ready' + | 'draining' + | 'connecting' + | 'unhealthy' + | 'standby' + | 'shutting_down' NodeStatusChange: { /** * Format: uuid @@ -3467,6 +4376,16 @@ export interface components { * @description Number of sandboxes running on the node */ sandboxCount: number + /** + * Format: int64 + * @description Cached node-scoped sandbox admission limit. Nonpositive values reject creation. + */ + maxSandboxes: number + /** + * Format: uint64 + * @description Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. + */ + outstandingWork: number metrics: components['schemas']['NodeMetrics'] /** * Format: uint64 @@ -3507,9 +4426,17 @@ export interface components { * @description Number of sandboxes running on the node */ sandboxCount: number + /** + * Format: int64 + * @description Cached node-scoped sandbox admission limit. Nonpositive values reject creation. + */ + maxSandboxes: number + /** + * Format: uint64 + * @description Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. + */ + outstandingWork: number metrics: components['schemas']['NodeMetrics'] - /** @description List of cached builds id on the node */ - cachedBuilds: string[] /** * Format: uint64 * @description Number of sandbox create successes @@ -3521,27 +4448,6 @@ export interface components { */ createFails: number } - CreatedAccessToken: { - /** - * Format: uuid - * @description Identifier of the access token - */ - id: string - /** @description Name of the access token */ - name: string - /** @description The fully created access token */ - token: string - mask: components['schemas']['IdentifierMaskingDetails'] - /** - * Format: date-time - * @description Timestamp of access token creation - */ - createdAt: string - } - NewAccessToken: { - /** @description Name of the access token */ - name: string - } TeamAPIKey: { /** * Format: uuid @@ -3635,6 +4541,8 @@ export interface components { * @description Error code */ code: number + /** @description Machine-readable semantic error code. Not a closed set; initial values: sandbox_capacity_unavailable, sandbox_placement_timeout, sandbox_no_compatible_node, sandbox_create_failed, internal_server_error, secret_limit_reached. */ + error_code?: string /** @description Error */ message: string } @@ -3661,11 +4569,286 @@ export interface components { name: string /** @description Auth token to use for interacting with volume content */ token: string + /** + * @description Domain to use as the destination for volume content requests, + * replacing the default `api.`. Only returned when the + * team is connected to a custom (BYOC) cluster; absent otherwise, in + * which case the default domain is used. + */ + domain?: string } NewVolume: { /** @description Name of the volume */ name: string } + /** @description Customer metadata of the secret. Always present, empty when unset. At most 32 entries; keys are limited to 128 bytes, values to 1024 bytes, and a secret's metadata to 8192 bytes in total. */ + SecretMetadata: { + [key: string]: string + } + /** @description Metadata of a secret. It never carries the secret value. */ + Secret: { + /** @description Identifier of the secret */ + secretID: string + /** @description Name of the secret, unique within the project */ + name: string + /** + * Format: int64 + * @description Version served to readers that do not name one + */ + currentVersion: number + metadata: components['schemas']['SecretMetadata'] + /** + * Format: date-time + * @description Time when the secret was created + */ + createdAt: string + /** + * Format: date-time + * @description Time when the secret was last updated + */ + updatedAt: string + } + NewSecret: { + /** @description Name of the secret, unique within the project. Names are lower-cased before storage and returned in that canonical form; the sec_ prefix is reserved for secret identifiers. */ + name: string + /** @description Runtime marker stored as the secret's first version. The runtime resolves it to a value at sandbox egress. */ + value: string + metadata?: components['schemas']['SecretMetadata'] + } + SecretUpdate: { + /** @description Runtime marker stored as the secret's new version. The runtime resolves it to a value at sandbox egress. */ + value: string + metadata?: components['schemas']['SecretMetadata'] + } + /** @description Sandbox event */ + SandboxEvent: { + /** + * Format: uuid + * @description Event unique identifier + */ + id: string + /** @description Event structure version */ + version: string + /** @description Event name */ + type: string + /** + * @deprecated + * @description Category of the event (e.g., 'lifecycle', 'process', etc.) + */ + eventCategory?: string + /** + * @deprecated + * @description Label for the specific event type (e.g., 'sandbox_started', 'process_oom', etc.) + */ + eventLabel?: string + /** @description Optional JSON data associated with the event */ + eventData?: Record | null + /** + * Format: date-time + * @description Timestamp of the event + */ + timestamp: string + /** + * Format: string + * @description Unique identifier for the sandbox + */ + sandboxId: string + /** + * Format: string + * @description Unique identifier for the sandbox execution + */ + sandboxExecutionId: string + /** + * Format: string + * @description Unique identifier for the sandbox template + */ + sandboxTemplateId: string + /** + * Format: string + * @description Unique identifier for the sandbox build + */ + sandboxBuildId: string + /** + * Format: uuid + * @description Team identifier associated with the sandbox + */ + sandboxTeamId: string + } + /** @description Configuration for registering new webhooks */ + WebhookCreate: { + name: string + /** Format: uri */ + url: string + events: string[] + /** @default true */ + enabled: boolean + /** @description Secret used to sign the webhook payloads */ + signatureSecret: string + } + /** @description Webhook creation response */ + WebhookCreation: { + /** @description Webhook unique identifier */ + id: string + /** @description Webhook user friendly name */ + name: string + /** + * Format: date-time + * @description Time when the template was created + */ + createdAt: string + /** @description Unique identifier for the team */ + teamId: string + /** Format: uri */ + url: string + enabled: boolean + events: string[] + } + /** @description Webhook detail response */ + WebhookDetail: { + /** @description Webhook unique identifier */ + id: string + /** @description Unique identifier for the team */ + teamId: string + /** @description Webhook user friendly name */ + name: string + /** + * Format: date-time + * @description Time when the template was created + */ + createdAt: string + /** Format: uri */ + url: string + enabled: boolean + events: string[] + } + /** @description Configuration for updating existing webhooks */ + WebhookConfiguration: { + enabled?: boolean + /** @description Webhook user friendly name */ + name?: string + /** Format: uri */ + url?: string + events?: string[] + /** @description Secret used to sign the webhook payloads */ + signatureSecret?: string + } + /** @description Webhook delivery attempt */ + WebhookDelivery: { + /** + * Format: uuid + * @description Delivery attempt identifier + */ + id: string + /** + * Format: uuid + * @description Team identifier + */ + teamId: string + /** + * Format: uuid + * @description Webhook configuration identifier + */ + webhookId: string + /** + * Format: uuid + * @description Sandbox event identifier + */ + eventId: string + /** @description Sandbox identifier */ + sandboxId: string + /** @description Sandbox event type */ + eventType: string + /** + * @description Delivery attempt status + * @enum {string} + */ + status: 'success' | 'failed' + /** + * Format: int32 + * @description Delivery request duration in milliseconds + */ + durationMs: number + /** @description Serialized webhook request body */ + requestBody: string + /** @description JSON-encoded request headers with sensitive values redacted */ + requestHeaders: string + /** + * Format: uri + * @description URL attempted for this delivery + */ + requestUrl: string + /** @description Truncated response body, if a response was received */ + responseBody?: string | null + /** @description JSON-encoded response headers, if a response was received */ + responseHeaders?: string | null + /** + * Format: int32 + * @description HTTP response status code, if a response was received + */ + responseHttpStatusCode?: number | null + /** + * @description Machine-readable non-HTTP or HTTP failure class + * @enum {string|null} + */ + errorClass: + | 'http_error' + | 'dns_error' + | 'timeout' + | 'transport_error' + | 'request_error' + | 'signature_error' + | 'canceled' + | null + /** @description Error message for failures without a useful response body */ + errorMessage?: string | null + /** + * Format: date-time + * @description Time when the delivery attempt started + */ + timestamp: string + } + /** @description Webhook delivery aggregate stats */ + WebhookDeliveryStats: { + buckets: components['schemas']['WebhookDeliveryStatsBucket'][] + /** Format: int64 */ + total: number + /** Format: int64 */ + failed: number + durationMs: components['schemas']['WebhookDeliveryDurationStats'] + } + /** @description Webhook delivery duration statistics in milliseconds */ + WebhookDeliveryDurationStats: { + /** Format: double */ + minimum: number + /** Format: double */ + average: number + /** Format: double */ + maximum: number + } + /** @description Webhook delivery stats for a time bucket */ + WebhookDeliveryStatsBucket: { + /** Format: date-time */ + timestamp: string + /** Format: int64 */ + total: number + /** Format: int64 */ + failed: number + durationMs: components['schemas']['WebhookDeliveryDurationStats'] + } + /** @description Webhook delivery attempts grouped by sandbox event */ + WebhookDeliveryGroup: { + /** Format: uuid */ + eventId: string + eventType: string + sandboxId: string + attempts: components['schemas']['WebhookDelivery'][] + } + /** @description Paginated webhook delivery attempts grouped by event */ + WebhookDeliveriesListPayload: { + data: components['schemas']['WebhookDeliveryGroup'][] + /** @description Cursor to pass to the next list request, or null when there is no next page. */ + nextCursor: string | null + } } responses: { /** @description Bad request */ @@ -3713,9 +4896,14 @@ export interface components { 'application/json': components['schemas']['Error'] } } - /** @description Gone */ - 410: { + /** @description Too many requests */ + 429: { headers: { + /** + * @description When present, the number of seconds to wait before retrying the request. + * @example 30 + */ + 'Retry-After'?: number [name: string]: unknown } content: { @@ -3731,15 +4919,55 @@ export interface components { 'application/json': components['schemas']['Error'] } } + /** @description Not implemented by this deployment */ + 501: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + /** @description Backend error */ + 502: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + /** @description Service unavailable */ + 503: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + /** @description Backend timeout */ + 504: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } } parameters: { + /** @description Identifier of the cluster */ + clusterID: string + /** @description Rig identifier (e.g. "default") */ + rigID: string templateID: string buildID: string sandboxID: string + /** @description Identifier of the team, as its UUID or its public project ID (prj_) */ teamID: string nodeID: string apiKeyID: string - accessTokenID: string snapshotID: string tag: string /** @description Maximum number of items to return per page */ @@ -3747,9 +4975,16 @@ export interface components { /** @description Cursor to start the list from */ paginationNextToken: string volumeID: string + secretID: string + webhookID: string } requestBodies: never - headers: never + headers: { + /** @description Cursor to fetch the next page of results, if more exist */ + XNextToken: string + /** @description Number of running sandboxes matching the filters, before pagination is applied. Only present when running sandboxes were requested. */ + XTotalRunning: number + } pathItems: never } export type $defs = Record