Agentic Sandbox Client Python
This Python client provides a simple, high-level interface for creating and interacting with sandboxes managed by the Agent Sandbox controller. It's designed to be used as a context manager, ensuring that sandbox resources are properly created and cleaned up.
It supports a scalable, cloud-native architecture using Kubernetes Gateways and a specialized Router, while maintaining a convenient Tunnel Mode for local testing.
Architecture
The client operates in four connectivity modes:
- Gateway Mode: Traffic flows from the Client -> Cloud Load Balancer (Gateway) -> Router Service -> Sandbox Pod. This supports external ingress via Gateway API.
- Tunnel Mode: Traffic flows from Localhost ->
kubectl port-forward-> Router Service -> Sandbox Pod. This requires no public IP and works on Kind/Minikube for local development. - In-Cluster Mode: The client connects directly to the sandbox pod (via pod IP or cluster DNS), bypassing the router. Intended for workloads running inside the cluster.
- Direct URL Mode: The client connects directly to a provided
api_url, bypassing discovery. This is useful when connecting through a custom domain or a manually specified router URL.
Prerequisites
- A running Kubernetes cluster.
- The Agent Sandbox Controller installed.
kubectlinstalled and configured locally.
Setup: Deploying the Router
Before using the client in Gateway Mode or Tunnel Mode, deploy the sandbox-router into your cluster.
-
Deploy the Router:
Follow the instructions in sandbox-router to deploy the router using the manifests in sandbox-router/deploy. (Note: If you installed a specific client release tag, replace
mainin these URLs with the corresponding tag.) -
Create a Sandbox Warmpool:
Ensure a
SandboxWarmPoolexists in your target namespace. The test_client.py uses the python-runtime-sandbox image.kubectl apply -f python-sandbox-warmpool.yaml
Installation
-
Create a virtual environment:
python3 -m venv .venv source .venv/bin/activate
-
Install Agent Sandbox Client
-
Option 1: Install from PyPI (Recommended):
The package is available on PyPI as
k8s-agent-sandbox.pip install k8s-agent-sandbox
If you are using tracing with GCP, install with the optional tracing dependencies:
pip install "k8s-agent-sandbox[tracing]"
-
Option 2: Install from source via git:
# Replace "main" with a specific version tag (e.g., "v0.1.0") from # https://github.com/kubernetes-sigs/agent-sandbox/releases to pin a version tag. export VERSION="main" pip install "git+https://github.com/kubernetes-sigs/agent-sandbox.git@${VERSION}#subdirectory=clients/python/agentic-sandbox-client"
Note: This package uses
setuptools-scmfor dynamic versioning. For Option 2 and Option 3, when installing locally, you may notice the version increment if your local repository has uncommitted changes or is ahead of the last tagged release. This is expected behavior to ensure unique versioning during development. -
Option 3: Install from source in editable mode:
If you have not already done so, first clone this repository:
cd ~ git clone https://github.com/kubernetes-sigs/agent-sandbox.git cd agent-sandbox/clients/python/agentic-sandbox-client
And then install the agentic-sandbox-client into your activated .venv:
pip install -e .
If you are using tracing with GCP, install with the optional tracing dependencies:
pip install -e ".[tracing]"
-
Usage Examples
1. Gateway Mode (GKE Gateway)
Use this when running against a real cluster with a public Gateway IP. The client automatically discovers the Gateway.
from k8s_agent_sandbox import SandboxClient
from k8s_agent_sandbox.models import SandboxGatewayConnectionConfig
# Connect via the GKE Gateway
client = SandboxClient(
connection_config=SandboxGatewayConnectionConfig(
gateway_name="external-http-gateway", # Name of the Gateway resource
)
)
sandbox = client.create_sandbox(warmpool="python-sandbox-warmpool", namespace="default")
try:
print(sandbox.commands.run("echo 'Hello from Cloud!'").stdout)
finally:
sandbox.terminate()
2. Tunnel Mode (Local Port-Forward)
Use this for local development or CI. The client automatically opens a secure tunnel to the
Router Service using kubectl.
from k8s_agent_sandbox import SandboxClient
from k8s_agent_sandbox.models import SandboxLocalTunnelConnectionConfig
# Automatically tunnels to svc/sandbox-router-svc
client = SandboxClient(
connection_config=SandboxLocalTunnelConnectionConfig()
)
sandbox = client.create_sandbox(warmpool="python-sandbox-warmpool", namespace="default")
try:
print(sandbox.commands.run("echo 'Hello from Local!'").stdout)
finally:
sandbox.terminate()
You can pass per-claim environment variables when creating a sandbox:
sandbox = client.create_sandbox(
warmpool="python-sandbox-warmpool",
namespace="default",
env={"FOO": "bar"},
)
Setting env populates SandboxClaim.spec.env, which forces a cold start
from the warm pool template instead of adopting a pre-warmed pod. This may
increase startup latency.
3. In-Cluster Mode (Direct Pod Connection)
Use this when the client runs inside the cluster (for example, another pod in the same cluster). The client connects directly to the sandbox runtime pod, bypassing the sandbox router.
The client first uses the pod IP reported in the Sandbox status. If the pod IP is not available
(for example, before status is populated or when running against an older controller), it falls
back to the stable cluster DNS endpoint:
http://{sandbox_id}.{namespace}.svc.cluster.local:{server_port}.
from k8s_agent_sandbox import SandboxClient
from k8s_agent_sandbox.models import SandboxInClusterConnectionConfig
connection_config = SandboxInClusterConnectionConfig()
client = SandboxClient(connection_config=connection_config)
sandbox = client.create_sandbox(warmpool="python-sandbox-warmpool", namespace="default")
try:
print(sandbox.commands.run("echo 'Hello from in-cluster!'").stdout)
finally:
sandbox.terminate()
4. Direct URL Mode
Use SandboxDirectConnectionConfig to bypass discovery entirely. Useful for:
- Internal Agents: Running inside the cluster (e.g. router Service DNS).
- Custom Domains: Connecting via HTTPS (e.g.,
https://sandbox.example.com).
from k8s_agent_sandbox import SandboxClient
from k8s_agent_sandbox.models import SandboxDirectConnectionConfig
client = SandboxClient(
connection_config=SandboxDirectConnectionConfig(
api_url="http://sandbox-router-svc.agent-sandbox-system.svc.cluster.local:8080"
)
)
sandbox = client.create_sandbox(warmpool="python-sandbox-warmpool", namespace="default")
try:
sandbox.commands.run("ls -la")
finally:
sandbox.terminate()
5. Custom Ports
If your sandbox runtime listens on a port other than 8888 (e.g., a Node.js app on 3000), specify server_port.
from k8s_agent_sandbox import SandboxClient
from k8s_agent_sandbox.models import SandboxLocalTunnelConnectionConfig
client = SandboxClient(
connection_config=SandboxLocalTunnelConnectionConfig(server_port=3000)
)
sandbox = client.create_sandbox(warmpool="node-sandbox-warmpool", namespace="default")
6. Async Client
For async applications (FastAPI, aiohttp, async agent orchestrators), use the AsyncSandboxClient.
Install the async extras first:
pip install k8s-agent-sandbox[async]
The async client requires an explicit connection config — SandboxLocalTunnelConnectionConfig
is not supported because it relies on a synchronous kubectl port-forward subprocess. Use
SandboxGatewayConnectionConfig, SandboxDirectConnectionConfig, or
SandboxInClusterConnectionConfig instead.
Direct connection (explicit URL, e.g. router service):
import asyncio
from k8s_agent_sandbox import AsyncSandboxClient
from k8s_agent_sandbox.models import SandboxDirectConnectionConfig
async def main():
config = SandboxDirectConnectionConfig(
api_url="http://sandbox-router-svc.agent-sandbox-system.svc.cluster.local:8080"
)
async with AsyncSandboxClient(connection_config=config) as client:
sandbox = await client.create_sandbox(
warmpool="python-sandbox-warmpool",
namespace="default",
)
result = await sandbox.commands.run("echo 'Hello from async!'")
print(result.stdout)
asyncio.run(main())
In-cluster (direct to sandbox pod; default: cluster DNS):
import asyncio
from k8s_agent_sandbox import AsyncSandboxClient
from k8s_agent_sandbox.models import SandboxInClusterConnectionConfig
async def main():
config = SandboxInClusterConnectionConfig() # default: cluster DNS
async with AsyncSandboxClient(connection_config=config) as client:
sandbox = await client.create_sandbox(
warmpool="python-sandbox-warmpool",
namespace="default",
)
result = await sandbox.commands.run("echo 'Hello from async!'")
print(result.stdout)
asyncio.run(main())
7. Labels and Pod Metadata
create_sandbox lets you attach metadata at two different levels:
labels: Kubernetes labels on the SandboxClaim object itself (SandboxClaim.metadata.labels). Useful for selecting/listing claims.pod_labels/pod_annotations: labels and annotations stamped onto the running Sandbox Pod viaspec.additionalPodMetadata. Because they live on the Pod, the workload can read them from inside the sandbox through the Downward API (for example, to stamp a tenant or client identifier and reject requests that don't belong to it).
sandbox = client.create_sandbox(
warmpool="python-sandbox-warmpool",
namespace="default",
labels={"team": "platform"}, # on the SandboxClaim object
pod_labels={"client-id": "tenant-a"}, # on the running Pod
pod_annotations={"owner": "tenant-a"}, # on the running Pod
)
pod_labels are validated with the same Kubernetes label rules as labels. The
same parameters are available on AsyncSandboxClient.create_sandbox.
Behavioral notes:
- A
pod_label/pod_annotationwhose key already exists on the warmpool template with a different value is rejected by the controller's "No Overrides" rule, and the reconcile errors. - Client-side validation only checks RFC-1123 label syntax. The controller's domain allow-list and system-label restrictions are enforced server-side and are not replicated client-side.
8. Custom Volume Claim Templates
You can dynamically request persistent volumes to be attached to your Sandbox Pod by specifying volume_claim_templates. This allows the sandbox to mount custom PersistentVolumeClaims (PVCs).
sandbox = client.create_sandbox(
warmpool="python-sandbox-warmpool",
namespace="default",
volume_claim_templates=[
{
"metadata": {
"name": "my-volume",
},
"spec": {
"accessModes": ["ReadWriteOnce"],
"resources": {
"requests": {
"storage": "1Gi",
},
},
},
}
],
)
The volume claim templates are validated against the warmpool template's policy and rules (e.g., whether custom volume claims are allowed or if overrides are permitted).
9. Startup Latency: How the SDK Waits for Readiness
create_sandbox() is fully watch-based — it never polls the Kubernetes
API on an interval, so there is no poll-interval latency added on top of the
controller's own claim-to-Ready time.
The wait is a single watch on the SandboxClaim. The claim controller
publishes the bound sandbox name (status.sandbox.name), the pod IPs
(status.sandbox.podIPs) and the forwarded Ready condition in one status
update when it adopts a warm-pool sandbox, so the first watch event that
carries the sandbox name normally also carries Ready=True and
create_sandbox() returns immediately. On a cold start (no warm sandbox
available, or env/volume_claim_templates set, which force cold starts)
the same watch simply keeps streaming claim updates until the forwarded
Ready condition flips to True.
Latency guidance:
- Do not poll
Sandbox/SandboxClaimobjects withget_*calls in a loop to detect readiness; a poll interval ofTadds an average ofT/2(uniformly distributed 0..T) on top of the controller latency. Usecreate_sandbox()/ the claimReadycondition watch. sandbox_ready_timeout(default 180s) bounds the whole wait; the watch returns as soon as the claim is Ready, the timeout only caps the worst case.- The Kubernetes client reuses a single authenticated connection pool for the watch, so no extra TLS handshakes occur on the ready path.
- With the local-tunnel connection mode, the first request additionally pays
for the
kubectl port-forwardstartup; the SDK probes the local port every 50ms while it comes up. Gateway/in-cluster modes do not have this step.
Testing
A test script is included to verify the full lifecycle (Creation -> Execution -> File I/O -> Cleanup).
Run in Tunnel Mode:
python test_client.py --namespace default
Run in Gateway Mode:
python test_client.py --gateway-name external-http-gateway
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file k8s_agent_sandbox-1.0.0.tar.gz.
File metadata
- Download URL: k8s_agent_sandbox-1.0.0.tar.gz
- Upload date:
- Size: 163.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
055d7c8e7b5db592d40f2e06ce36753758cb88737dac0d4328943ca4e24d77ae
|
|
| MD5 |
a748c8001b54bb3806f7ded67fd9113f
|
|
| BLAKE2b-256 |
e906917bd584618e9f6a7e8ff5f4c6efb1a16c349250b6007e696b03a16a7d20
|
Provenance
The following attestation bundles were made for k8s_agent_sandbox-1.0.0.tar.gz:
Publisher:
pypi-publish.yml on kubernetes-sigs/agent-sandbox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
k8s_agent_sandbox-1.0.0.tar.gz -
Subject digest:
055d7c8e7b5db592d40f2e06ce36753758cb88737dac0d4328943ca4e24d77ae - Sigstore transparency entry: 2629844352
- Sigstore integration time:
-
Permalink:
kubernetes-sigs/agent-sandbox@bb72f49d79f009a960eed2ae6c32e1cc082399c5 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/kubernetes-sigs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@bb72f49d79f009a960eed2ae6c32e1cc082399c5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file k8s_agent_sandbox-1.0.0-py3-none-any.whl.
File metadata
- Download URL: k8s_agent_sandbox-1.0.0-py3-none-any.whl
- Upload date:
- Size: 96.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5497a51a1d7137f83346b0df8206da808100700561a099b52d60c90a0b476bb5
|
|
| MD5 |
5860a35038f8784899c9c3903cbd3e12
|
|
| BLAKE2b-256 |
033477b6b114a029533ed244f317e4232f26cc63833e35761485120eeaae8df6
|
Provenance
The following attestation bundles were made for k8s_agent_sandbox-1.0.0-py3-none-any.whl:
Publisher:
pypi-publish.yml on kubernetes-sigs/agent-sandbox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
k8s_agent_sandbox-1.0.0-py3-none-any.whl -
Subject digest:
5497a51a1d7137f83346b0df8206da808100700561a099b52d60c90a0b476bb5 - Sigstore transparency entry: 2629844469
- Sigstore integration time:
-
Permalink:
kubernetes-sigs/agent-sandbox@bb72f49d79f009a960eed2ae6c32e1cc082399c5 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/kubernetes-sigs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@bb72f49d79f009a960eed2ae6c32e1cc082399c5 -
Trigger Event:
push
-
Statement type: