Move your agent’s application and dependencies into a Freestyle VM, then
configure its network access and model credentials from a trusted controller.
You can retain an inference.local URL or let the application call the provider’s
normal hostname with credentials injected at the edge.
This guide covers direct OpenAI and Anthropic HTTP inference, plus OpenRouter’s Chat Completions API. It maps the relevant OpenShell features and identifies policy or gateway behavior that needs a separate implementation before you cut over.
Why Migrate
Freestyle VMs are full Linux virtual machines designed for long-running, complex tasks. They are a good fit for coding and research agents that work for hours, days, or weeks, explore multiple approaches, and need to retain their environment’s state.
- Fast startup. Start workers from prepared snapshots with their runtimes, dependencies, and application state already in place. New tasks can reuse a ready environment without repeating its setup.
- Persistent workspaces. Keep an agent working for as long as its task needs. Pause preserves its running processes and memory so it can resume at the same point, even weeks later. This suits long-running work and tasks waiting for human input.
- Powerful Linux environments. Use full hardware virtualization with nested virtualization, FUSE, eBPF, and Linux networking. The Ubuntu images run Docker and systemd, and larger VM sizes give complex workloads room for browsers, databases, and development tools.
- Cheap snapshots and branching. Snapshot memory and disk with minimal interruption, then create multiple VMs from that exact state. Branch experiments, recover from failed attempts, or let research and reinforcement-learning agents explore several strategies from one checkpoint.
- Built for multiple customers and private networks. Key workspaces by user with VM slugs, control access with identities, and isolate tenant networks with VPCs and firewall rules. WireGuard tunnels let agents reach your existing private infrastructure without exposing its services publicly.
Model access fits into the same workspace: scope TLS rules to a VM or VPC, choose its model, and rotate credentials while keeping provider keys outside the guest. Use OpenRouter when a workspace needs models from multiple providers through one gateway.
Use the policy and gateway comparison below to account for any OpenShell controls your workload also depends on.
Map Your Current Setup
Start with your OpenShell image, startup command, workspace files, network policy, provider configuration, and any interceptors or middleware. Record the HTTP paths the workload actually calls, including discovery, token counting, uploads, and streaming connections.
| OpenShell configuration or operation | Freestyle migration |
|---|---|
| Sandbox image and startup command | Prepare a VM snapshot containing the application; start its process with vm.exec() or a service. |
openshell sandbox exec | VM exec for one-off commands, PTY for interactive processes. |
| Workspace files | Copy application files through VM files, Git, or SSH; rebuild OS and runtime dependencies in the VM. |
| Network endpoint grants | TLS rules for named HTTP egress and firewall rules for other network access. |
| Provider credential record | Store the credential in the controller’s secret store and supply it in a TLS header transform. |
| Selected inference model | JSON Patch add at /model on the generation endpoint. |
| Provider/model update | Replace the full TLS rule using freestyle.tls.rules.update. |
OpenShell supports creating sandboxes from containers and directories; Freestyle boots VM snapshots. There is no OpenShell image or policy import in this recipe. Use NVIDIA’s sandbox lifecycle reference to inventory the operations you need to replace. If the application requires Docker, run its container inside the VM.
Prepare And Start The VM
Prepare an Ubuntu-based snapshot containing
your application, its runtime and provider SDK, and ca-certificates. Install
packages while the builder has the required network access. Copy application
data, not OpenShell-managed proxy configuration, CA files, or provider secrets.
Run this TypeScript in your trusted controller after installing
npm install freestyle@latest. Set FREESTYLE_API_KEY and
FREESTYLE_SNAPSHOT_ID there:
import { Freestyle, type CreateTlsRuleOptions } from "freestyle";
function requiredEnv(name: string): string {
const value = process.env[name]?.trim();
if (!value) throw new Error(`Missing ${name}`);
return value;
}
const freestyle = new Freestyle();
const { vm, vmId } = await freestyle.vms.create({
snapshotId: requiredEnv("FREESTYLE_SNAPSHOT_ID"),
firewall: { rules: [] },
});
const result = await vm.exec("pwd");
console.log({ vmId, stdout: result.stdout, statusCode: result.statusCode });
This VM begins without a broad public-egress grant. Add the named model route below before starting the agent, plus any explicit grants its tools need. For private repositories, use the Git credential injection guide.
Keep An Inference Alias
With the empty firewall above, use the direct-provider configuration from the
OpenAI,
Anthropic, or
OpenRouter guide first.
Host-alias rules currently omit the automatic edge firewall grant: the alias
configuration below needs an existing allowed path to the edge or requests
time out, even with the correct CA. A direct-provider rule with
destination: { public: true } supplies that edge grant without broad public
egress. This is a routing limitation, separate from container certificate trust.
OpenShell’s inference.local routing
holds provider credentials and selects a model outside the sandbox. To preserve
that URL for an OpenAI Responses client, add the following to the controller
above. Supply OPENAI_API_KEY and OPENAI_MODEL only to the controller:
const options: CreateTlsRuleOptions = {
action: "allow",
domain: "inference.local",
source: { vmId },
destination: { host: "api.openai.com", port: 443 },
match: { method: ["POST"], path: { exact: "/v1/responses" } },
transform: [
{ headers: { authorization: `Bearer ${requiredEnv("OPENAI_API_KEY")}` } },
{ jsonPatch: [{ op: "add", path: "/model", value: requiredEnv("OPENAI_MODEL") }] },
],
};
const rule = await freestyle.tls.rules.create(options);
console.log({ ruleId: rule.id });
Freestyle configures the VM’s hostname resolution and system CA trust. Docker
containers need their own CA and hostname setup;
Python Requests and HTTPX need the appropriate bundle setting,
even when curl already works in the VM. The edge
forwards the original request path to the destination host, injecting the
configured key and model only when the method/path match. It does not translate
between provider protocols or add a base-path prefix.
Use the OpenAI guest example
with baseURL: "https://inference.local/v1". The key and model placeholders stay
the same. For other API surfaces, change the route and client together:
| Client | Guest SDK baseURL | Destination host | Exact POST path | Injected header |
|---|---|---|---|---|
| OpenAI Responses | https://inference.local/v1 | api.openai.com | /v1/responses | authorization: Bearer … |
| OpenAI Chat Completions | https://inference.local/v1 | api.openai.com | /v1/chat/completions | authorization: Bearer … |
| Anthropic Messages | https://inference.local | api.anthropic.com | /v1/messages | x-api-key: … |
| OpenRouter Chat Completions | https://inference.local/api/v1 | openrouter.ai | /api/v1/chat/completions | authorization: Bearer … |
The Anthropic client adds /v1 itself and supplies anthropic-version;
use its guest example.
Retain the Node CA-bundle setting from the provider guides.
For OpenRouter, use the OpenRouter recipe,
which also replaces the models fallback list. Its alias base URL includes
/api/v1; changing only the destination host will not add the /api prefix.
These are alternative configurations for one alias. To expose more than one
route to the same VM, assign separate aliases such as responses.internal and
messages.internal, each with its own rule and SDK client. Different VMs can
also use the same alias with VM-scoped rules selecting different backends.
Two rules for the same source and domain do not dispatch by match; the edge
selects a TLS rule before it evaluates the HTTP request conditions.
If preserving an alias is unnecessary, use the provider’s normal hostname with
destination: { public: true }, as in the
OpenAI and
Anthropic guides. For OpenRouter,
use openrouter.ai and retain /api/v1 in the SDK base URL.
Account For Policy And Gateway Differences
OpenShell denies unsupported inference paths and filters caller headers for
its configured provider. Freestyle’s match only gates transforms: an
unmatched request keeps its original headers and body and is forwarded. Header
transforms overwrite specified headers; they do not implement an arbitrary
header allowlist. If your application depends on endpoint denial or header
filtering, retain a gateway providing those controls. See
OpenShell inference routing
and Freestyle request matching.
OpenShell’s policy can bind network access to binaries and configure filesystem/process restrictions. Freestyle TLS rules scope grants to a VM or VPC; every process within that scope can use them. VM isolation does not reproduce an OpenShell process policy. Rebuild required in-guest restrictions explicitly or separate workloads into VMs.
OpenShell’s provider adapters include cloud-specific signing and translation. A Freestyle header transform and JSON Patch do not replace Bedrock signing, Vertex token exchange, credential refresh, or custom interceptor code. Keep the necessary provider adapter in a service your VM can reach. Similarly, middleware that inspects responses or arbitrary body fields needs its own implementation.
The JSON recipe supports ordinary HTTP inference and SSE responses. It does
not cover multipart uploads, JSONL batch files, or WebSocket sessions. Patching
/model does not constrain nested batch or fallback models. Check
transform limits before assuming
a gateway extension can be replaced with a patch.
Rule updates can take about five seconds to reach cached edge decisions and do not interrupt requests already in flight. Do not rely on an update to terminate an existing stream. Rotation instructions are in each provider guide.
Verify Before Cutting Over
- Run the application’s startup command and a representative tool action in the new VM. Verify workspace files and required network grants.
- Send a small provider request with placeholder guest credentials. Verify the response’s model and provider usage, then exercise streaming if you use it.
- Exercise every additional endpoint the agent needs. A generation-only match will not inject credentials into discovery, token counting, or uploads.
- Check an unmatched path with a local test origin to confirm your expected behavior. If you need it denied, verify that your additional gateway denies it; do not treat missing injected credentials as a firewall decision.
- Change the model or rotate the key through the controller and verify a fresh request after cache refresh. Keep the prior deployment available until the workload passes these checks.
When ready, direct new work to the Freestyle VM. Retire the old OpenShell sandbox and provider grants through your normal process. Deleting a Freestyle TLS rule removes that grant; revoking the provider credential is a separate operation.