Your first program
This program creates a sandbox, waits until it is ready, runs a command in it, prints the output, and deletes the sandbox again.
1. Create the project
dotnet new console -n HelloOpenShell
cd HelloOpenShell
dotnet add package OpenShell.Net
2. Write the program
Replace the contents of Program.cs. Start with the namespaces and the client; this connects to the active gateway registered by the OpenShell CLI. To use an explicit address and token instead, create the client as shown in Connect to a gateway.
using OpenShell;
using OpenShell.Gateway;
using var client = GatewayClient.Connect();
Then add the program itself below it:
// 1. Create the sandbox. The name is optional: pass null to let the gateway generate one.
var sandbox = await client.Sandboxes.CreateAsync(
"hello-openshell",
new SandboxSpec { Image = "registry.example.com/team/python-agent:1.0" });
try
{
// 2. Wait until it is Ready. A sandbox that fails, or a timeout, throws OpenShellException.
await client.Sandboxes.WaitReadyAsync(sandbox.Name, new WaitOptions { Timeout = TimeSpan.FromMinutes(2) });
// 3. Run a command and read its output.
var result = await client.Exec.RunAsync(sandbox.Name, ["echo", "hello from OpenShell"]);
Console.WriteLine($"exit {result.ExitCode}: {result.StdoutText}");
}
finally
{
// 4. Delete it, and wait while the gateway finishes the cleanup in the background.
var deletion = await client.Sandboxes.DeleteAsync(sandbox.Name);
if (deletion.Outcome == DeletionOutcome.Accepted)
{
await client.Sandboxes.WaitDeletedAsync(
sandbox.Name,
new WaitOptions { ExpectedSandboxId = deletion.ResourceId });
}
}
Use an image your gateway can pull.
3. Run it
dotnet run
exit 0: hello from OpenShell
What happened
- CreateAsync asked the gateway for a sandbox and returned as soon as the gateway accepted it, in the
Provisioningphase. - WaitReadyAsync polled the sandbox, with backoff, until it reached
Ready. A sandbox that entersErroror stops fails the wait at once; running out of time throws anOpenShellExceptionwhoseKindisDeadlineExceeded. - RunAsync ran the command (an argument list, not a shell string) and buffered its output into an ExecResult:
ExitCode,StdoutText,StderrText. A non-zero exit code is a result, not an exception. - DeleteAsync returned a typed DeletionResult.
Acceptedmeans the gateway is still cleaning up, so the program waits with WaitDeletedAsync;ExpectedSandboxIdkeeps a new sandbox with the same name from extending the wait.
Every failure surfaces as an OpenShellException with a Kind you can switch on; see Errors.
Tip
The same code runs unchanged against FakeOpenShellClient from OpenShell.Net.Testing, which is how this documentation tests it. See Testing.
Next steps
- Sandboxes: images, environment, labels, templates, stop and start, suspend and resume, updates.
- Exec and streaming: streaming output, interactive sessions, logs, and watches.
- Authentication: OIDC, client credentials, mTLS, and Cloudflare Access.