Repository navigation
Add secrets management and secret delivery to sandboxes - #139
Merged
Merged
Conversation
Adds client.secrets (sync and async) for the API-key routes: public key, list, create, get, update, add_version, delete and delete_version. Values are encrypted client-side as a compact JWE (RSA-OAEP-256 + A256GCM) bound to the secret's encryption context, after checking the key's policy and RFC 7638 key ID. Secrets resolve by ID or exact name. Sandbox image launches accept `secrets` for the startup command, and exec/processes.start accept `secrets`, starting through the control API and waiting through the runtime. Sandboxes and snapshots expose contains_secrets. Control API errors honour the server's `retryable` flag and a machine code sent as `error`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhbFMc29f5JN5JxCciajJV
AHarmlessPyro
marked this pull request as ready for review
October 12, 2026 02:43
- Treat only 8-4-4-4-12 keys as secret IDs, so UUID-like names fall back to the name search instead of failing on the server's 400. - A sandbox create with secrets whose outcome is unknown raises a non-retryable secret_process_outcome_unknown: a sandbox may be running its startup command with the secrets. - Normalize the server's SECRET_PROCESS_OUTCOME_UNKNOWN to the documented code, keeping the server body in details. - Add the sandbox and secret context to control error messages, as the CLI does. - Without a server retryable flag, SECRETS_NOT_CONFIGURED and other codes retrying can't fix are not retryable. - Reject use_shell with secrets, accept processId in start responses, and check secret names, env names, versions and list bounds locally. - Document SECRETS_ADMIN_REQUIRED, SECRETS_FEATURE_DISABLED, SECRETS_NOT_CONFIGURED and SECRET_STARTUP_UNSUPPORTED. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Interrupting an in-flight HTTPX request needed a timer thread that shut down sockets through trace hooks, with special cases for shared HTTP/2 connections, TLS stream swaps and trickled responses, plus deadline-aware copies of the token refresh and control request paths. The sync secret process wait now checks its limit between requests and caps each request's HTTP timeouts at the time left, like the async client's transport. A trickled response or a token refresh can overrun the limit; the README points to the async client for a strict one. The control request path is back to master's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NikhilShahi
approved these changes
Oct 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds sandbox secrets to the SDK, following the
hx vm secretsCLI (metlo-labs/browserctrl#1519) and reconciled against the current server and CLI on master. It needs the server's secrets stack deployed, and the secrets feature enabled for the team.client.secrets(sync and async)getby ID makes one request.encryptionContext. The API only receives the envelope.kid.cryptography, which is a new dependency (>=41.0.0). Most of thepoetry.lockdiff is that package and its dependencies, locked per Python version.secrets.manage. Updating, rotating or deleting existing secrets through the SDK needs bothsecrets.readandsecrets.manage, because the SDK resolves metadata and encryption context, even for known IDs.hx auth loginsession instead of an API key, only team owners and admins can use/secrets. Other members getSECRETS_ADMIN_REQUIRED.strorbytes, used byte-for-byte, 1 byte to 32 KiB, UTF-8, no NUL"latest"page >= 1andlimit1 to 100createwith a value checks the value, then fetches and verifies the key, before creating the secret. A bad value or key leaves nothing behind.secret_value_not_stored. The error names the secret and points toadd_version, because retryingcreatewould fail with "name taken".Using secrets
sandboxes.create:secretsgo to the image's startup command, so they work for image launches only. Snapshot launches with secrets are rejected locally.SECRET_STARTUP_UNSUPPORTED; pass the secrets toexecorprocesses.startinstead.secret_process_outcome_unknown: a sandbox may already be running its startup command with the secrets. The CLI does the same.POST /api/sandbox/:id/processes.timeout_secbecomestimeoutMs, and a positivetimeout_sectakes precedence overtimeout_ms, as in ordinary command execution.use_shell, env names that are already set byenv, and invalid env names are rejected locally.process.idorprocessIdin the start response.execcloses stdin, ignoring a 409 from a command that already exited.execwait through the runtime in chunks below the runtime request timeout, and keep waiting after a 408.wait()andresult()have no local limit by default. A positive wait timeout limits the local wait, leaves the command running, and lets the handle be waited on again.secret_process_failedwith{processId, sandboxId}.secret_process_outcome_unknown, which is not retryable because the command may be running:SECRET_PROCESS_OUTCOME_UNKNOWN, whose body stays indetailsSandboxDetail/Sandbox/SandboxSnapshotSummaryexposecontains_secrets.Errors (all control requests)
retryableflag overrides the status-code default.SECRETS_NOT_CONFIGUREDis no longer retried.errornext to amessage(e.g.API_KEY_SCOPE_DENIED) becomesHyperbrowserError.code.detailsstill carries the full body (requiredScopes,sandboxId,causeCode, …).(sandbox <id>)and(<env>: secret <id>, version <n>)when the body includes them, as the CLI does.SECRETS_FEATURE_DISABLED: the 403/secretsreturns when the team lacks the feature. metlo-labs/browserctrl#1549 adds the same code to process starts, creates and snapshot restores.Testing
tests/test_secrets.pycovers:retryableparsing in both the control request path and the transporttests/test_typing_contract.pyandtests/test_typed_request_surface.pypass with mypy 1.14.1 and pyright from the Poetry environment. The typecheck fixtures cover the new calls.ruff checkis clean. CI exercises Python 3.8 through 3.14.🤖 Generated with Claude Code
https://claude.ai/code/session_01NhbFMc29f5JN5JxCciajJV
Note
High Risk
Introduces client-side crypto, secret injection into sandbox processes, and non-retryable ambiguous-outcome errors; mistakes could leak credentials into environments or cause unsafe retries after partial API success.
Overview
Adds
client.secretson sync and async clients to create, version, list, update, and delete team secrets. Values are encrypted locally (JWE via newcryptographydependency) before upload; the API never returns plaintext.Sandboxes can receive secrets at image create (
secretson startup) and insandbox.exec/sandbox.processes.start, which routes secret-bearing starts through the control API instead of the runtime SSE path. Process handles expose resolved secret versions and use chunkedwait()with stricter failure semantics (secret_process_outcome_unknown,secret_process_failed). Models addcontains_secretson sandboxes/snapshots and validation for secret references.Control-plane error parsing now honors server
retryableflags, maps machine codes fromerrorfields, and enriches messages with sandbox/secret context. README documents limits, scopes, and operational caveats.Reviewed by Cursor Bugbot for commit 64cfebf. Bugbot is set up for automated code reviews on this repo. Configure here.