OpenShell execution
Run Codex sessions behind OpenShell policy while Laufwerk owns workflow durability and workspaces.
OpenShell support is unreleased. Use a build containing this integration; the published alpha.14 packages do not include it.
NVIDIA OpenShell supplies the sandbox lifecycle, filesystem and network policy enforcement, and credential injection. Laufwerk continues to own workflows, approvals, session history, workspace access and write-back. Select it at the execution target used by Workspace.open and execution.agent.
This integration lives in @laufwerk/execution/openshell. It reuses NVIDIA's official TypeScript SDK and the pinned AI SDK Harness sandbox contract. It does not introduce a package, agent API, workflow engine or gateway provisioner.
Capabilities exposed through Laufwerk
Laufwerk exposes a tested subset of OpenShell for durable agent workflows. The adapter does not expose the full OpenShell API. A capability being available in OpenShell does not mean it is configurable through Laufwerk.
| Area | Current Laufwerk support |
|---|---|
| Agent execution | Pinned Codex harness with subscription credentials; turns, command execution, file access and session resume |
| Customer configuration | Named local gateway and credential provider, native OpenShell v1 network policies, CPU and memory limits, and a compatible prepared image |
| Workspace and lifecycle | Laufwerk controls workspace mounts and access mode, filesystem/process policy, sandbox identity, allocation, resume and confirmed cleanup |
| Additional OpenShell APIs | No general passthrough for arbitrary gateway or sandbox options; snapshots, GPU configuration, dynamic policy updates and arbitrary port forwarding are not exposed by this adapter |
Keeping workspace bindings and lifecycle under Laufwerk's control lets it prevent conflicting writers and unsafe write-back across worker restarts. Using NVIDIA's SDK separately to modify a Laufwerk-owned sandbox can invalidate those guarantees; it is not a supported way to extend the adapter. The deployment and authentication limits below also apply.
Request additional capabilities
If your workflow needs an OpenShell capability that Laufwerk does not expose, request it in the Laufwerk community repository. These boundaries describe the current integration, not a commitment to keep its API permanently limited.
Include the OpenShell capability or option you need, the customer workflow it would enable, your OpenShell version and deployment type, and the limitation you encounter today. This helps us prioritize additional native OpenShell options and verify that they preserve Laufwerk's durability and workspace guarantees.
Qualified environment
The first supported combination is OpenShell 0.0.116, Linux, a local Docker gateway, Bun 1.4.2, and Laufwerk's pinned Codex harness with subscription credentials. The gateway and worker must use the same host filesystem and Docker engine. The worker runs as a non-root user with Docker access. OpenShell must support hard Landlock enforcement and Docker bind mounts (enable_bind_mounts = true). Provision gateway certificates and networking using the OpenShell administrator documentation.
Use a dedicated local gateway registered in the worker user's $XDG_CONFIG_HOME/openshell/gateways (normally ~/.config/openshell/gateways). Laufwerk reads the named alias and its mTLS credentials and accepts loopback HTTPS only. System-only aliases, remote gateways, Docker Desktop, Kubernetes, other agents, API-key billing and automatic subscription refresh are outside this qualification. Do not rename or externally recreate Laufwerk-owned sandboxes while operations are in flight.
CPU limits are cores, with millicore precision. Memory is bytes. Per-session PID and swap limits are not supported by this adapter; configure gateway limits separately. Do not replace a Docker factory that depends on those limits without reviewing that difference. Host custom tools execute outside the OpenShell policy boundary.
Set up a consumer
Using the CLI from the same build as your installed Laufwerk packages:
laufwerk init --execution openshell --example issue-to-fixThis writes laufwerk/execution.ts, laufwerk/openshell/policy.ts, registers the target in laufwerk/laufwerk.config.ts, and makes the example import that same target. It does not start a gateway, provision credentials or allocate a sandbox.
Install @nvidia/openshell-sdk@0.0.116 in the consumer's laufwerk/ package. NVIDIA distributes this version through GitHub Packages: configure @nvidia:registry=https://npm.pkg.github.com and authenticate with a token that has read:packages, following the official SDK installation instructions. Keep authentication outside committed files. The dependency is optional: ordinary Laufwerk installations and configuration imports do not load it.
For qualification here, registry access was unavailable. We built the official SDK from the verified v0.0.116 source using its gen and build scripts. No fork or vendored SDK is included. A published-registry installation of the NVIDIA package has not been independently verified.
Provision Codex once
The built-in OpenShell Codex profile permits a different executable location from the pinned Harness bootstrap. Export it using the official CLI:
openshell provider profile export codex --gateway laufwerk-local > laufwerk/openshell/codex-profile.yamlIn this non-secret profile, change id to laufwerk-codex, and add /sandbox/.harness-bootstrap/codex/node_modules/** to binaries. Preserve its credentials and endpoints. Then import it:
openshell provider profile import --gateway laufwerk-local --file laufwerk/openshell/codex-profile.yamlCreate a provider of type laufwerk-codex with the subscription's CODEX_AUTH_ACCESS_TOKEN, CODEX_AUTH_REFRESH_TOKEN, CODEX_AUTH_ACCOUNT_ID, and optional CODEX_AUTH_ID_TOKEN. The CLI accepts environment variable names, so values need not appear in arguments or policy files. For a file-backed Codex subscription login, an explicit, local setup script can do this:
import { readFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
const auth = JSON.parse(await readFile(join(process.env.CODEX_HOME ?? join(homedir(), ".codex"), "auth.json"), "utf8"));
if (auth.auth_mode !== "chatgpt") throw new Error("A Codex subscription login is required");
const credentials: Record<string, string> = {};
for (const key of ["access_token", "refresh_token", "account_id", "id_token"]) {
const value = auth.tokens?.[key];
if (typeof value === "string" && value) credentials[`CODEX_AUTH_${key.toUpperCase()}`] = value;
else if (key !== "id_token") throw new Error(`Missing subscription ${key}`);
}
const child = Bun.spawn([
"openshell", "provider", "create", "--gateway", "laufwerk-local",
"--name", "laufwerk-codex", "--type", "laufwerk-codex",
...Object.keys(credentials).flatMap(key => ["--credential", key]),
], { env: { ...process.env, ...credentials }, stdout: "ignore", stderr: "ignore" });
if (await child.exited) throw new Error("Provider creation failed; inspect the gateway configuration");Real credentials stay with the gateway. The sandbox receives credential placeholders and a non-secret synthetic ID token to satisfy the pinned Codex bootstrap. Credentials are not saved in Laufwerk's workflow database or prepared images. Provisioning and rotation are explicit operator actions; refresh across token expiry is not qualified. An expired provider fails the run without falling back to API-key billing.
Define and use the target
laufwerk/execution.ts:
import { createOpenShellExecution } from "@laufwerk/execution/openshell";
import { policy } from "./openshell/policy";
export const execution = createOpenShellExecution({
id: "secure-coding",
gateway: "laufwerk-local",
credentials: { kind: "codex-subscription", provider: "laufwerk-codex" },
policy,
resources: { cpus: 2, memoryBytes: 4 * 1024 ** 3 },
});laufwerk/laufwerk.config.ts:
import { defineConfig } from "@laufwerk/sdk";
import { execution } from "./execution";
export default defineConfig({ projectId: "my-project", executions: [execution] });policy is the OpenShell v1 networkPolicies object, not a Laufwerk policy language. The generated policy permits the Codex bootstrap binary to reach its four provider endpoints over HTTPS. Add build/download endpoints only when your workflow requires them. Laufwerk supplies the filesystem and process portion: the prepared bootstrap, writable sandbox home and temporary files, and the managed workspace at /sandbox/work. Workspace access determines whether its bind mount is writable.
Prepare the image explicitly, then check the selected target:
laufwerk prepare openshell --target secure-coding
laufwerk doctor --execution openshell --target secure-codingprepare downloads/builds the existing pinned Codex bootstrap without credentials. Its image cache defaults to ~/.laufwerk/images/openshell; imageCacheDirectory overrides it. An explicit image must match the managed recipe. Runtime startup never installs missing dependencies. Repeat preparation after a recipe change. Doctor checks gateway version, provider existence and the prepared image; its inference: "untested" result is intentional. It does not prove model availability or the full host policy configuration.
Inside a workflow, use the ordinary SDK primitives:
const agent = execution.agent({ harness: createCodex(), permissionMode: "allow-all" });
const workspace = yield* Workspace.open({ source: ".", execution });
const writer = yield* Session.open({ key: "writer", agent, workspace, access: "read-write" });
yield* writer.ask({ key: "edit", prompt: "Implement the requested change." });
const checked = yield* writer.exec({ key: "check", command: "bun test" });
// Handle checked.exitCode according to the workflow's success/retry policy.
yield* writer.close();
// A reviewer can now open the same workspace with access: "read-only".
yield* Workspace.writeBack({ workspace });Import Session and Workspace from @laufwerk/sdk, createCodex from @laufwerk/sdk/harness/codex, and the shared execution from your consumer. The generated issue-to-fix workflow is a complete example.
Ownership and recovery
A sandbox can keep writing after a turn, approval wait or worker exit. Laufwerk therefore records allocation intent before calling OpenShell, keeps workspace ownership until confirmed deletion, and recreates only local forwarding handles on resume. Multiple live read-only sessions may share a workspace; a live writer excludes other sessions and write-back. Close a writer before handing its workspace to a reviewer.
The target's stable ID and definition fingerprint are persisted with the workspace/session. Do not change its gateway, policy, provider or resource settings while a session needs to resume. Changed definitions fail clearly; they never silently allocate a replacement sandbox. Restore the definition to resume, or terminate and clean up the old run.
For a terminal run without a live worker lease:
laufwerk cleanup <run-id>A failed turn can close the conversation while the execution resource still needs cleanup. Call session.close() after handling that failure; it consults the persisted execution ownership even when no conversation resume state remains. If close fails, keep the target registered and use terminal-run cleanup once the gateway is reachable.
A confirmed gateway validation rejection releases its allocation intent. Timeouts, disconnects and other ambiguous create failures keep their claims and never trigger an automatic replacement allocation.
Cleanup resolves the registered target and checks the recorded gateway and sandbox identity. It can delete a sandbox whose bridge has died, and reconcile an allocation intent when the gateway still exposes the matching ownership label. If allocation outcome is unknown and no matching sandbox is visible, it retains the resource claim for operator reconciliation. Never clear database ownership merely because a lease expired or a transport failed. Keep the target registered until cleanup completes. Pruning and admission release remain blocked by outstanding resources.
Command cancellation deletes the owned sandbox as a process fence, including descendants; that session must not be reused. Detach closes local transports only. File transfers and buffered command output are limited to 16 MiB per file/stream, with binary stdin, chunked writes and atomic rename. Missing files return null; permission or transport failures throw.
Verification
The opt-in suite uses a provisioned gateway/provider and prepared image:
LAUFWERK_OPENSHELL_GATEWAY=laufwerk-local \
LAUFWERK_OPENSHELL_PROVIDER=laufwerk-codex \
LAUFWERK_OPENSHELL_IMAGE_CACHE="$HOME/.laufwerk/images/openshell" \
bun run test:openshellIt makes real subscription model calls. It verifies editing, multiple turns, command execution, a human approval handoff between separate worker processes, a background writer surviving that handoff, read-only review, blocked premature write-back, confirmed cleanup, binary transfer, denied egress and command cancellation. Failure artifacts and unresolved resources are retained for explicit cleanup; successful temporary workflow storage is removed.