Repository navigation
Conversation
7cd039d to
b69cdc1
Compare
ebe76ec to
5660716
Compare
5660716 to
697020d
Compare
|
|
ebaec87 to
2891cbb
Compare
2768536 to
0d69f5d
Compare
0d69f5d to
b463e46
Compare
ae0ea59 to
22f5e73
Compare
tkkhq
left a comment
There was a problem hiding this comment.
Existing sandbox contract mismatches remain at this head:
- Setting
idle_timeout_secondsstill adds that field to the create request atsrc/volcano_sdk/sandboxes.py:68–70, butCreateSandboxSessionRequestforbids extra properties and does not define it. Remove the option fromSandboxCreateOptionsand request construction, and update their tests. Existing discussion. - An unlimited local session returns
expires_at: null, which the contract permits. Session construction and refresh still pass it to the timestamp decoder atsrc/volcano_sdk/sandbox_session.py:89,166; the decoder rejects None withTypeError. Accept None in the public session type and decoder, and add regression coverage. Existing discussion.
cfc7dfe to
2390c31
Compare
2390c31 to
2f166c8
Compare
AI review: 1 P0-P2 finding(s)Syncs the OpenAPI contract and generated client for autofix runs, auth-proxy headers, and health statuses. One logout regression can leave local credentials active after a failed sign-out. 🟡 P2
⚪ P3
Not checked: The AI review read the diff, the code, and the PR's text; it ran no tests or builds and read nothing outside this repository.
Push a fix, or have a collaborator with write access (including the PR author) add the Human review: needed
Decide the points above: approve to accept them, or request changes. Suggested reviewer: |
| return response_204 | ||
|
|
||
| if response.status_code == 401: | ||
| response_401 = Error.from_dict(response.json()) |
There was a problem hiding this comment.
⚪ P3: A logout 401 that doesn't match the Error schema now raises a raw exception from sign_out and leaves the local session set
The regenerated auth_logout._parse_response now runs Error.from_dict(response.json()) on a 401. AuthIdentityTransport.auth_logout (src/volcano_sdk/_transport_auth_identity.py:365) still calls auth_logout.sync_detailed and passes the result to parsed_response.
If a 401 body is not JSON, or has no error key (for example a 401 from a gateway or proxy), the parser raises JSONDecodeError or KeyError. Before this change, a 401 had no parser: parsed was None and parsed_response decoded the body leniently.
_sign_out_captured (src/volcano_sdk/_auth_requests.py:229-240) only catches VolcanoError. The raw exception therefore skips clear_session_if_current, and client.auth.sign_out() fails without clearing the local session. Previously it raised AuthenticationError after clearing the session.
The documented contract says the Volcano server returns Error JSON on a 401, so the trigger needs a 401 that breaks that contract. The fix is to switch the logout adapter to generated_request(...) plus unparsed_response, the pattern auth_forgot_password, auth_reset_password, and auth_signup_anonymous already use.
Found by claude-opus-5-5
| return response_204 | ||
|
|
||
| if response.status_code == 401: | ||
| response_401 = Error.from_dict(response.json()) |
There was a problem hiding this comment.
🟡 P2: Preserve local sign-out cleanup when a 401 body cannot be parsed
If an auth proxy returns HTTP 401 with an empty or HTML body, this new parser raises JSONDecodeError. AuthIdentityTransport.auth_logout() calls it directly, while AuthRequests._sign_out_captured() catches only VolcanoError, so client.auth.sign_out() skips clear_session_if_current() and retains local credentials, violating the cleanup guarantee in docs/authentication.md. Previously this response reached normal error handling and session cleanup. Preserve raw error handling in the handwritten logout adapter or translate parsing failures into TransportError.
Found by gpt-6.1-sol
Summary
API change report
Public API
New Endpoints: 3
GET /projects/{id}/autofix-runs
GET /projects/{id}/autofix-runs/{runId}
POST /projects/{id}/issues/{issueId}/autofix-runs
Deleted Endpoints: None
Modified Endpoints: 20
POST /auth/forgot-password
POST /auth/logout
POST /auth/oauth/exchange
GET /auth/password-policy
POST /auth/refresh
POST /auth/reset-password
POST /auth/signin
or account banned/deleted
' to 'Unauthorized - Invalid credentials, invalid/tampered/revoked anon key,
or account banned/deleted; or invalid auth proxy metadata
'
POST /auth/signup
' to 'Unauthorized - Invalid, tampered, revoked, or wrong-project anon key.
Also returned for invalid, repeated, or under-scoped auth proxy metadata
(
invalid auth proxy metadata).'
POST /auth/signup-anonymous
POST /functions/{functionId}/invoke
visibilitydecides which credentialsmay call it, and the function does not run for any other. A
privatefunction answers every credential but a service key exactly as a
missing one, with 404, so its name and id cannot be discovered; an anon
key on an
authenticatedfunction gets 403. An SDK calling by nameresolves it first through
GET /functions/resolve, which answers thatanon key with 404 instead.
With Service Key (admin/background operations, every visibility):
With Auth User Token (user-facing,
authenticatedorpublicfunctions):__volcano_authcontext:With Anon Key (
publicfunctions only):functions.invokevisibility: public__volcano_auth)Transport and CORS:
POST
{payload: ...}contract, including for functions whose DNS ingress isconfigured in HTTP mode.
invoke_url. It is on adifferent domain from this API, so it cannot be derived from the API host.
/. HTTP-mode DNS ingress accepts GET,HEAD, POST, PUT, PATCH, and DELETE at
/and nested paths.POST, OPTIONS. HTTP-mode DNSpreflight advertises
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS.http_auth_mode: noneapplies only to public HTTP-mode DNS ingress; thisdirect operation always requires a Volcano credential.
Durable functions are not invocable here. A durable function's id
answers 404, whatever its visibility, because a synchronous call would
run it with no execution record, no idempotency and no concurrency
accounting. Start one with
POST /durable-functions/{functionId}/executions.' to 'Invoke a function. The function's
visibilitydecides which credentialsmay call it, and the function does not run for any other. A
privatefunction answers every credential but a service key exactly as a
missing one, with 404, so its name and id cannot be discovered; an anon
key on an
authenticatedfunction gets 403. An SDK calling by nameresolves it first through
GET /functions/resolve, which answers thatanon key with 404 instead.
With Service Key (admin/background operations, every visibility):
With Auth User Token (user-facing,
authenticatedorpublicfunctions):__volcano_authcontext:With Anon Key (
publicfunctions only):functions.invokevisibility: public__volcano_auth)Reserved payload keys: the top-level
__volcano_authand__volcano_httpkeys are reserved. Volcano removes caller-suppliedvalues for both before the function runs; only an auth user token adds
a server-built
__volcano_auth, and only HTTP-mode ingress sets__volcano_http: true.Transport and CORS:
POST
{payload: ...}contract, including for functions whose DNS ingress isconfigured in HTTP mode.
invoke_url. It is on adifferent domain from this API, so it cannot be derived from the API host.
/. HTTP-mode DNS ingress accepts GET,HEAD, POST, PUT, PATCH, and DELETE at
/and nested paths.POST, OPTIONS. HTTP-mode DNSpreflight advertises
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS.http_auth_mode: noneapplies only to public HTTP-mode DNS ingress; thisdirect operation always requires a Volcano credential.
Durable functions are not invocable here. A durable function's id
answers 404, whatever its visibility, because a synchronous call would
run it with no execution record, no idempotency and no concurrency
accounting. Start one with
POST /durable-functions/{functionId}/executions.'
If invoked with auth user token, Volcano automatically injects
__volcano_authcontext:' to 'Payload to send to the function.
If invoked with auth user token, Volcano automatically injects
__volcano_authcontext:The top-level
__volcano_authand__volcano_httpkeys are reserved;Volcano removes caller-supplied values for both.
'
GET /projects
POST /projects
GET /projects/{id}
PATCH /projects/{id}
POST /projects/{id}/frontends
If a frontend with the same name already exists in the project, this operation updates that
frontend using the uploaded archive and starts a new deployment. A deployment that starts
immediately returns
status: provisioning, then transitions toactive,degraded, orfailed. If another deployment is running, the response preserves the frontend's current statusand exposes the queued deployment through
pending_deployment_id.Existing frontend traffic continues to use an available runtime while the new deployment builds
and provisions. Each deployment publishes its own static assets before the runtimes switch to its
build, and the live build's assets keep serving until the new deployment is live, so a page loaded
mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes
back on the build they were running, leaves the frontend
activeon the previous deployment, andrecords the attempted deployment as failed.
degradedmeans the runtime remains available butedge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a
given frontend, while independent frontends and projects can deploy concurrently.
For monorepos, provide
app_rootas a relative path from the uploaded archive rootto the Next.js app that should be built. Omit it for single-app archives.
Supported frontend environments are Next.js 15.x and 16.x with Node.js
22.x or 24.x. The Node.js runtime is inferred from
package.jsonengines.node; if omitted, Volcano uses Node.js 22.x.The selected Node.js family must also satisfy the installed Next.js package's
engines.nodeconstraint. Volcano tests Next 15.5.27 (^18.18.0 || ^19.8.0 || >=20.0.0) and Next 16.4.0 (>=20.9.0).Source archive size is enforced by the API with
SOURCE_ARCHIVE_SIZE_LIMIT_MB; the CLIdoes not apply its own source archive size limit. After the final container images are
built, the publish build enforces
LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MBbefore pushing.This operation is limited by plan-based frontend deployment quotas (
FREE_FRONTEND_DEPLOYMENTS,PRO_FRONTEND_DEPLOYMENTS).Each project can contain up to 10,000 frontends regardless of plan.
' to 'Creates and deploys a frontend for the project.
If a frontend with the same name already exists in the project, this operation updates that
frontend using the uploaded archive and starts a new deployment. A deployment that starts
immediately returns
status: provisioning, then transitions toactive,degraded, orfailed. If another deployment is running, the response preserves the frontend's current statusand exposes the queued deployment through
pending_deployment_id.Existing frontend traffic continues to use an available runtime while the new deployment builds
and provisions. Each deployment publishes its own static assets before the runtimes switch to its
build, and the live build's assets keep serving until the new deployment is live, so a page loaded
mid-deployment resolves its assets whichever build served it. A failed redeploy puts the runtimes
back on the build they were running, leaves the frontend
activeon the previous deployment, andrecords the attempted deployment as failed.
degradedmeans the runtime remains available butedge synchronization requires recovery; Volcano retries the edge step without rebuilding. Only one deployment may run for a
given frontend, while independent frontends and projects can deploy concurrently.
For monorepos, provide
app_rootas a relative path from the uploaded archive rootto the Next.js app that should be built. Omit it for single-app archives.
Supported frontend environments are Next.js 15.x and 16.x with Node.js
22.x or 24.x. The Node.js runtime is inferred from
package.jsonengines.node; if omitted, Volcano uses Node.js 22.x.The selected Node.js family must also satisfy the installed Next.js package's
engines.nodeconstraint. Volcano tests Next 15.5.28 (^18.18.0 || ^19.8.0 || >=20.0.0) and Next 16.4.2 (>=20.9.0).Source archive size is enforced by the API with
SOURCE_ARCHIVE_SIZE_LIMIT_MB; the CLIdoes not apply its own source archive size limit. After the final container images are
built, the publish build enforces
LAMBDA_TARGET_CONTAINER_SIZE_LIMIT_MBbefore pushing.This operation is limited by plan-based frontend deployment quotas (
FREE_FRONTEND_DEPLOYMENTS,PRO_FRONTEND_DEPLOYMENTS).Each project can contain up to 10,000 frontends regardless of plan.
'
GET /projects/{id}/health
deployed resources. The endpoint does not run live provider probes.
A successful request returns 200 even when the project status is
unhealthy; transport and authorization failures use HTTP errors.' to 'Returns a fast control-plane health snapshot for the project and its
deployed resources. The endpoint does not run live provider probes.
A successful request returns 200 even when the project status is
degradedorcritical; transport and authorization failures use HTTPerrors.
'
DELETE /projects/{id}/logo
POST /projects/{id}/logo
POST /projects/{id}/service-keys
containing only blank strings, the key is granted full access ([""])
for backward compatibility. Provide an explicit list (e.g.
["functions.invoke", "locks.manage"]) to restrict the key; ""
grants everything. Scope enforcement applies to function invocation,
storage object operations, and project locks.
' to 'Optional least-privilege scope for the key. When omitted, empty, or
containing only blank strings, the key is granted full access ([""])
for backward compatibility. Provide an explicit list (e.g.
["functions.invoke", "locks.manage"]) to restrict the key; ""
grants everything. Scope enforcement applies to function invocation,
storage object operations, project locks, and server-side auth proxy
metadata (auth.signin, auth.signup, auth.refresh, auth.logout,
auth.password_reset).
'
DELETE /projects/{id}/storage/buckets/{bucketName}/policies/{policyId}
Validation