Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/opensandbox-group/OpenSandbox/llms.txt

Use this file to discover all available pages before exploring further.

The OpenSandbox C# SDK provides a fully async, IAsyncDisposable-based API for creating and managing secure sandbox environments from .NET applications. It covers sandbox lifecycle management, shell command execution with real-time streaming, comprehensive file operations, endpoint resolution, volume mounts, egress policy control, and Credential Vault integration. The SDK targets .NET Standard 2.0 through .NET 10 and integrates with Microsoft.Extensions.Logging for diagnostics.

Installation

dotnet add package Alibaba.OpenSandbox

Quick Start

Use await using to ensure DisposeAsync() is called when the sandbox goes out of scope.
using OpenSandbox;
using OpenSandbox.Config;
using OpenSandbox.Core;

var config = new ConnectionConfig(new ConnectionConfigOptions
{
    Domain = "api.opensandbox.io",
    ApiKey = "your-api-key",
    // Protocol = ConnectionProtocol.Https,
    // RequestTimeoutSeconds = 60,
});

try
{
    await using var sandbox = await Sandbox.CreateAsync(new SandboxCreateOptions
    {
        ConnectionConfig = config,
        Image = "ubuntu",
        TimeoutSeconds = 10 * 60,
    });

    var execution = await sandbox.Commands.RunAsync("echo 'Hello Sandbox!'");
    Console.WriteLine(execution.Logs.Stdout.FirstOrDefault()?.Text);

    // Optional but recommended: terminate the remote instance when done
    await sandbox.KillAsync();
}
catch (SandboxException ex)
{
    Console.Error.WriteLine($"Sandbox Error: [{ex.Error.Code}] {ex.Error.Message}");
    Console.Error.WriteLine($"Request ID: {ex.RequestId}");
}

ConnectionConfig Parameters

ParameterDescriptionDefaultEnvironment Variable
ApiKeyAPI key for authenticationOptionalOPEN_SANDBOX_API_KEY
DomainSandbox service domain (host[:port])localhost:8080OPEN_SANDBOX_DOMAIN
ProtocolHTTP protocol (Http/Https)Http
RequestTimeoutSecondsRequest timeout for SDK HTTP calls30
UseServerProxyRoute execd/endpoint calls through the server proxyfalse
HeadersExtra headers applied to every request{}
using OpenSandbox.Config;

var config = new ConnectionConfig(new ConnectionConfigOptions
{
    Domain = "api.opensandbox.io",
    ApiKey = "your-key",
    RequestTimeoutSeconds = 60,
    Headers = new Dictionary<string, string>
    {
        ["X-Custom-Header"] = "value"
    },
});

Sandbox.CreateAsync() Parameters

ParameterDescriptionDefault
ImageDocker image to useRequired
TimeoutSecondsAutomatic termination TTL10 minutes
EntrypointContainer entrypoint command["tail","-f","/dev/null"]
ResourceCPU and memory limits{"cpu":"1","memory":"2Gi"}
EnvEnvironment variables{}
MetadataCustom metadata tags{}
NetworkPolicyOptional outbound egress policy
CredentialProxyCredential Vault proxy startup settings
VolumesStorage mounts (Host/PVC, supports ReadOnly and SubPath)
ExtensionsExtra server-defined fields{}
ManualCleanupDisable automatic TTL expiryfalse
SkipHealthCheckSkip readiness checksfalse
HealthCheckCustom readiness check function
ReadyTimeoutSecondsMax time to wait for readiness30 seconds
HealthCheckPollingIntervalPoll interval while waiting (ms)200 ms
Metadata keys under opensandbox.io/ are reserved for system-managed labels and will be rejected by the server.
The C# SDK uses an explicit ManualCleanup = true flag rather than TimeoutSeconds = null to request a non-expiring sandbox. This avoids ambiguity between “unset, use the default TTL” and “explicitly disable expiry” in the int? options model.

Key Operations

Lifecycle

var info = await sandbox.GetInfoAsync();
Console.WriteLine($"State: {info.Status.State}");
Console.WriteLine($"Created: {info.CreatedAt}");
Console.WriteLine($"Expires: {info.ExpiresAt}");

await sandbox.PauseAsync();

// Resume returns a fresh, connected Sandbox instance
var resumed = await sandbox.ResumeAsync();

// Renew: expiresAt = now + timeoutSeconds
await resumed.RenewAsync(30 * 60);
Connect to an existing sandbox by ID:
var connected = await Sandbox.ConnectAsync(new SandboxConnectOptions
{
    SandboxId = "existing-sandbox-id",
    ConnectionConfig = config
});

Custom Health Check

var sandbox = await Sandbox.CreateAsync(new SandboxCreateOptions
{
    ConnectionConfig = config,
    Image = "nginx:latest",
    HealthCheck = async (sbx) =>
    {
        var ep = await sbx.GetEndpointAsync(80);
        return !string.IsNullOrEmpty(ep.EndpointAddress);
    },
});

Command Execution with Streaming

using OpenSandbox.Models;

var handlers = new ExecutionHandlers
{
    OnStdout = msg => { Console.WriteLine($"STDOUT: {msg.Text}"); return Task.CompletedTask; },
    OnStderr = msg => { Console.Error.WriteLine($"STDERR: {msg.Text}"); return Task.CompletedTask; },
    OnExecutionComplete = c => { Console.WriteLine($"Finished in {c.ExecutionTimeMs}ms"); return Task.CompletedTask; },
};

await sandbox.Commands.RunAsync(
    "for i in 1 2 3; do echo \"Count $i\"; sleep 0.2; done",
    handlers: handlers
);
For long-running background commands, poll status and retrieve incremental logs:
var execution = await sandbox.Commands.RunAsync(
    "python /app/server.py",
    options: new RunCommandOptions
    {
        Background = true,
        TimeoutSeconds = 120,
    });

var status = await sandbox.Commands.GetCommandStatusAsync(execution.Id!);
var logs = await sandbox.Commands.GetBackgroundCommandLogsAsync(execution.Id!, cursor: 0);
Console.WriteLine($"running={status.Running}, cursor={logs.Cursor}");

File Operations

await sandbox.Files.CreateDirectoriesAsync(new[]
{
    new CreateDirectoryEntry { Path = "/tmp/demo", Mode = 755 }
});

await sandbox.Files.WriteFilesAsync(new[]
{
    new WriteEntry { Path = "/tmp/demo/hello.txt", Data = "Hello World", Mode = 644 }
});

var content = await sandbox.Files.ReadFileAsync("/tmp/demo/hello.txt");
Console.WriteLine($"Content: {content}");

var files = await sandbox.Files.SearchAsync(
    new SearchEntry { Path = "/tmp/demo", Pattern = "*.txt" });
foreach (var file in files)
{
    Console.WriteLine(file.Path);
}

await sandbox.Files.DeleteDirectoriesAsync(new[] { "/tmp/demo" });
await sandbox.Files.DeleteFilesAsync(new[] { "/tmp/demo/hello.txt" });

Endpoints

// Returns address without scheme, e.g. "localhost:44772"
var endpoint = await sandbox.GetEndpointAsync(44772);
Console.WriteLine(endpoint.EndpointAddress);

// Returns a full URL, e.g. "http://localhost:44772"
var url = await sandbox.GetEndpointUrlAsync(44772);
Console.WriteLine(url);

Egress Policy

var policy = await sandbox.GetEgressPolicyAsync();

await sandbox.PatchEgressRulesAsync(new[]
{
    new NetworkRule { Action = NetworkRuleAction.Allow, Target = "www.github.com" },
    new NetworkRule { Action = NetworkRuleAction.Deny, Target = "pypi.org" }
});

Credential Vault

var sandbox = await Sandbox.CreateAsync(new SandboxCreateOptions
{
    ConnectionConfig = config,
    Image = "python:3.11",
    NetworkPolicy = new NetworkPolicy
    {
        DefaultAction = NetworkRuleAction.Deny,
        Egress = new List<NetworkRule>
        {
            new() { Action = NetworkRuleAction.Allow, Target = "api.example.com" }
        }
    },
    CredentialProxy = new CredentialProxyConfig { Enabled = true }
});

await sandbox.CreateCredentialVaultAsync(
    new[]
    {
        new Credential
        {
            Name = "api-token",
            Source = new InlineCredentialSource { Value = "<token>" }
        }
    },
    new[]
    {
        new CredentialBinding
        {
            Name = "api-token",
            Match = new CredentialMatch
            {
                Schemes = new[] { "https" },
                Ports = new[] { 443 },
                Hosts = new[] { "api.example.com" },
                Paths = new[] { "/v1/*" }
            },
            Auth = new CredentialAuth
            {
                Type = "apiKey",
                Name = "x-api-key",
                Credential = "api-token"
            }
        }
    });

Volume Mounts

var sandbox = await Sandbox.CreateAsync(new SandboxCreateOptions
{
    ConnectionConfig = config,
    Image = "python:3.11",
    Volumes = new[]
    {
        new Volume
        {
            Name = "workspace",
            Host = new Host { Path = "/tmp/opensandbox-e2e/host-volume-test" },
            MountPath = "/workspace",
            ReadOnly = false
        }
    }
});

Diagnostics and Logging

The SDK integrates with Microsoft.Extensions.Logging for structured diagnostics.
using Microsoft.Extensions.Logging;
using OpenSandbox.Config;

using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.SetMinimumLevel(LogLevel.Debug);
    builder.AddConsole();
});

var sandbox = await Sandbox.CreateAsync(new SandboxCreateOptions
{
    Image = "python:3.11",
    ConnectionConfig = new ConnectionConfig(),
    Diagnostics = new SdkDiagnosticsOptions
    {
        LoggerFactory = loggerFactory
    }
});

SandboxManager (Admin)

await using var manager = SandboxManager.Create(new SandboxManagerOptions
{
    ConnectionConfig = config
});

var list = await manager.ListSandboxInfosAsync(new SandboxFilter
{
    States = new[] { SandboxStates.Running },
    PageSize = 10
});

foreach (var s in list.Items)
{
    Console.WriteLine(s.Id);
}

Error Handling

The SDK throws SandboxException and its derived types for API failures and timeout conditions.
try
{
    var execution = await sandbox.Commands.RunAsync("echo 'Hello Sandbox!'");
    Console.WriteLine(execution.Logs.Stdout.FirstOrDefault()?.Text);
}
catch (SandboxReadyTimeoutException)
{
    Console.Error.WriteLine("Sandbox did not become ready before the configured timeout.");
}
catch (SandboxApiException ex)
{
    Console.Error.WriteLine($"API Error: status={ex.StatusCode}, requestId={ex.RequestId}, message={ex.Message}");
}
catch (SandboxException ex)
{
    Console.Error.WriteLine($"Sandbox Error: [{ex.Error.Code}] {ex.Error.Message}");
}

Supported Frameworks

The SDK targets .NET Standard 2.0 for maximum compatibility, and is also tested on:
  • .NET Standard 2.0 (.NET Framework 4.6.1+, .NET Core 2.0+, Mono, Xamarin)
  • .NET Standard 2.1
  • .NET 6.0 (LTS), .NET 7.0, .NET 8.0 (LTS), .NET 9.0, .NET 10.0

Build docs developers (and LLMs) love