Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions openapi-public.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1586,7 +1586,8 @@ paths:
$ref: '#/components/schemas/filesystem.CreateWatcherResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
servers:
- *id004
/filesystem.Filesystem/GetWatcherEvents:
Expand Down Expand Up @@ -1654,7 +1655,8 @@ paths:
$ref: '#/components/schemas/filesystem.ListDirResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
servers:
- *id004
/filesystem.Filesystem/MakeDir:
Expand Down Expand Up @@ -1688,7 +1690,8 @@ paths:
$ref: '#/components/schemas/filesystem.MakeDirResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
servers:
- *id004
/filesystem.Filesystem/Move:
Expand Down Expand Up @@ -1722,7 +1725,8 @@ paths:
$ref: '#/components/schemas/filesystem.MoveResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
servers:
- *id004
/filesystem.Filesystem/Remove:
Expand Down Expand Up @@ -1756,7 +1760,8 @@ paths:
$ref: '#/components/schemas/filesystem.RemoveResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
servers:
- *id004
/filesystem.Filesystem/RemoveWatcher:
Expand Down Expand Up @@ -1824,7 +1829,8 @@ paths:
$ref: '#/components/schemas/filesystem.StatResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
servers:
- *id004
/filesystem.Filesystem/WatchDir:
Expand All @@ -1849,7 +1855,8 @@ paths:
$ref: '#/components/schemas/filesystem.WatchDirResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
parameters:
- &id006
name: Connect-Protocol-Version
Expand Down Expand Up @@ -2052,7 +2059,8 @@ paths:
$ref: '#/components/schemas/process.StartResponse'
'502': *id003
security:
- *id005
- SandboxAccessTokenAuth: []
SandboxUserAuth: []
parameters:
- *id006
- *id007
Expand Down Expand Up @@ -2264,6 +2272,12 @@ components:
(on connect), [POST /sandboxes/{sandboxID}/resume](/docs/api-reference/sandboxes/resume-a-sandbox)
(on resume), and [GET /sandboxes/{sandboxID}](/docs/api-reference/sandboxes/get-a-sandbox)
(for running or paused sandboxes).'
SandboxUserAuth:
type: http
scheme: basic
description: Optional system user for the operation. Sets file ownership and
resolves relative paths. Pass the desired username with no password. Defaults
to the sandbox's default user when omitted.
parameters:
FilePath:
name: path
Expand Down
53 changes: 53 additions & 0 deletions scripts/generate_openapi_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@

# Security scheme name for envd endpoints (must not collide with platform's AccessTokenAuth)
SANDBOX_AUTH_SCHEME = "SandboxAccessTokenAuth"
SANDBOX_USER_SCHEME = "SandboxUserAuth"

# ---------------------------------------------------------------------------
# Proto parsing — auto-detect streaming RPCs
Expand Down Expand Up @@ -575,6 +576,17 @@ def setup_sandbox_auth_scheme(spec: dict[str, Any]) -> None:
"and [GET /sandboxes/{sandboxID}](/docs/api-reference/sandboxes/get-a-sandbox) (for running or paused sandboxes)."
),
}
# Optional Basic auth for setting the system user on sandbox operations.
# The SDK sends: Authorization: Basic <base64("username:")>
schemes[SANDBOX_USER_SCHEME] = {
"type": "http",
"scheme": "basic",
"description": (
"Optional system user for the operation. Sets file ownership and resolves "
"relative paths. Pass the desired username with no password. "
"Defaults to the sandbox's default user when omitted."
),
}


# Mapping of (path, method) to desired operationId for the public docs.
Expand Down Expand Up @@ -616,6 +628,20 @@ def add_operation_ids(spec: dict[str, Any]) -> None:
"/process.Process/StreamInput",
}

# Connect-RPC endpoints that accept an optional user via Authorization header.
# The SDK sends: Authorization: Basic <base64("username:")>
# This is not part of the protobuf message — it must be added as an OpenAPI parameter.
USER_HEADER_ENDPOINTS = {
"/process.Process/Start",
"/filesystem.Filesystem/ListDir",
"/filesystem.Filesystem/MakeDir",
"/filesystem.Filesystem/Move",
"/filesystem.Filesystem/Remove",
"/filesystem.Filesystem/Stat",
"/filesystem.Filesystem/WatchDir",
"/filesystem.Filesystem/CreateWatcher",
}


def fix_spec_issues(spec: dict[str, Any]) -> None:
"""Fix known discrepancies between the source spec and the live API.
Expand Down Expand Up @@ -1113,6 +1139,32 @@ def _singularize(word: str) -> str:
print(f" {f}")


def add_user_auth_security(spec: dict[str, Any]) -> None:
"""Add optional Basic auth (user) security to Connect-RPC endpoints that support it.

The sandbox resolves user from an Authorization: Basic header where the
username encodes the desired OS user. This is a transport-level concern
not captured in the proto definitions, so we inject it during post-processing.

Endpoints that support user get two security options (OR):
- SandboxAccessTokenAuth only (uses default user)
- SandboxAccessTokenAuth + SandboxUserAuth (custom user)
"""
paths = spec.get("paths", {})
count = 0
for ep_path in USER_HEADER_ENDPOINTS:
path_item = paths.get(ep_path, {})
op = path_item.get("post")
if not op:
continue
op["security"] = [
{SANDBOX_AUTH_SCHEME: [], SANDBOX_USER_SCHEME: []},
]
count += 1
if count:
print(f"==> Added optional user auth (Basic) to {count} Connect-RPC endpoints")


def _strip_supabase_security(path_item: dict[str, Any]) -> None:
"""Remove Supabase security entries from all operations in a path item.

Expand Down Expand Up @@ -1455,6 +1507,7 @@ def main() -> None:
setup_sandbox_auth_scheme(merged)
add_operation_ids(merged)
fix_spec_issues(merged)
add_user_auth_security(merged)

# Remove internal/unwanted paths
filter_paths(merged)
Expand Down
Loading