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 CLI
Package Manager
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
Parameter Description Default Environment Variable ApiKeyAPI key for authentication Optional OPEN_SANDBOX_API_KEYDomainSandbox service domain (host[:port]) localhost:8080OPEN_SANDBOX_DOMAINProtocolHTTP protocol (Http/Https) Http— RequestTimeoutSecondsRequest timeout for SDK HTTP calls 30— UseServerProxyRoute execd/endpoint calls through the server proxy false— 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
Parameter Description Default ImageDocker image to use Required TimeoutSecondsAutomatic termination TTL 10 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 expiry falseSkipHealthCheckSkip readiness checks falseHealthCheckCustom readiness check function — ReadyTimeoutSecondsMax time to wait for readiness 30 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