Skip to content

Find a component

Loading the directory…

↑ ↓ to select · Enter to open · Esc to closeBrowse all →
Contributor login

Freestyle

Sandbox execution

Bijection source
git clone https://github.com/bijectionhq/freestyle.git
cd freestyle
git checkout 635839f04d41b7093b3dc13d30f94d773ff070d3

Bijection repository access is required. Request access.

Description

Sandbox execution

Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.

Available to evaluate

Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.

Getting started

  1. Get the source at the revision above and read its package documentation.
  2. Check the required Bijection SDK version, dependencies, and external service credentials.
  3. Follow the package setup instructions and the guide to using components.
  4. Exercise the component in your deployment according to your project’s verification rules.

Open source

Maintained source: bijectionhq/freestyle. Based on freestyle-sh/convex. License: MIT. Consult the repository for license terms, notices, and contributor attribution.

Compute runs with the external sandbox provider. Bijection connects application state and operations to that provider.

From the upstream publisher

Manage durable Freestyle VMs from Convex actions

Explore the demo ↗

Upstream overview

From the upstream package documentation.

Freestyle for Convex manages long-lived Freestyle VMs from Convex actions while keeping a reactive, cached record of each VM in your Convex database. It provides idempotent VM creation with retry recovery, tenant isolation via an app-supplied ownerId, and helpers for the full VM lifecycle including create, start, pause, exec, snapshot, resize, and delete. Read operations like get and list work in reactive Convex queries; mutating operations run in actions against the Freestyle API.

Use cases

From the upstream package documentation.

  • Building AI agent workspaces where each user or session needs an isolated full Linux environment with Docker and networking
  • Running untrusted or long-lived code execution sandboxes that need to be paused and resumed between user sessions
  • Snapshotting a configured VM state to use as a base image for future agent instances
  • Managing per-tenant VMs in a multi-tenant app where VM state needs to be reactively queryable alongside other Convex data
  • Executing shell commands inside a VM from a Convex action and returning structured results to the client

How it works

From the upstream package documentation.

Register the component in convex/convex.config.ts using app.use(freestyle) and set your FREESTYLE_API_KEY environment variable on the Convex deployment. Instantiate a Freestyle client in your app's convex/ directory by passing components.freestyle, then derive ownerId from server-side auth state rather than accepting it as a client argument to enforce tenant isolation.

Mutating operations such as create, start, pause, exec, resize, snapshot, and delete make external HTTP requests to the Freestyle API and must run inside Convex actions. The get and list methods read from cached Convex records and can be used in reactive queries. Call refresh from an action when you need the current remote state synced back into Convex.

VM creation is idempotent and retry-safe. The component writes a Convex record first, then provisions the remote VM with a random ownership marker stored in Freestyle metadata under the reserved convex_component_id key. If an action retries after the remote VM exists but before the Convex record was linked, the client locates the VM by slug and verifies the marker before linking, rejecting any VM with a mismatched marker.

Firewall rules are required on creation because Freestyle uses a default-deny network model. Use an empty rules array for a fully isolated VM or declare only the traffic your workload needs. Multiple Freestyle client instances can be configured with different API keys to support multiple Freestyle accounts from a single Convex deployment.

Upstream package FAQ

What kind of Linux capabilities do Freestyle VMs support?

Freestyle VMs are full Linux machines with root access and support for Docker, systemd, nested virtualization, FUSE, eBPF, and full Linux networking. If a workload can run on an EC2 instance, it can run in a Freestyle VM managed through @freestyle-sh/convex.

How does @freestyle-sh/convex handle retried Convex actions during VM creation?

The Freestyle component uses idempotent VM creation by reserving a Convex record first and embedding a random ownership marker in the VM's Freestyle metadata. If a Convex action is retried after the remote VM was created but before the record was linked, the component finds the VM by slug and verifies the marker before linking. A VM with the same slug but a different marker is rejected to prevent accidental adoption.

Do Freestyle VMs persist state when paused?

Yes. Freestyle VMs can be hibernated with their exact memory state preserved and resumed weeks later in that same state. Snapshots also capture both memory and disk, and creating a new VM from a snapshot restores it to the exact state of the original.

Which Freestyle operations must run in Convex actions versus queries?

Mutating operations that make external requests, including create, start, pause, exec, resize, snapshot, refresh, and delete, must run in Convex actions because they call the Freestyle API. The get and list methods read from cached Convex records and can run in reactive queries.

How do you configure the Freestyle API key for @freestyle-sh/convex?

Set the FREESTYLE_API_KEY environment variable on your Convex deployment using npx convex env set FREESTYLE_API_KEY your-api-key. The Freestyle client reads this key by default. You can also pass an explicit apiKey option to the Freestyle constructor to support multiple Freestyle accounts within the same Convex app.

Use with agents and CLI

Give your coding agent the component’s source identity and setup guidance.

agent-prompt.md
Help me integrate Freestyle with Bijection.

Read: https://bijection.com/components/freestyle/SKILL.md

Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.

Upstream README.md

View repository ↗

Documentation from freestyle-sh/convex, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.

Freestyle for Convex

Manage long-lived Freestyle VMs from Convex actions while keeping a reactive record of each VM in Convex.

Freestyle VMs are the most powerful VMs for AI agents. They are full Linux machines with root access, Docker, systemd, nested virtualization, FUSE, eBPF, and full Linux networking. VMs start quickly, preserve memory when paused, and can be snapshotted or branched from an exact machine state.

The component provides:

  • idempotent VM creation with retry recovery;
  • cached VM lifecycle and resource state for reactive queries;
  • create, refresh, start, pause, resize, update, exec, snapshot, and delete helpers;
  • explicit tenant isolation through an app-supplied ownerId; and
  • protection against adopting an unrelated Freestyle VM with the same slug.

Install

npm install @freestyle-sh/convex

Register the component in convex/convex.config.ts:

import { defineApp } from "convex/server";
import freestyle from "@freestyle-sh/convex/convex.config.js";

const app = defineApp();
app.use(freestyle);

export default app;

Set a Freestyle account API key on the Convex deployment:

npx convex env set FREESTYLE_API_KEY your-api-key

Create an API key from the Freestyle dashboard or with the CLI:

npx freestyle@latest login
npx freestyle@latest tokens create convex

Create an authenticated API

Instantiate the component client in your app's convex/ directory. Convex components cannot authenticate your app's users, so the wrapper must derive ownerId from trusted auth state. Do not accept ownerId as a client argument.

import { v } from "convex/values";
import { Freestyle } from "@freestyle-sh/convex";
import { components } from "./_generated/api.js";
import { action, query } from "./_generated/server.js";

const freestyle = new Freestyle(components.freestyle);

async function ownerId(ctx: {
  auth: { getUserIdentity(): Promise<{ subject: string } | null> };
}) {
  const identity = await ctx.auth.getUserIdentity();
  if (!identity) throw new Error("Unauthenticated");
  return identity.subject;
}

export const createWorkspace = action({
  args: { slug: v.string() },
  handler: async (ctx, { slug }) => {
    return await freestyle.create(ctx, {
      ownerId: await ownerId(ctx),
      slug,
      snapshotId: "freestyle/ubuntu",
      idleTimeoutSeconds: 600,
      firewall: {
        rules: [{ action: "allow", source: {}, destination: { public: true } }],
      },
    });
  },
});

export const listWorkspaces = query({
  args: {},
  handler: async (ctx) => {
    return await freestyle.list(ctx, { ownerId: await ownerId(ctx) });
  },
});

Freestyle has a default-deny network model, so firewall is required. Use { rules: [] } for an isolated VM, or declare only the traffic the workload needs. See the Freestyle quickstart for firewall examples.

Lifecycle and commands

Mutating operations make external requests and must run in Convex actions:

await freestyle.start(ctx, { ownerId, slug });
await freestyle.pause(ctx, { ownerId, slug });

const result = await freestyle.exec(ctx, {
  ownerId,
  slug,
  options: { command: "git status --short", timeoutMs: 300_000 },
});

await freestyle.resize(ctx, {
  ownerId,
  slug,
  options: { cpu: 8, memory: 16 * 1024, storage: 80 * 1024 },
});

const snapshot = await freestyle.snapshot(ctx, {
  ownerId,
  slug,
  options: { slug: `${slug}-configured` },
});

await freestyle.delete(ctx, { ownerId, slug });

get and list read cached records and can run in reactive Convex queries. Call refresh from an action when the application needs the current remote state:

const cached = await freestyle.get(ctx, { ownerId, slug });
const current = await freestyle.refresh(ctx, { ownerId, slug });

Creation and retries

create requires a stable Freestyle slug. The component reserves a Convex record first and adds a random ownership marker to the VM's Freestyle metadata. If the action is retried after the remote VM was created but before the Convex record was linked, the client finds the VM by slug and verifies that marker before linking it. A VM with the same slug and a different marker is rejected. The component reserves the convex_component_id metadata key and one of Freestyle's 64 VM metadata entries.

Freestyle slugs are account-wide. Two owners may use the same logical name in Convex, but their remote VM slugs still need to be unique within the Freestyle account. Prefix or hash tenant identifiers when generating slugs for a multi-tenant app.

Configuration

The constructor reads FREESTYLE_API_KEY from the consuming Convex app by default. Explicit options support multiple Freestyle accounts and local API testing:

const freestyle = new Freestyle(components.freestyle, {
  apiKey: process.env.SECONDARY_FREESTYLE_API_KEY,
  baseUrl: "https://api.freestyle.sh",
});

Each separately registered Convex component instance has its own tables. Each Freestyle client may also use a different API key.

Development

npm install
CONVEX_AGENT_MODE=anonymous npx convex init
npm run build:codegen
npx convex codegen --typecheck disable
npm test
npm run typecheck
npm run lint
npm run format:check

The full backend-only example is in example/convex.

Keywords