Skip to main content
The Esprit Cloud runtime executes security scans in managed cloud sandboxes, eliminating the need for local Docker installation and providing better scalability for team collaboration.

Architecture

CloudRuntime Class

Implemented in esprit/runtime/cloud_runtime.py, the CloudRuntime manages cloud sandbox lifecycle:

Initialization

class CloudRuntime(AbstractRuntime):
    def __init__(self, access_token: str, api_base: str) -> None:
        self.access_token = access_token  # From esprit login
        self.api_base = api_base          # e.g., https://api.esprit.cloud
        self._sandboxes = {}              # Track active sandboxes
Authentication:
  • Requires valid access token from esprit login
  • Token stored in ~/.esprit/credentials.json
  • Throws SandboxInitializationError if not authenticated

Configuration

Environment variables:
VariableDefaultDescription
ESPRIT_API_BASEhttps://api.esprit.cloudCloud API endpoint
ESPRIT_SCAN_MODEquickScan mode: quick, deep, or compliance
ESPRIT_CLOUD_SANDBOX_STATE_FILE~/.esprit/state/cloud_sandboxes.jsonTracked sandboxes for cleanup

Sandbox Lifecycle

1. Sandbox Creation

Method: create_sandbox() (lines 381-463)
  1. Sanitize local sources (if scanning local code)
  2. Attempt legacy endpoint first:
    POST /sandbox/create
    {
      "agent_id": "agent-123",
      "local_sources": [...]
    }
    
  3. Fallback to modern endpoint on 404/405:
    POST /sandbox
    {
      "scan_id": "cli-agent-123",
      "target": "https://example.com",
      "target_type": "url",
      "scan_type": "quick"
    }
    
  4. Poll for tool server URL (max 240 seconds)
  5. Track sandbox in state file for cleanup
  6. Return SandboxInfo with credentials
Endpoint Compatibility: Supports both legacy and modern Esprit Cloud API versions.

2. Tool Server URL Polling

Method: _poll_tool_server_url() (lines 309-361) Modern cloud sandboxes provision asynchronously:
while time.monotonic() < deadline:
    response = await client.get(f"{api_base}/sandbox/{sandbox_id}")
    data = response.json()
    
    if tool_server_url := data.get("tool_server_url"):
        return tool_server_url
    
    if data.get("status") in {"running", "ready", "active"}:
        # Fallback endpoint
        return f"{api_base}/sandbox/{sandbox_id}"
    
    if data.get("status") in {"stopped", "failed", "error"}:
        return None  # Provisioning failed
Polling parameters:
  • Max timeout: 240 seconds
  • Poll interval: 1.5 seconds
  • Connect timeout: 5 seconds
  • Request timeout: 8 seconds
Grace period:
  • First 30 seconds: Ignore transient “stopped” status (ECS warmup)
  • After 30 seconds: Treat terminal states as failures

3. Retry Logic

Method: create_sandbox() with retry wrapper (lines 388-426)
for attempt in range(2):  # Max 2 attempts
    try:
        # Create sandbox
        # Poll for readiness
        return sandbox_info
    except timeout:
        if attempt < 1:
            await asyncio.sleep(2)  # Retry delay
            continue
        raise
Retry scenarios:
  • Tool server not ready within 240 seconds
  • Automatically destroys unready sandbox before retry

4. Local Source Upload

Method: _sanitize_local_sources() (lines 227-247) For local code scanning:
local_sources = [
    {
        "source_path": "/Users/me/my-app",
        "workspace_subdir": "my-app"
    }
]
# Cloud API uploads to sandbox workspace
Process:
  1. CLI sends local source paths to cloud API
  2. Cloud API clones/uploads to sandbox
  3. Files available at /workspace/<workspace_subdir>

5. Sandbox Info Response

Method: create_sandbox() returns SandboxInfo:
return {
    "workspace_id": "sb-abc123",           # Cloud sandbox ID
    "api_url": "https://sb-abc123.esprit.cloud",  # Tool server URL
    "auth_token": "<cloud_token>",         # Sandbox-specific token
    "tool_server_port": 443,               # HTTPS port
    "agent_id": "agent-123",               # Agent identifier
}

6. Sandbox Cleanup

Method: destroy_sandbox() (lines 493-506)
await client.delete(
    f"{api_base}/sandbox/{sandbox_id}",
    headers={"Authorization": f"Bearer {access_token}"},
)
Cleanup triggers:
  • Normal scan completion
  • CLI process exit
  • Manual cleanup via esprit cleanup

State Tracking

Cloud sandboxes are tracked in ~/.esprit/state/cloud_sandboxes.json:
{
  "version": 1,
  "sandboxes": [
    {
      "sandbox_id": "sb-abc123",
      "api_base": "https://api.esprit.cloud",
      "created_at": "1709510400"
    }
  ]
}

Stale Sandbox Cleanup

Method: cleanup_stale_sandboxes() (lines 134-174) Cleans up sandboxes from abrupt terminations:
cleaned = await CloudRuntime.cleanup_stale_sandboxes(
    access_token=token,
    api_base=api_base,
)
print(f"Cleaned up {cleaned} stale sandboxes")
When to run:
  • Before starting new scans
  • After unexpected CLI crashes
  • Manual cleanup via esprit cleanup

Target Inference

Method: _infer_scan_target() (lines 177-224) Cloud runtime automatically infers target from scan config:
Repository:
{
  "target": "https://github.com/user/repo",
  "target_type": "repository"
}
Local Code:
{
  "target": "/Users/me/my-app",
  "target_type": "local_upload"
}
Web Application:
{
  "target": "https://example.com",
  "target_type": "url"
}
IP Address:
{
  "target": "192.168.1.100",
  "target_type": "url"
}

Workspace Diffs

Method: get_workspace_diffs() (lines 471-491) Retrieves file edits from cloud sandbox:
await client.get(
    f"{api_url}/diffs",
    headers={"Authorization": f"Bearer {auth_token}"},
)
edits = resp.json().get("edits", [])
Same format as Docker runtime diffs.

Error Handling

raise SandboxInitializationError(
    "Esprit Cloud authentication required.",
    "Run `esprit login` and try again.",
)
Thrown when:
  • No access token provided
  • Invalid/expired token
  • HTTP 401 response
raise SandboxInitializationError(
    "Failed to create cloud sandbox.",
    f"Cloud sandbox API returned HTTP {status_code}: {response.text}",
)
Includes:
  • HTTP status code
  • Response body (first 500 chars)
  • Endpoint that failed
raise SandboxInitializationError(
    "Failed to connect to Esprit Cloud sandbox API.",
    str(error),
)
Thrown when:
  • Network connectivity issues
  • DNS resolution failures
  • Timeout connecting to API
raise SandboxInitializationError(
    "Cloud sandbox is still provisioning.",
    "Tool server did not become ready in time. Please retry in a few moments.",
)
Thrown when:
  • Sandbox not ready within 240 seconds
  • After 2 retry attempts
  • Automatically destroys unready sandbox

Advanced Cleanup

Method: cleanup() (lines 512-539) Handles cleanup in different async contexts:
asyncio.run(self._cleanup_all(sandbox_ids))
Directly runs cleanup coroutine.
with ThreadPoolExecutor(max_workers=1) as pool:
    pool.submit(asyncio.run, self._cleanup_all(sandbox_ids)).result(timeout=20)
Uses thread pool to avoid blocking active loop.
for sandbox_id in sandbox_ids:
    loop.create_task(self.destroy_sandbox(sandbox_id))
Best-effort scheduling if thread pool fails.

Performance & Scalability

Advantages over Docker

FeatureDocker RuntimeCloud Runtime
SetupRequires Docker DesktopNo local dependencies
Resource UsageUses local CPU/RAMCloud resources
ScalingLimited by hostUnlimited cloud capacity
CollaborationSingle machineTeam access
PersistenceLocal onlyCloud storage
Network SpeedlocalhostInternet latency

Cost Optimization

  • Sandboxes auto-terminate after inactivity
  • Shared infrastructure across team
  • Pay-per-use pricing model
  • Automatic resource scaling

Security

  • Sandboxes isolated per scan
  • Encrypted communication (HTTPS)
  • Token-based authentication
  • Automatic credential rotation
  • Audit logs in cloud console

Troubleshooting

  1. Run esprit login to re-authenticate
  2. Check token: cat ~/.esprit/credentials.json
  3. Verify API endpoint: echo $ESPRIT_API_BASE
  4. Test connectivity: curl https://api.esprit.cloud/health
  1. Check cloud service status
  2. Retry with: esprit scan <target> (automatic retry)
  3. Increase timeout (not configurable, fixed at 240s)
  4. Contact support if persistent
  1. Check internet connectivity
  2. Verify firewall allows HTTPS to *.esprit.cloud
  3. Check proxy settings: env | grep -i proxy
  4. Disable VPN if blocking cloud access
  1. Run esprit cleanup to remove orphaned sandboxes
  2. Check state file: cat ~/.esprit/state/cloud_sandboxes.json
  3. Manually delete via cloud console if needed

API Compatibility

Cloud runtime supports two API versions:

Legacy API (v1)

POST /sandbox/create
Authorization: Bearer <token>

{
  "agent_id": "agent-123",
  "local_sources": [...]
}
Response includes immediate api_url and auth_token.

Modern API (v2)

POST /sandbox
Authorization: Bearer <token>

{
  "scan_id": "cli-agent-123",
  "target": "https://example.com",
  "target_type": "url",
  "scan_type": "quick"
}

GET /sandbox/{sandbox_id}
Authorization: Bearer <token>
Response includes status field, requires polling for tool_server_url. Auto-detection: Runtime tries legacy first, falls back to modern on 404/405.

Next Steps

Docker Sandbox

Alternative local runtime

Tools

Available tools in cloud sandboxes

Build docs developers (and LLMs) love