diff --git a/CLAUDE.md b/CLAUDE.md index 60b27dc2..aa329e17 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,5 @@ Use pnpm for JavaScript packages and uv for Python packages. +Use English exclusively in source code, comments, documentation, commit messages, and GitHub pull request titles and descriptions. Keep the JavaScript and Python SDKs, including sync and async Python APIs, behaviorally aligned. Run format checks, lint, type checks, unit tests, deterministic generation, builds, and package-install checks before committing. The API and envd snapshots under spec/ are generated from mono/infra. Do not edit them manually. Update them with `make sync-specs MONO_DIR=/path/to/mono`, then run `make generate`. diff --git a/reference-config/operations.yaml b/reference-config/operations.yaml index 3593301c..bd00e7ef 100644 --- a/reference-config/operations.yaml +++ b/reference-config/operations.yaml @@ -2,6 +2,8 @@ schemaVersion: 1 controlPlane: source: spec/openapi.yml + publicAuthSchemes: + - ApiKeyAuth publicTags: - sandboxes - snapshots @@ -41,6 +43,8 @@ controlPlane: envd: source: spec/envd/envd.yaml + publicAuthSchemes: + - AccessTokenAuth operations: GET /health: { id: getEnvironmentHealth, group: environment, slug: health } GET /metrics: { id: getEnvironmentMetrics, group: environment, slug: metrics } diff --git a/reference/manifest.json b/reference/manifest.json index 448d0c35..39a07f32 100644 --- a/reference/manifest.json +++ b/reference/manifest.json @@ -2,14 +2,14 @@ "files": { "connect/filesystem.md": "1cc1e81386810d3c5c999458e02340e3becb3da8138d943558584094750e5aae", "connect/process.md": "037574455e9e8c5159c391d8c007e7272560f62c4e5ee681f8aec29f9cd24539", - "openapi/control-plane.yml": "f0a5b8c69f92bc19ed2bebb77d1fc097f991f8ecdd06cf439082eb73b64c93fe", - "openapi/envd.yml": "e4d91017beb3885ef4b389ab23f30f43682ee6a2c01721275e6513d8cfe6217d", + "openapi/control-plane.yml": "95df3951c30223827e4933f1dab8c6e2c75a2a8d81269646f807427d11078548", + "openapi/envd.yml": "30a0fba3b410c77db6b50b674f0ef9d110bf626ca2558820b5a6a05e4640cf19", "openapi/markdown/assignTemplateTags.md": "fa9f7b2fd59332151695fab853f7f29b253e8837bbd6d8fa669258a303622819", "openapi/markdown/composeFiles.md": "c814b69b4721e1a8c1a4d202bfc38feccfd12576071a21af8d9380baf6c2ff28", - "openapi/markdown/connectSandbox.md": "d2acb99ec38360e5146f06a895f96bd6034a9cd8a56ff8acbd727cc8d1bb8fa5", - "openapi/markdown/createSandbox.md": "e17fc2aa20769eb0f4456aeaaf8e887af5faf3d4e9ed636e400bf08eaf401c17", + "openapi/markdown/connectSandbox.md": "035437cfff8d3174ac6517b90c3da75dc338181ec19d07274cef46430453c6ed", + "openapi/markdown/createSandbox.md": "d9934ce3b88e1aa9df092fbff4ad88b87136b6ed786c3c7ca1aba86b361ac51a", "openapi/markdown/createSnapshot.md": "b1aaf62afe3723c04fcd7c1f1b092a24a0342b28de2b680d6a9d0afc014ddc3b", - "openapi/markdown/createTemplate.md": "4a9604d8cb7af1cb8c0da6b7e3bd56cfb7a20a4c91463013e55850f9854ea06c", + "openapi/markdown/createTemplate.md": "6e9fe66bc4a9671fd778620064989117fd40a560d5a0e2db874e6ade707384c4", "openapi/markdown/deleteTemplate.md": "31f4140050bb8753a0e8294f4b60425695866b2a3930a861ed1657ec4fb07029", "openapi/markdown/deleteTemplateTags.md": "94e5f1831a4044ea6acb248326fba83d9c6f0123bd61a3b2c401426fcd434b82", "openapi/markdown/downloadFile.md": "3cded9f4d62b95e4fbb6bc4d548340c5943538010b04bfba690bfb042cd0ae4d", @@ -17,12 +17,12 @@ "openapi/markdown/getEnvironmentHealth.md": "640c9435ce216a6fd0e5da7ee646d8527e5de428c946fad219946f5c1de5622a", "openapi/markdown/getEnvironmentMetrics.md": "32f7d61ddb8980caec0beaa3821b99e5f6ef90c0e812390d4d417844ec99acdc", "openapi/markdown/getEnvironmentVariables.md": "12803bbc7e3f2638e16f013ce05b2a688d8085739e38049c1022452fd7881e50", - "openapi/markdown/getSandbox.md": "3cf849c2093a536b19858ec431162413cd8b5e66ae7fb344f83b881b331d15ca", + "openapi/markdown/getSandbox.md": "697dcedd7f51a776705eae4e0ea0c358f93da5d7408fae0e779a7eba39c88130", "openapi/markdown/getSandboxLogs.md": "d5e3adfca5ff72d3b04ad8f2f5bb038ce429d8d7381cc37db33c9b4f393df047", "openapi/markdown/getSandboxMetrics.md": "cc810ac8bd47c0bf3903a2a529d5489af61611cb7268ef2327f93007d08d9467", "openapi/markdown/getTeamMetrics.md": "8cdaab8e8e3f089793e75ce6e831ee4effec72acf4f9297644503e175fc25368", - "openapi/markdown/getTeamMetricsMax.md": "85ee15af0672e21484da2378da9ac0e5171d062eff30de3a70f2927056383ec0", - "openapi/markdown/getTemplate.md": "43e22cc700d0bdcc85206d59db06bc9bea47d57bc4d418ec7c353e4b404939cb", + "openapi/markdown/getTeamMetricsMax.md": "ec55664edb022a0d60573c0500a4d47c78a34a3a2cd538b842438fb569c97cb8", + "openapi/markdown/getTemplate.md": "08d46844ebf9a8825375faa3ff447d4f79fd36d421e9ab576ca9aed266b861c6", "openapi/markdown/getTemplateBuildLogs.md": "a9a58101cfbb8317a2cbfb0bf8ba35cfdb237cb318c1010309fc34e74da81052", "openapi/markdown/getTemplateBuildStatus.md": "d27fafb869e7ec31271b71f54e82bd166f0548cafffbe3614d9b6bed9381f26b", "openapi/markdown/getTemplateByAlias.md": "df84543b45bdff8db21e5084eb046b0cc46333d0a18dd296c04f9639a1ad6d71", @@ -40,7 +40,7 @@ "openapi/markdown/updateSandboxNetwork.md": "30717801dfc58a1ddd3e7eb889af7ad2fec16ea7c6d8a13f8eb1356066165ac2", "openapi/markdown/updateTemplate.md": "0e18de978fc7005ec36645d003827354df933fb9e51ba218fbdea55639c0636a", "openapi/markdown/uploadFile.md": "acf5f7029d2b4de2ce96b50bb24f5e4495c188dfed878851ffe2f91e5b7b24d3", - "openapi/operations.json": "740c58aa17455c219647713f1d3f9ea5bea7ea0aeff346436f8c1f1902c973d3", + "openapi/operations.json": "1593bd80b73d87117a5ebf8211d0ba014e0170c84fca5fe9f1a82ec457a6b575", "sdk/cli/auth.md": "0b2c75262b0c0670c7bc0f0f3cbed65fbe4a16d6fe4437789a5685b8bf2174a1", "sdk/cli/sandbox.md": "0f59649197a6f42282a8b789c4384e8e41d2e525de414a22dffe073f0fca4d64", "sdk/cli/template.md": "f270b22b9ee10a9b954a14f24e04c5449b4d5823bb28002ffde3ed682594cfc5", diff --git a/reference/openapi/control-plane.yml b/reference/openapi/control-plane.yml index e8f6c103..c8b11a18 100644 --- a/reference/openapi/control-plane.yml +++ b/reference/openapi/control-plane.yml @@ -5,11 +5,6 @@ info: servers: - url: https://api.agentbox-runtime.ru components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key parameters: idempotencyKey: name: Idempotency-Key @@ -179,18 +174,11 @@ components: TeamUser: required: - id - - email properties: id: type: string format: uuid description: Identifier of the user - email: - type: string - nullable: true - deprecated: true - default: null - description: Email of the user TemplateUpdateRequest: properties: public: @@ -444,7 +432,6 @@ components: SandboxMetric: description: Metric entry with timestamp and line required: - - timestamp - timestampUnix - cpuCount - cpuUsedPct @@ -454,11 +441,6 @@ components: - diskUsed - diskTotal properties: - timestamp: - type: string - format: date-time - deprecated: true - description: Timestamp of the metric entry timestampUnix: type: integer format: int64 @@ -491,23 +473,10 @@ components: type: integer format: int64 description: Total disk space in bytes - SandboxVolumeMount: - type: object - properties: - name: - type: string - description: Name of the volume - path: - type: string - description: Path of the volume - required: - - name - - path Sandbox: required: - templateID - sandboxID - - clientID - envdVersion properties: templateID: @@ -519,19 +488,8 @@ components: alias: type: string description: Alias of the template - clientID: - type: string - deprecated: true - description: Identifier of the client envdVersion: $ref: '#/components/schemas/EnvdVersion' - envdAccessToken: - type: string - description: Access token used for envd communication - trafficAccessToken: - type: string - nullable: true - description: Token required for accessing sandbox via proxy. domain: type: string nullable: true @@ -540,7 +498,6 @@ components: required: - templateID - sandboxID - - clientID - startedAt - cpuCount - memoryMB @@ -558,10 +515,6 @@ components: sandboxID: type: string description: Identifier of the sandbox - clientID: - type: string - deprecated: true - description: Identifier of the client startedAt: type: string format: date-time @@ -572,9 +525,6 @@ components: description: Time when the sandbox will expire envdVersion: $ref: '#/components/schemas/EnvdVersion' - envdAccessToken: - type: string - description: Access token used for envd communication allowInternetAccess: type: boolean nullable: true @@ -598,15 +548,10 @@ components: $ref: '#/components/schemas/SandboxNetworkConfig' lifecycle: $ref: '#/components/schemas/SandboxLifecycle' - volumeMounts: - type: array - items: - $ref: '#/components/schemas/SandboxVolumeMount' ListedSandbox: required: - templateID - sandboxID - - clientID - startedAt - cpuCount - memoryMB @@ -624,10 +569,6 @@ components: sandboxID: type: string description: Identifier of the sandbox - clientID: - type: string - deprecated: true - description: Identifier of the client startedAt: type: string format: date-time @@ -648,10 +589,6 @@ components: $ref: '#/components/schemas/SandboxState' envdVersion: $ref: '#/components/schemas/EnvdVersion' - volumeMounts: - type: array - items: - $ref: '#/components/schemas/SandboxVolumeMount' SandboxesWithMetrics: required: - sandboxes @@ -705,10 +642,6 @@ components: $ref: '#/components/schemas/Mcp' iam: $ref: '#/components/schemas/SandboxIam' - volumeMounts: - type: array - items: - $ref: '#/components/schemas/SandboxVolumeMount' SandboxIam: type: object description: Sandbox workload identity configuration. A non-empty, valid tokens @@ -742,10 +675,6 @@ components: minimum: 0 default: 15 description: Time to live for the sandbox in seconds. - autoPause: - type: boolean - deprecated: true - description: Automatically pauses the sandbox after the timeout ConnectSandbox: type: object required: @@ -828,16 +757,10 @@ components: TeamMetric: description: Team metric with timestamp required: - - timestamp - timestampUnix - concurrentSandboxes - sandboxStartRate properties: - timestamp: - type: string - format: date-time - deprecated: true - description: Timestamp of the metric entry timestampUnix: type: integer format: int64 @@ -853,15 +776,9 @@ components: MaxTeamMetric: description: Team metric with timestamp required: - - timestamp - timestampUnix - value properties: - timestamp: - type: string - format: date-time - deprecated: true - description: Timestamp of the metric entry timestampUnix: type: integer format: int64 @@ -891,13 +808,6 @@ components: failedCount: type: integer description: Number of builds that failed to cancel - VolumeToken: - type: object - properties: - token: - type: string - required: - - token Template: required: - templateID @@ -913,7 +823,6 @@ components: - spawnCount - buildCount - envdVersion - - aliases - names - buildStatus properties: @@ -932,12 +841,6 @@ components: public: type: boolean description: Whether the template is public or only accessible by the team - aliases: - type: array - description: Aliases of the template - deprecated: true - items: - type: string names: type: array description: Names of the template (namespace/alias format when namespaced) @@ -977,7 +880,6 @@ components: - templateID - buildID - public - - aliases - names - tags properties: @@ -1000,76 +902,6 @@ components: description: Tags assigned to the template build items: type: string - aliases: - type: array - description: Aliases of the template - deprecated: true - items: - type: string - TemplateLegacy: - required: - - templateID - - buildID - - cpuCount - - memoryMB - - diskSizeMB - - public - - createdAt - - updatedAt - - createdBy - - lastSpawnedAt - - spawnCount - - buildCount - - envdVersion - - aliases - properties: - templateID: - type: string - description: Identifier of the template - buildID: - type: string - description: Identifier of the last successful build for given template - 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 @@ -1109,7 +941,6 @@ components: required: - templateID - public - - aliases - names - createdAt - updatedAt @@ -1123,12 +954,6 @@ components: public: type: boolean description: Whether the template is public or only accessible by the team - aliases: - type: array - description: Aliases of the template - deprecated: true - items: - type: string names: type: array description: Names of the template (namespace/alias format when namespaced) @@ -1225,15 +1050,6 @@ components: description: Tags to assign to the template build items: type: string - alias: - description: Alias of the template. Deprecated, use name instead. - type: string - maxLength: 128 - deprecated: true - teamID: - deprecated: true - type: string - description: Identifier of the team cpuCount: $ref: '#/components/schemas/CPUCount' memoryMB: @@ -1245,10 +1061,6 @@ components: alias: description: Alias of the template type: string - teamID: - deprecated: true - type: string - description: Identifier of the team cpuCount: $ref: '#/components/schemas/CPUCount' memoryMB: @@ -1708,37 +1520,6 @@ components: type: integer 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 @@ -1903,54 +1684,6 @@ components: maskedValueSuffix: type: string description: Suffix used in masked version of the token or key - Volume: - type: object - properties: - volumeID: - type: string - description: ID of the volume - name: - type: string - description: Name of the volume - required: - - volumeID - - name - VolumeAndToken: - type: object - properties: - volumeID: - type: string - description: ID of the volume - name: - type: string - description: Name of the volume - 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 - - token - NewVolume: - type: object - properties: - name: - type: string - description: Name of the volume - pattern: ^[a-zA-Z0-9_-]+$ - required: - - name tags: - name: sandboxes - name: snapshots @@ -1964,12 +1697,6 @@ paths: description: Get metrics for the team tags: - team-metrics - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/teamID' - in: query @@ -2012,12 +1739,6 @@ paths: description: Get the maximum metrics for the team in the given interval tags: - team-metrics - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/teamID' - in: query @@ -2067,12 +1788,6 @@ paths: description: Create a sandbox from the template tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] requestBody: required: true content: @@ -2099,12 +1814,6 @@ paths: description: List all sandboxes tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - name: metadata in: query @@ -2152,12 +1861,6 @@ paths: description: List metrics for given sandboxes tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - name: sandbox_ids in: query @@ -2190,12 +1893,6 @@ paths: description: Get sandbox logs tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' - in: query @@ -2257,12 +1954,6 @@ paths: description: Get a sandbox by id tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' responses: @@ -2284,12 +1975,6 @@ paths: description: Kill a sandbox tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' responses: @@ -2308,12 +1993,6 @@ paths: description: Get sandbox metrics tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' - in: query @@ -2356,12 +2035,6 @@ paths: description: Pause the sandbox tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' requestBody: @@ -2393,12 +2066,6 @@ paths: status means the request failed before any fork was attempted.' tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' requestBody: @@ -2433,12 +2100,6 @@ paths: TTL is only extended. tags: - sandboxes - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' requestBody: @@ -2476,12 +2137,6 @@ paths: from the time of the request. Calling this method multiple times overwrites the TTL, each time using the current timestamp as the starting point to measure the timeout duration. - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] tags: - sandboxes requestBody: @@ -2507,12 +2162,6 @@ paths: description: Update the network configuration for a running sandbox. Replaces the current egress rules with the provided configuration. Omitting field clears it. - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] tags: - sandboxes requestBody: @@ -2539,12 +2188,6 @@ paths: post: summary: Refresh sandbox description: Refresh the sandbox extending its time to live - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] tags: - sandboxes requestBody: @@ -2570,12 +2213,6 @@ paths: sandbox's lifetime. tags: - snapshots - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/sandboxID' requestBody: @@ -2606,12 +2243,6 @@ paths: description: List all snapshots for the team tags: - snapshots - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - name: sandboxID in: query @@ -2651,12 +2282,6 @@ paths: description: Create a new template tags: - templates - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] requestBody: required: true content: @@ -2685,13 +2310,6 @@ paths: description: List all templates tags: - templates - security: - - ApiKeyAuth: [] - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - in: query required: false @@ -2728,13 +2346,6 @@ paths: description: Get an upload link for a tar file containing build layer files tags: - templates - security: - - AccessTokenAuth: [] - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' - in: path @@ -2765,12 +2376,6 @@ paths: description: List all builds for a template tags: - templates - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' - $ref: '#/components/parameters/paginationNextToken' @@ -2795,13 +2400,6 @@ paths: description: Delete a template tags: - templates - security: - - ApiKeyAuth: [] - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' responses: @@ -2818,12 +2416,6 @@ paths: description: Start the build tags: - templates - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' - $ref: '#/components/parameters/buildID' @@ -2847,13 +2439,6 @@ paths: description: Update template tags: - templates - security: - - ApiKeyAuth: [] - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' requestBody: @@ -2882,13 +2467,6 @@ paths: description: Get template build info tags: - templates - security: - - AccessTokenAuth: [] - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' - $ref: '#/components/parameters/buildID' @@ -2934,13 +2512,6 @@ paths: description: Get template build logs tags: - templates - security: - - AccessTokenAuth: [] - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' - $ref: '#/components/parameters/buildID' @@ -3000,12 +2571,6 @@ paths: description: Assign tag(s) to a template build tags: - tags - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] requestBody: required: true content: @@ -3033,12 +2598,6 @@ paths: description: Delete multiple tags from templates tags: - tags - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] requestBody: required: true content: @@ -3063,12 +2622,6 @@ paths: description: List all tags for a template tags: - tags - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - $ref: '#/components/parameters/templateID' responses: @@ -3095,12 +2648,6 @@ paths: description: Check if template with given alias exists tags: - templates - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] parameters: - name: alias in: path diff --git a/reference/openapi/envd.yml b/reference/openapi/envd.yml index 918e65f0..c2eedb01 100644 --- a/reference/openapi/envd.yml +++ b/reference/openapi/envd.yml @@ -19,9 +19,6 @@ paths: /metrics: get: summary: Service stats - security: - - AccessTokenAuth: [] - - {} responses: '200': description: The resource usage metrics of the service @@ -35,9 +32,6 @@ paths: /envs: get: summary: Environment variables - security: - - AccessTokenAuth: [] - - {} responses: '200': description: Environment variables @@ -53,9 +47,6 @@ paths: summary: Download a file tags: - files - security: - - AccessTokenAuth: [] - - {} parameters: - $ref: '#/components/parameters/FilePath' - $ref: '#/components/parameters/User' @@ -120,9 +111,6 @@ paths: ' tags: - files - security: - - AccessTokenAuth: [] - - {} parameters: - $ref: '#/components/parameters/FilePath' - $ref: '#/components/parameters/User' @@ -148,9 +136,6 @@ paths: Source files are deleted after successful composition. tags: - files - security: - - AccessTokenAuth: [] - - {} requestBody: required: true content: @@ -176,7 +161,6 @@ paths: $ref: '#/components/responses/NotEnoughDiskSpace' operationId: composeFiles components: - securitySchemes: {} parameters: FilePath: name: path diff --git a/reference/openapi/markdown/connectSandbox.md b/reference/openapi/markdown/connectSandbox.md index e1fd6081..c4e6d545 100644 --- a/reference/openapi/markdown/connectSandbox.md +++ b/reference/openapi/markdown/connectSandbox.md @@ -37,10 +37,7 @@ Schema: `Sandbox` | `templateID` | `string` | yes | Identifier of the template from which is the sandbox created | | `sandboxID` | `string` | yes | Identifier of the sandbox | | `alias` | `string` | no | Alias of the template | -| `clientID` | `string` | yes | Identifier of the client | | `envdVersion` | `EnvdVersion` | yes | Version of the envd running in the sandbox | -| `envdAccessToken` | `string` | no | Access token used for envd communication | -| `trafficAccessToken` | `string` | no | Token required for accessing sandbox via proxy. | | `domain` | `string` | no | Base domain where the sandbox traffic is accessible | ### 201 @@ -56,10 +53,7 @@ Schema: `Sandbox` | `templateID` | `string` | yes | Identifier of the template from which is the sandbox created | | `sandboxID` | `string` | yes | Identifier of the sandbox | | `alias` | `string` | no | Alias of the template | -| `clientID` | `string` | yes | Identifier of the client | | `envdVersion` | `EnvdVersion` | yes | Version of the envd running in the sandbox | -| `envdAccessToken` | `string` | no | Access token used for envd communication | -| `trafficAccessToken` | `string` | no | Token required for accessing sandbox via proxy. | | `domain` | `string` | no | Base domain where the sandbox traffic is accessible | ### 400 diff --git a/reference/openapi/markdown/createSandbox.md b/reference/openapi/markdown/createSandbox.md index 178f820e..e786c16a 100644 --- a/reference/openapi/markdown/createSandbox.md +++ b/reference/openapi/markdown/createSandbox.md @@ -26,7 +26,6 @@ Schema: `NewSandbox` | `envVars` | `EnvVars` | no | | | `mcp` | `Mcp` | no | MCP configuration for the sandbox | | `iam` | `SandboxIam` | no | Sandbox workload identity configuration. A non-empty, valid tokens map enables workload identity for the sandbox. | -| `volumeMounts` | `array` | no | | ## Responses @@ -43,10 +42,7 @@ Schema: `Sandbox` | `templateID` | `string` | yes | Identifier of the template from which is the sandbox created | | `sandboxID` | `string` | yes | Identifier of the sandbox | | `alias` | `string` | no | Alias of the template | -| `clientID` | `string` | yes | Identifier of the client | | `envdVersion` | `EnvdVersion` | yes | Version of the envd running in the sandbox | -| `envdAccessToken` | `string` | no | Access token used for envd communication | -| `trafficAccessToken` | `string` | no | Token required for accessing sandbox via proxy. | | `domain` | `string` | no | Base domain where the sandbox traffic is accessible | ### 401 diff --git a/reference/openapi/markdown/createTemplate.md b/reference/openapi/markdown/createTemplate.md index 35b027f1..9cc0e0dd 100644 --- a/reference/openapi/markdown/createTemplate.md +++ b/reference/openapi/markdown/createTemplate.md @@ -16,8 +16,6 @@ Schema: `TemplateBuildRequestV3` | --- | --- | --- | --- | | `name` | `string` | no | Name of the template. Can include a tag with colon separator (e.g. "my-template" or "my-template:v1"). If tag is included, it will be treated as if the tag was provided in the tags array. | | `tags` | `array` | no | Tags to assign to the template build | -| `alias` | `string` | no | Alias of the template. Deprecated, use name instead. | -| `teamID` | `string` | no | Identifier of the team | | `cpuCount` | `CPUCount` | no | CPU cores for the sandbox | | `memoryMB` | `MemoryMB` | no | Memory for the sandbox in MiB | @@ -38,7 +36,6 @@ Schema: `TemplateRequestResponseV3` | `public` | `boolean` | yes | Whether the template is public or only accessible by the team | | `names` | `array` | yes | Names of the template | | `tags` | `array` | yes | Tags assigned to the template build | -| `aliases` | `array` | yes | Aliases of the template | ### 400 diff --git a/reference/openapi/markdown/getSandbox.md b/reference/openapi/markdown/getSandbox.md index 0fdb6e4b..856de9de 100644 --- a/reference/openapi/markdown/getSandbox.md +++ b/reference/openapi/markdown/getSandbox.md @@ -25,11 +25,9 @@ Schema: `SandboxDetail` | `templateID` | `string` | yes | Identifier of the template from which is the sandbox created | | `alias` | `string` | no | Alias of the template | | `sandboxID` | `string` | yes | Identifier of the sandbox | -| `clientID` | `string` | yes | Identifier of the client | | `startedAt` | `string` | yes | Time when the sandbox was started | | `endAt` | `string` | yes | Time when the sandbox will expire | | `envdVersion` | `EnvdVersion` | yes | Version of the envd running in the sandbox | -| `envdAccessToken` | `string` | no | Access token used for envd communication | | `allowInternetAccess` | `boolean` | no | Whether internet access was explicitly enabled or disabled for the sandbox. Null means it was not explicitly set. | | `domain` | `string` | no | Base domain where the sandbox traffic is accessible | | `cpuCount` | `CPUCount` | yes | CPU cores for the sandbox | @@ -39,7 +37,6 @@ Schema: `SandboxDetail` | `state` | `SandboxState` | yes | State of the sandbox | | `network` | `SandboxNetworkConfig` | no | | | `lifecycle` | `SandboxLifecycle` | no | Sandbox lifecycle policy returned by sandbox info. | -| `volumeMounts` | `array` | no | | ### 404 diff --git a/reference/openapi/markdown/getTeamMetricsMax.md b/reference/openapi/markdown/getTeamMetricsMax.md index 47b3519f..e4f4abe5 100644 --- a/reference/openapi/markdown/getTeamMetricsMax.md +++ b/reference/openapi/markdown/getTeamMetricsMax.md @@ -25,7 +25,6 @@ Schema: `MaxTeamMetric` | Field | Type | Required | Description | | --- | --- | --- | --- | -| `timestamp` | `string` | yes | Timestamp of the metric entry | | `timestampUnix` | `integer` | yes | Timestamp of the metric entry in Unix time (seconds since epoch) | | `value` | `number` | yes | The maximum value of the requested metric in the given interval | diff --git a/reference/openapi/markdown/getTemplate.md b/reference/openapi/markdown/getTemplate.md index 55c9bce8..d633d566 100644 --- a/reference/openapi/markdown/getTemplate.md +++ b/reference/openapi/markdown/getTemplate.md @@ -26,7 +26,6 @@ Schema: `TemplateWithBuilds` | --- | --- | --- | --- | | `templateID` | `string` | yes | Identifier of the template | | `public` | `boolean` | yes | Whether the template is public or only accessible by the team | -| `aliases` | `array` | yes | Aliases of the template | | `names` | `array` | yes | Names of the template (namespace/alias format when namespaced) | | `createdAt` | `string` | yes | Time when the template was created | | `updatedAt` | `string` | yes | Time when the template was last updated | diff --git a/reference/openapi/operations.json b/reference/openapi/operations.json index 43ae3694..774c82b7 100644 --- a/reference/openapi/operations.json +++ b/reference/openapi/operations.json @@ -6,7 +6,12 @@ "group": "sandboxes", "slug": "connect", "spec": "control-plane", - "markdown": "openapi/markdown/connectSandbox.md" + "markdown": "openapi/markdown/connectSandbox.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "createSandbox", @@ -15,7 +20,12 @@ "group": "sandboxes", "slug": "create", "spec": "control-plane", - "markdown": "openapi/markdown/createSandbox.md" + "markdown": "openapi/markdown/createSandbox.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "forkSandbox", @@ -24,7 +34,12 @@ "group": "sandboxes", "slug": "fork", "spec": "control-plane", - "markdown": "openapi/markdown/forkSandbox.md" + "markdown": "openapi/markdown/forkSandbox.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getSandbox", @@ -33,7 +48,12 @@ "group": "sandboxes", "slug": "get", "spec": "control-plane", - "markdown": "openapi/markdown/getSandbox.md" + "markdown": "openapi/markdown/getSandbox.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "killSandbox", @@ -42,7 +62,12 @@ "group": "sandboxes", "slug": "kill", "spec": "control-plane", - "markdown": "openapi/markdown/killSandbox.md" + "markdown": "openapi/markdown/killSandbox.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "listSandboxes", @@ -51,7 +76,12 @@ "group": "sandboxes", "slug": "list", "spec": "control-plane", - "markdown": "openapi/markdown/listSandboxes.md" + "markdown": "openapi/markdown/listSandboxes.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "listSandboxMetrics", @@ -60,7 +90,12 @@ "group": "sandboxes", "slug": "list-metrics", "spec": "control-plane", - "markdown": "openapi/markdown/listSandboxMetrics.md" + "markdown": "openapi/markdown/listSandboxMetrics.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getSandboxLogs", @@ -69,7 +104,12 @@ "group": "sandboxes", "slug": "logs", "spec": "control-plane", - "markdown": "openapi/markdown/getSandboxLogs.md" + "markdown": "openapi/markdown/getSandboxLogs.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getSandboxMetrics", @@ -78,7 +118,12 @@ "group": "sandboxes", "slug": "metrics", "spec": "control-plane", - "markdown": "openapi/markdown/getSandboxMetrics.md" + "markdown": "openapi/markdown/getSandboxMetrics.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "updateSandboxNetwork", @@ -87,7 +132,12 @@ "group": "sandboxes", "slug": "network", "spec": "control-plane", - "markdown": "openapi/markdown/updateSandboxNetwork.md" + "markdown": "openapi/markdown/updateSandboxNetwork.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "pauseSandbox", @@ -96,7 +146,12 @@ "group": "sandboxes", "slug": "pause", "spec": "control-plane", - "markdown": "openapi/markdown/pauseSandbox.md" + "markdown": "openapi/markdown/pauseSandbox.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "refreshSandbox", @@ -105,7 +160,12 @@ "group": "sandboxes", "slug": "refresh", "spec": "control-plane", - "markdown": "openapi/markdown/refreshSandbox.md" + "markdown": "openapi/markdown/refreshSandbox.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "setSandboxTimeout", @@ -114,7 +174,12 @@ "group": "sandboxes", "slug": "timeout", "spec": "control-plane", - "markdown": "openapi/markdown/setSandboxTimeout.md" + "markdown": "openapi/markdown/setSandboxTimeout.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "createSnapshot", @@ -123,7 +188,12 @@ "group": "snapshots", "slug": "create", "spec": "control-plane", - "markdown": "openapi/markdown/createSnapshot.md" + "markdown": "openapi/markdown/createSnapshot.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "listSnapshots", @@ -132,7 +202,12 @@ "group": "snapshots", "slug": "list", "spec": "control-plane", - "markdown": "openapi/markdown/listSnapshots.md" + "markdown": "openapi/markdown/listSnapshots.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "assignTemplateTags", @@ -141,7 +216,12 @@ "group": "tags", "slug": "assign", "spec": "control-plane", - "markdown": "openapi/markdown/assignTemplateTags.md" + "markdown": "openapi/markdown/assignTemplateTags.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "deleteTemplateTags", @@ -150,7 +230,12 @@ "group": "tags", "slug": "delete", "spec": "control-plane", - "markdown": "openapi/markdown/deleteTemplateTags.md" + "markdown": "openapi/markdown/deleteTemplateTags.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "listTemplateTags", @@ -159,7 +244,12 @@ "group": "tags", "slug": "list", "spec": "control-plane", - "markdown": "openapi/markdown/listTemplateTags.md" + "markdown": "openapi/markdown/listTemplateTags.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getTeamMetrics", @@ -168,7 +258,12 @@ "group": "team-metrics", "slug": "team-metrics", "spec": "control-plane", - "markdown": "openapi/markdown/getTeamMetrics.md" + "markdown": "openapi/markdown/getTeamMetrics.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getTeamMetricsMax", @@ -177,7 +272,12 @@ "group": "team-metrics", "slug": "team-metrics-max", "spec": "control-plane", - "markdown": "openapi/markdown/getTeamMetricsMax.md" + "markdown": "openapi/markdown/getTeamMetricsMax.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getTemplateByAlias", @@ -186,7 +286,12 @@ "group": "templates", "slug": "alias", "spec": "control-plane", - "markdown": "openapi/markdown/getTemplateByAlias.md" + "markdown": "openapi/markdown/getTemplateByAlias.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getTemplateBuildLogs", @@ -195,7 +300,12 @@ "group": "templates", "slug": "build-logs", "spec": "control-plane", - "markdown": "openapi/markdown/getTemplateBuildLogs.md" + "markdown": "openapi/markdown/getTemplateBuildLogs.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getTemplateBuildStatus", @@ -204,7 +314,12 @@ "group": "templates", "slug": "build-status", "spec": "control-plane", - "markdown": "openapi/markdown/getTemplateBuildStatus.md" + "markdown": "openapi/markdown/getTemplateBuildStatus.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "createTemplate", @@ -213,7 +328,12 @@ "group": "templates", "slug": "create", "spec": "control-plane", - "markdown": "openapi/markdown/createTemplate.md" + "markdown": "openapi/markdown/createTemplate.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "deleteTemplate", @@ -222,7 +342,12 @@ "group": "templates", "slug": "delete", "spec": "control-plane", - "markdown": "openapi/markdown/deleteTemplate.md" + "markdown": "openapi/markdown/deleteTemplate.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getTemplate", @@ -231,7 +356,12 @@ "group": "templates", "slug": "get", "spec": "control-plane", - "markdown": "openapi/markdown/getTemplate.md" + "markdown": "openapi/markdown/getTemplate.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "listTemplates", @@ -240,7 +370,12 @@ "group": "templates", "slug": "list", "spec": "control-plane", - "markdown": "openapi/markdown/listTemplates.md" + "markdown": "openapi/markdown/listTemplates.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "startTemplateBuild", @@ -249,7 +384,12 @@ "group": "templates", "slug": "start-build", "spec": "control-plane", - "markdown": "openapi/markdown/startTemplateBuild.md" + "markdown": "openapi/markdown/startTemplateBuild.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "updateTemplate", @@ -258,7 +398,12 @@ "group": "templates", "slug": "update", "spec": "control-plane", - "markdown": "openapi/markdown/updateTemplate.md" + "markdown": "openapi/markdown/updateTemplate.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getTemplateUploadUrl", @@ -267,7 +412,12 @@ "group": "templates", "slug": "upload-url", "spec": "control-plane", - "markdown": "openapi/markdown/getTemplateUploadUrl.md" + "markdown": "openapi/markdown/getTemplateUploadUrl.md", + "auth": { + "type": "apiKey", + "header": "X-API-Key", + "required": true + } }, { "operationId": "getEnvironmentHealth", @@ -276,7 +426,8 @@ "group": "environment", "slug": "health", "spec": "envd", - "markdown": "openapi/markdown/getEnvironmentHealth.md" + "markdown": "openapi/markdown/getEnvironmentHealth.md", + "auth": null }, { "operationId": "getEnvironmentMetrics", @@ -285,7 +436,12 @@ "group": "environment", "slug": "metrics", "spec": "envd", - "markdown": "openapi/markdown/getEnvironmentMetrics.md" + "markdown": "openapi/markdown/getEnvironmentMetrics.md", + "auth": { + "type": "apiKey", + "header": "X-Access-Token", + "required": false + } }, { "operationId": "getEnvironmentVariables", @@ -294,7 +450,12 @@ "group": "environment", "slug": "variables", "spec": "envd", - "markdown": "openapi/markdown/getEnvironmentVariables.md" + "markdown": "openapi/markdown/getEnvironmentVariables.md", + "auth": { + "type": "apiKey", + "header": "X-Access-Token", + "required": false + } }, { "operationId": "composeFiles", @@ -303,7 +464,12 @@ "group": "files", "slug": "compose", "spec": "envd", - "markdown": "openapi/markdown/composeFiles.md" + "markdown": "openapi/markdown/composeFiles.md", + "auth": { + "type": "apiKey", + "header": "X-Access-Token", + "required": false + } }, { "operationId": "downloadFile", @@ -312,7 +478,12 @@ "group": "files", "slug": "download", "spec": "envd", - "markdown": "openapi/markdown/downloadFile.md" + "markdown": "openapi/markdown/downloadFile.md", + "auth": { + "type": "apiKey", + "header": "X-Access-Token", + "required": false + } }, { "operationId": "uploadFile", @@ -321,6 +492,11 @@ "group": "files", "slug": "upload", "spec": "envd", - "markdown": "openapi/markdown/uploadFile.md" + "markdown": "openapi/markdown/uploadFile.md", + "auth": { + "type": "apiKey", + "header": "X-Access-Token", + "required": false + } } ] diff --git a/scripts/filter-public-openapi.py b/scripts/filter-public-openapi.py index e3cf100c..f077eab6 100755 --- a/scripts/filter-public-openapi.py +++ b/scripts/filter-public-openapi.py @@ -5,83 +5,9 @@ import argparse from pathlib import Path -from typing import Any - import yaml - -REMOVED_SCHEMAS = { - "CreatedAccessToken", - "NewVolume", - "NewAccessToken", - "SandboxVolumeMount", - "TemplateLegacy", - "Volume", - "VolumeAndToken", - "VolumeToken", -} - - -def remove_volume_mounts(value: Any) -> None: - if isinstance(value, dict): - properties = value.get("properties") - if isinstance(properties, dict): - properties.pop("volumeMounts", None) - for child in value.values(): - remove_volume_mounts(child) - elif isinstance(value, list): - for child in value: - remove_volume_mounts(child) - - -def remove_deprecated_contract(value: Any) -> None: - """Remove deprecated operations and fields from the generated public API.""" - if not isinstance(value, dict): - return - - paths = value.get("paths") - if isinstance(paths, dict): - for path_item in paths.values(): - if not isinstance(path_item, dict): - continue - for method in list(path_item): - operation = path_item[method] - if isinstance(operation, dict) and operation.get("deprecated") is True: - del path_item[method] - elif isinstance(operation, dict): - operation.pop("security", None) - - properties = value.get("properties") - if isinstance(properties, dict): - removed = { - name - for name, schema in properties.items() - if isinstance(schema, dict) and schema.get("deprecated") is True - } - for name in removed: - del properties[name] - required = value.get("required") - if isinstance(required, list): - value["required"] = [name for name in required if name not in removed] - - for child in value.values(): - if isinstance(child, dict): - remove_deprecated_contract(child) - elif isinstance(child, list): - for item in child: - remove_deprecated_contract(item) - - -def assert_no_removed_refs(value: Any) -> None: - if isinstance(value, dict): - ref = value.get("$ref") - if isinstance(ref, str) and ref.rsplit("/", 1)[-1] in REMOVED_SCHEMAS: - raise RuntimeError(f"public schema still references removed component: {ref}") - for child in value.values(): - assert_no_removed_refs(child) - elif isinstance(value, list): - for child in value: - assert_no_removed_refs(child) +from public_openapi import filter_public_openapi def main() -> None: @@ -90,16 +16,7 @@ def main() -> None: args = parser.parse_args() document = yaml.safe_load(args.path.read_text()) - remove_volume_mounts(document) - remove_deprecated_contract(document) - document.pop("security", None) - document.get("components", {}).pop("securitySchemes", None) - - schemas = document.get("components", {}).get("schemas", {}) - for name in REMOVED_SCHEMAS: - schemas.pop(name, None) - - assert_no_removed_refs(document) + filter_public_openapi(document) args.path.write_text(yaml.safe_dump(document, sort_keys=False)) diff --git a/scripts/generate-reference.py b/scripts/generate-reference.py index 7bbead30..066e3233 100644 --- a/scripts/generate-reference.py +++ b/scripts/generate-reference.py @@ -14,6 +14,8 @@ import yaml +from public_openapi import filter_public_openapi + ROOT = Path(__file__).resolve().parents[1] OUT = ROOT / "reference" HTTP_METHODS = {"get", "post", "put", "patch", "delete"} @@ -160,10 +162,61 @@ def render_operation_markdown(document: dict, record: dict) -> str: return "\n".join(lines).rstrip() + "\n" +def normalize_operation_auth( + document: dict, operation: dict, public_scheme_names: set[str] +) -> dict | None: + """Return one documentation-safe auth mechanism for an operation.""" + security = operation.get("security", document.get("security")) + if not security: + return None + if not isinstance(security, list) or not all( + isinstance(requirement, dict) for requirement in security + ): + raise SystemExit("Operation security must be an OpenAPI security array") + + anonymous_allowed = any(not requirement for requirement in security) + public_requirements = [ + requirement + for requirement in security + if requirement and set(requirement).issubset(public_scheme_names) + ] + public_schemes = { + name for requirement in public_requirements for name in requirement + } + if not public_schemes: + if anonymous_allowed: + return None + raise SystemExit( + "Authenticated public operation has no allowed public auth scheme" + ) + if len(public_schemes) != 1 or any( + len(requirement) != 1 for requirement in public_requirements + ): + raise SystemExit( + "Public reference supports exactly one normalized auth mechanism" + ) + + scheme_name = next(iter(public_schemes)) + scheme = document.get("components", {}).get("securitySchemes", {}).get(scheme_name) + if not isinstance(scheme, dict): + raise SystemExit(f"Public auth scheme is missing: {scheme_name}") + if scheme.get("type") != "apiKey" or scheme.get("in") != "header": + raise SystemExit(f"Public auth scheme {scheme_name} must be an apiKey header") + header = scheme.get("name") + if not isinstance(header, str) or not header: + raise SystemExit(f"Public auth scheme {scheme_name} has no header name") + return { + "type": "apiKey", + "header": header, + "required": not anonymous_allowed, + } + + def build_openapi(name: str, config: dict) -> list[dict]: source = ROOT / config["source"] document = yaml.safe_load(source.read_text()) assigned = config["operations"] + public_auth_schemes = set(config.get("publicAuthSchemes", [])) seen = set() output_paths = {} @@ -220,6 +273,9 @@ def build_openapi(name: str, config: dict) -> list[dict]: "slug": metadata["slug"], "spec": "control-plane" if name == "controlPlane" else "envd", "markdown": f"openapi/markdown/{metadata['id']}.md", + "auth": normalize_operation_auth( + document, operation, public_auth_schemes + ), } ) @@ -243,12 +299,7 @@ def build_openapi(name: str, config: dict) -> list[dict]: public["tags"] = [ {"name": group} for group in sorted({record["group"] for record in records}) ] - if "components" in public and "securitySchemes" in public["components"]: - public["components"]["securitySchemes"] = { - key: value - for key, value in public["components"]["securitySchemes"].items() - if key == "ApiKeyAuth" - } + filter_public_openapi(public, for_reference=True) destination = ( OUT / "openapi" diff --git a/scripts/public_openapi.py b/scripts/public_openapi.py new file mode 100644 index 00000000..c2f42b85 --- /dev/null +++ b/scripts/public_openapi.py @@ -0,0 +1,109 @@ +"""Shared filtering policies for generated public OpenAPI artifacts.""" + +from __future__ import annotations + +from typing import Any + +REMOVED_SCHEMAS = { + "CreatedAccessToken", + "NewVolume", + "NewAccessToken", + "SandboxVolumeMount", + "TemplateLegacy", + "Volume", + "VolumeAndToken", + "VolumeToken", +} + +CLIENT_REMOVED_PROPERTIES = {"volumeMounts"} +REFERENCE_REMOVED_PROPERTIES = CLIENT_REMOVED_PROPERTIES | { + "envdAccessToken", + "trafficAccessToken", +} + + +def remove_properties(value: Any, names: set[str]) -> None: + if isinstance(value, dict): + properties = value.get("properties") + if isinstance(properties, dict): + removed = set(properties) & names + for name in removed: + del properties[name] + required = value.get("required") + if isinstance(required, list): + value["required"] = [name for name in required if name not in removed] + for child in value.values(): + remove_properties(child, names) + elif isinstance(value, list): + for child in value: + remove_properties(child, names) + + +def remove_deprecated_contract(value: Any) -> None: + """Remove deprecated operations and fields from the generated public API.""" + if not isinstance(value, dict): + return + + paths = value.get("paths") + if isinstance(paths, dict): + for path_item in paths.values(): + if not isinstance(path_item, dict): + continue + for method in list(path_item): + operation = path_item[method] + if isinstance(operation, dict) and operation.get("deprecated") is True: + del path_item[method] + elif isinstance(operation, dict): + operation.pop("security", None) + + properties = value.get("properties") + if isinstance(properties, dict): + removed = { + name + for name, schema in properties.items() + if isinstance(schema, dict) and schema.get("deprecated") is True + } + for name in removed: + del properties[name] + required = value.get("required") + if isinstance(required, list): + value["required"] = [name for name in required if name not in removed] + + for child in value.values(): + if isinstance(child, dict): + remove_deprecated_contract(child) + elif isinstance(child, list): + for item in child: + remove_deprecated_contract(item) + + +def assert_no_removed_refs(value: Any) -> None: + if isinstance(value, dict): + ref = value.get("$ref") + if isinstance(ref, str) and ref.rsplit("/", 1)[-1] in REMOVED_SCHEMAS: + raise RuntimeError( + f"public schema still references removed component: {ref}" + ) + for child in value.values(): + assert_no_removed_refs(child) + elif isinstance(value, list): + for child in value: + assert_no_removed_refs(child) + + +def filter_public_openapi(document: dict, *, for_reference: bool = False) -> dict: + """Mutate and return a client-safe or stricter documentation-safe contract.""" + remove_properties( + document, + REFERENCE_REMOVED_PROPERTIES if for_reference else CLIENT_REMOVED_PROPERTIES, + ) + remove_deprecated_contract(document) + document.pop("security", None) + document.get("components", {}).pop("securitySchemes", None) + + schemas = document.get("components", {}).get("schemas", {}) + for name in REMOVED_SCHEMAS: + schemas.pop(name, None) + + assert_no_removed_refs(document) + return document diff --git a/scripts/test-reference-contract.py b/scripts/test-reference-contract.py index 4efeac14..c9178967 100644 --- a/scripts/test-reference-contract.py +++ b/scripts/test-reference-contract.py @@ -18,6 +18,52 @@ "/fsfreeze", "/fsthaw", } +FORBIDDEN_REFERENCE_PROPERTIES = { + "clientID", + "envdAccessToken", + "trafficAccessToken", + "volumeMounts", +} +FORBIDDEN_SECURITY_SCHEMES = { + "AccessTokenAuth", + "AdminApiKeyAuth", + "AdminTeamAuth", + "AuthProviderBearerAuth", + "AuthProviderTeamAuth", +} + + +def assert_local_refs_resolve(document: dict) -> None: + def visit(value): + if isinstance(value, dict): + reference = value.get("$ref") + if isinstance(reference, str) and reference.startswith("#/"): + resolved = document + for part in reference[2:].split("/"): + resolved = resolved[part.replace("~1", "/").replace("~0", "~")] + for child in value.values(): + visit(child) + elif isinstance(value, list): + for child in value: + visit(child) + + visit(document) + + +def assert_public_schema(value) -> None: + if isinstance(value, dict): + properties = value.get("properties") + if isinstance(properties, dict): + assert not (set(properties) & FORBIDDEN_REFERENCE_PROPERTIES) + assert not any( + isinstance(schema, dict) and schema.get("deprecated") is True + for schema in properties.values() + ) + for child in value.values(): + assert_public_schema(child) + elif isinstance(value, list): + for child in value: + assert_public_schema(child) def main() -> None: @@ -35,6 +81,22 @@ def main() -> None: } assert len(ids) == len(set(ids)), "operationId values must be unique" for operation in operations: + assert "auth" in operation, operation["operationId"] + auth = operation["auth"] + if operation["spec"] == "control-plane": + assert auth == { + "type": "apiKey", + "header": "X-API-Key", + "required": True, + }, operation["operationId"] + elif operation["path"] == "/health": + assert auth is None, operation["operationId"] + else: + assert auth == { + "type": "apiKey", + "header": "X-Access-Token", + "required": False, + }, operation["operationId"] markdown = REFERENCE / operation["markdown"] assert markdown.is_file(), operation["operationId"] rendered = markdown.read_text() @@ -72,6 +134,24 @@ def main() -> None: ) assert "x-not-implemented" not in rendered_specs assert "SandboxEgressProxyConfig" not in rendered_specs + for forbidden in FORBIDDEN_REFERENCE_PROPERTIES | FORBIDDEN_SECURITY_SCHEMES: + assert forbidden not in rendered_specs + + for name in ("openapi/control-plane.yml", "openapi/envd.yml"): + document = yaml.safe_load((REFERENCE / name).read_text()) + assert "securitySchemes" not in document.get("components", {}) + for path_item in document["paths"].values(): + for method, operation in path_item.items(): + if method.lower() in {"get", "post", "put", "patch", "delete"}: + assert "security" not in operation + assert_public_schema(document) + assert_local_refs_resolve(document) + + rendered_markdown = "\n".join( + path.read_text() for path in (REFERENCE / "openapi/markdown").glob("*.md") + ) + for forbidden in FORBIDDEN_REFERENCE_PROPERTIES: + assert forbidden not in rendered_markdown javascript_files = { str(path.relative_to(REFERENCE)) @@ -83,14 +163,10 @@ def main() -> None: assert not any("parseOutput" in path for path in javascript_files) assert not any("extractError" in path for path in javascript_files) - list_sandboxes = ( - REFERENCE / "openapi/markdown/listSandboxes.md" - ).read_text() + list_sandboxes = (REFERENCE / "openapi/markdown/listSandboxes.md").read_text() assert "Metadata query used to filter the sandboxes" in list_sandboxes assert "### 200" in list_sandboxes - create_sandbox = ( - REFERENCE / "openapi/markdown/createSandbox.md" - ).read_text() + create_sandbox = (REFERENCE / "openapi/markdown/createSandbox.md").read_text() assert "Schema: `NewSandbox`" in create_sandbox