#
URL: https://docs.freestyle.sh/v2/about.md
Content:
---
title: About Freestyle
description: Infrastructure for code you didn't write
---
## What is Freestyle?
Freestyle provides infrastructure for managing code you didn't write. Whether that code comes from your users or from AI, managing it presents different challenges than running code you wrote yourself. We help solve those challenges with APIs designed for each stage of the code lifecycle:
- [Git](/v2/git): Multi-tenant Git hosting for storing and versioning code
- [VMs](/v2/vms): Fast virtual machines for development, testing, and execution
- [Serverless Deployments](/v2/serverless/deployments): Deploy web applications with sub-second cold starts
- [Serverless Runs](/v2/serverless/runs): One-shot code execution without deployment
- [Domains](/v2/domains): Custom domain management with automatic SSL
## Who Uses Freestyle?
**AI App Builders** use Freestyle to manage code in [Git](/v2/git), iterate on it with [VMs](/v2/vms), and deploy it with [Serverless Deployments](/v2/serverless/deployments). If you're building an AI App Builder, check out [Building an AI App Builder on Freestyle](/guides/app-builder).
**Workflow Engines** use [Serverless Runs](/v2/serverless/runs) to execute custom code that powers their workflows. They can also store workflows in [Git](/v2/git) to get forking, branching, and versioning.
**AI Coding Agents** use [VMs](/v2/vms) to iterate on code, run browser tests, and commit changes to [Git](/v2/git).
**Chatbots and AI Assistants** integrate with [Serverless Runs](/v2/serverless/runs) to run code for data transformations, API calls, or analytics. For heavier workloads, they use [VMs](/v2/vms).
**Task Agents** use [VMs](/v2/vms) with browsers to complete real-world tasks, [Git](/v2/git) to store artifacts with version control, and [Serverless Deployments](/v2/serverless/deployments) to display reports and results.
## Why Freestyle?
Freestyle isn't just a place to run, deploy, or store code—it's all of that together. As you scale, managing disparate services for each part of the lifecycle gets harder and less performant. All of Freestyle's APIs are designed to work together, letting you trace code through its lifecycle, debug faster, and scale easily.
**Lifecycle Management**: All APIs integrate with Freestyle Git. See what code is deployed, where it came from, and its full history.
**Multi-Tenant by Default**: Managing thousands of codebases from users or AI has observability challenges that don't exist when managing your own code. Rate limit a user, a deployment, or a specific job—we make it easy.
**Battle Tested**: These APIs started as our internal infrastructure. This documentation is deployed through our Web API, and this domain is managed through our Domains API. We use these APIs every day.
}
#
URL: https://docs.freestyle.sh/v2.md
Content:
---
title: Getting Started
# description: Infrastructure for code you didn't write
---
import { Card, Cards } from 'fumadocs-ui/components/card';
import { Callout } from 'fumadocs-ui/components/callout';
import { YellowTilde, GreenCheck, RedX } from "@/components/marks";
import { cursorUrl, vscodeUrl } from "@/lib/cursor-link";
import Image from "next/image";
import { VscVscode } from "react-icons/vsc";
import AddToCursor from "../../public/cursor-install-dark.svg";
import InstallSandboxes from "../../src/components/installSandboxes";
Freestyle is infrastructure for code you didn't write. Whether it's for your users or your AI agents, managing code you didn't write presents unique challenges that traditional infrastructure wasn't designed to solve.
### Get an API Key
Get your API key from the [Freestyle Dashboard](https://admin.freestyle.sh). Store it in an environment variable named `FREESTYLE_API_KEY` so our SDKs can automatically detect it.
### Install the SDK
Otherwise, see the [API Reference](/API-Reference/web/handle_deploy_web_v2) for direct HTTP usage.
### Add to Your Editor
#### Cursor
#### VSCode
Add to VSCode
#### Claude Code
```bash
claude mcp add --transport http freestyle-docs https://docs.freestyle.sh/api/mcp/mcp
```
#### Claude Desktop
Add this to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"freestyle-docs": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://docs.freestyle.sh/api/mcp/mcp"]
}
}
}
```
## Talk to Us
Questions or need help? Join our [Discord server](https://discord.gg/YTRprVkdnz).
}
#
URL: https://docs.freestyle.sh/v2/sdk-patterns.md
Content:
---
title: SDK Design Patterns
description: Learn the consistent patterns used throughout the Freestyle SDK.
---
The Freestyle SDK follows consistent design patterns to make the API predictable and easy to learn. Once you understand these patterns, you'll know how to use any part of the SDK.
## Object-Based Parameters
SDK methods take a single object parameter with named properties:
```ts
const { vm } = await freestyle.vms.create({
workdir: "/app",
ports: [{ port: 443, targetPort: 3000 }],
persistence: { type: "ephemeral" },
});
```
This makes it clear what each parameter does and keeps the API flexible as new options are added.
## Destructured Returns
Creation methods return an object with both the helper instance and metadata:
```ts
const { vm, vmId, domains } = await freestyle.vms.create();
const { identity, identityId } = await freestyle.identities.create();
const { repo, repoId } = await freestyle.git.repos.create();
```
The helper instance is always named after the namespace noun (e.g., `vm` from `vms`, `identity` from `identities`). This gives you both the instance for interacting with the resource and the IDs/metadata you need.
## Namespaced Structure
APIs are organized by resource type with consistent operations:
```ts
freestyle.vms.create()
freestyle.vms.list()
freestyle.vms.delete({ vmId })
freestyle.git.repos.create()
freestyle.git.repos.list()
freestyle.git.repos.delete({ repoId })
freestyle.identities.create()
freestyle.identities.list()
freestyle.identities.delete({ identityId })
```
The pattern is `freestyle..()` where operations include `create`, `list`, `delete`, `update`, etc.
### Delete operations use the namespace
```ts
const { repoId } = await freestyle.git.repos.create({ ... });
await freestyle.git.repos.delete({ repoId });
```
This works whether you created the resource in the current code or are referencing one that exists elsewhere.
## Flexible Resource References
Parameters accept multiple formats for the same resource type:
```ts
// Using a full URL
const { vm } = await freestyle.vms.create({
gitRepos: [{
repo: "https://github.com/owner/repo",
path: "/app"
}]
});
// Using a Freestyle repo ID
const { vm } = await freestyle.vms.create({
gitRepos: [{
repo: "repo_abc123",
path: "/app"
}]
});
```
The SDK automatically handles different formats, so you can use whatever is most convenient.
## Inline Configuration Options
Options are specified inline with string literals:
```ts
const { vm } = await freestyle.vms.create({
persistence: {
type: "ephemeral",
deleteEvent: "OnSuspend",
}
});
const { vm } = await freestyle.vms.create({
persistence: {
type: "sticky",
priority: 5,
}
});
```
TypeScript's autocomplete will show you all available options as you type.
## Consistent List Operations
All list operations follow the same pattern:
```ts
const { vms, nextPageToken } = await freestyle.vms.list({
pageToken?: string,
pageSize?: number,
filter?: { ... }
});
const { repos, nextPageToken } = await freestyle.git.repos.list({
pageToken?: string,
pageSize?: number,
filter?: { ... }
});
```
Lists return an object with the resource array (matching the namespace name) and an optional `nextPageToken` for pagination.
## Practical Examples
### Creating resources with proper destructuring
```ts
// VMs
const { vm, vmId, domains } = await freestyle.vms.create({
ports: [{ port: 443, targetPort: 3000 }],
});
// Git repos
const { repo, repoId } = await freestyle.git.repos.create({
url: "https://github.com/company/repo",
});
// Identities with permissions
const { identity, identityId } = await freestyle.identities.create();
await identity.permissions.vms.create({
vmId: vmId,
});
const { token } = await identity.createToken();
```
### Using helper instances
```ts
// VM instance methods
await vm.start();
await vm.suspend();
await vm.exec("npm install");
// Identity instance methods
await identity.permissions.vms.create({ vmId });
const { tokens } = await identity.listTokens();
```
### Familiar APIs use standard conventions
Some helpers like filesystem operations follow established conventions:
```ts
// Filesystem uses positional parameters (like Node.js fs)
await vm.fs.writeTextFile("/app/config.json", JSON.stringify(config));
const content = await vm.fs.readTextFile("/app/config.json");
// Simple operations can return values directly
const result = await vm.exec("ls");
const text = await vm.fs.readTextFile("/file.txt");
```
### List and delete patterns
```ts
// List all VMs
const { vms } = await freestyle.vms.list();
// List with pagination
const { vms, nextPageToken } = await freestyle.vms.list({
pageSize: 50,
});
if (nextPageToken) {
const { vms: moreVms } = await freestyle.vms.list({
pageToken: nextPageToken,
});
}
// Delete resources using namespace method
await freestyle.vms.delete({ vmId: vm.vmId });
await freestyle.git.repos.delete({ repoId: repo.repoId });
await freestyle.identities.delete({ identityId: identity.identityId });
```
### Resource references in multiple formats
```ts
// Snapshots accept VM ID or snapshot ID
const { vm } = await freestyle.vms.create({
snapshotId: "snap_abc123",
});
// Git repos accept URL or Freestyle repo ID
const { vm } = await freestyle.vms.create({
gitRepos: [
{ repo: "https://github.com/owner/repo", path: "/app" },
{ repo: "repo_xyz789", path: "/lib" },
],
});
```
## Why These Patterns?
These patterns make the SDK:
- **Predictable** - If you know how one API works, you know how they all work
- **Discoverable** - TypeScript autocomplete guides you to the right usage
- **Flexible** - New features can be added without breaking existing code
- **Type-safe** - Catch errors at compile time instead of runtime
- **Readable** - Code is self-documenting with named parameters
}
#
URL: https://docs.freestyle.sh/v2/why.md
Content:
---
title: Why Freestyle
description: Somebody else already does X. Why should I use Freestyle?
---
### Our competitors are great
GitHub is highly reliable and universally trusted with storing our code. Daytona
and E2B can spin up sandboxes in milliseconds. And Vercel can serve your website
with incredibly low latency all over the world. The catch is, none of these
platforms were designed to work together **efficiently**. After just a few
operations, you've shipped your data all over country multiple times and paid
the price.
### We don't have any competitors
When you store a repo on freestyle, develop it on a freestyle vm, and deploy it
to freestyle's network, your data never even has to touch a network cable. Every
single one of our services is optimized for bare metal. We build our own git
server, our own VM, our own js runtime, and our own hardware to cut down latency
and cost at every level. It's **physically impossible** to achieve competitive
performance with any combination of other platforms.
### Why you should care
Over the past decade, most software has gotten less and less efficient as
networks and computers have gotten faster. Us humans are limited in how fast we
can move, so this has gone largely unnoticed. Agents on the other hand have no
such limitations. Agents can move as fast as their infrastructure allows them
to. Any inefficiency caused by transferring context between platforms has direct
impact on your competitiveness in the market. Using Freestyle is a competitive
advantage.
}
#
URL: https://docs.freestyle.sh/v2/domains/deploy-to-custom-domain.md
Content:
---
title: Deploy to a Custom Domain
description: Configure DNS to point your domain at Freestyle.
---
Once you have [verified ownership of a domain](/v2/domains), you need to configure your DNS to point at Freestyle's servers.
## APEX Domain
To deploy to an APEX domain (e.g. `yourdomain.com`), add an A record pointing to Freestyle:
```
Type: A
Name: @
Value: 35.235.84.134
```
## Subdomain
To deploy to a subdomain (e.g. `app.yourdomain.com`), add an A record for that subdomain:
```
Type: A
Name: app
Value: 35.235.84.134
```
## Wildcard Subdomain
To deploy to all subdomains of a domain (e.g. `*.yourdomain.com`), add a wildcard A record:
```
Type: A
Name: *
Value: 35.235.84.134
```
When configuring DNS records, it's easy to accidentally create a record like
`yourdomain.com.yourdomain.com`. To verify your record is correct, run
`dig yourdomain.com` and check the output:
```
;; ANSWER SECTION:
yourdomain.com. 60 IN A 35.235.84.134
```
## Deploy
Once your DNS is configured, you can deploy to your custom domain:
```ts
import { freestyle } from "freestyle-sandboxes";
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
domains: ["yourdomain.com"],
build: true,
});
```
}
#
URL: https://docs.freestyle.sh/v2/domains.md
Content:
---
title: Verify your domain
---
Domains allow you to configure custom domain names for your Serverless Deployments.
Custom domains for VMs is coming soon.
Before you can use a custom domain on Freestyle, you need to verify that you own
the domain. You can do this programmatically using the freestyle SDK or through
in the dashboard under [Custom Domains](https://admin.freestyle.sh/dashboard/domain-ownership).
```ts
import { freestyle } from "freestyle-sandboxes";
const { record, instructions } = await freestyle.domains.verifications.create({
domain: "example.com",
});
console.log(instructions);
console.log(record);
```
Navigate to your domain's DNS settings and create a TXT record with the following details:
Name: _freestyle-verification.example.com
Value: \
After adding the record, return here to complete the verification process. Note that DNS changes may take some time to propagate. If verification fails after waiting a few minutes, try setting the Name to just "_freestyle-verification". Some DNS providers automatically append the domain name to the record name.
```
{
type: "TXT",
name: `_freestyle-verification.example.com`,
value: "",
}
```
## Complete verification
After adding the DNS record, you can complete the verification process by domain
name or verification ID. If you verify by domain, freestyle will attempt to
verify all pending verifications for that domain, and is the most common
approach. If you are verifying a domain on behalf of multiple different users,
you may want to verify by verification ID instead.
```ts
import { freestyle } from "freestyle-sandboxes";
await freestyle.domains.verifications.complete({
domain: "example.com",
});
```
```ts
import { freestyle } from "freestyle-sandboxes";
const { verificationId } = await freestyle.domains.verifications.create({
domain: "example.com",
});
// After adding the DNS record...
await freestyle.domains.verifications.complete({
verificationId: verificationId,
});
```
Verifying a domain proves your ownership but does not point it at Freestyle.
You cannot deploy to it yet. See [Deploy to a Custom Domain](/v2/domains/deploy-to-custom-domain)
to configure your DNS records.
}
#
URL: https://docs.freestyle.sh/v2/git/about.md
Content:
---
title: About Freestyle Git
description: An introduction to the Freestyle Git service for managing source code in AI projects, followed by a comparison of alternative source control methods and their relative strengths.
---
import { YellowTilde, GreenCheck, RedX } from "@/components/marks";
This document explores various source control strategies for multi-tenant AI applications, comparing their advantages and disadvantages to help you choose the best fit for your needs.
To get started with Freestyle's Git service, check out the [Getting Started Guide](/v2/git).
## Overview
Freestyle Git Service is a hosted Git platform designed specifically for multi-tenant applications, enabling seamless management of repositories across numerous users and organizations without the burden of maintaining your own Git infrastructure. It provides an API for programmatically creating and managing repositories, controlling access through identity-based permissions tailored for diverse roles like CI/CD pipelines or team members, and setting up event-driven automation triggers. The service further enhances workflows with features for CI/CD integration, direct application deployments from repositories, Git objects access for inspection, support for Git LFS (Large File Storage), bidirectional synchronization with GitHub repositories, and compatibility with the GitHub Files API.
## Thinking about Source Control
In multi-tenant applications, where you're managing codebases for numerous users and organizations, maintaining a centralized source of truth for code becomes critical to handle version tracking, collaboration, and secure access. A well-designed system delivers reliable version history, seamless collaboration, and robust security, while avoiding issues like data loss, access breaches, or workflow inefficiencies. Key considerations when choosing a system include:
- Multi-tenant support for segregated repositories
- Fine-grained permissions and identity management
- Data accessibility for efficient retrieval, sharing, and manipulation of code across different users and environments
- API integration for programmatic control and CI/CD support for automated workflows
- "Time travel" capabilities to navigate backward and forward through code history
- Debuggability and observability for troubleshooting and monitoring changes
- Support for branching and forking to facilitate parallel development and experimentation
- Ease of integration with existing tools and services
To help you choose, here are a range of source control techniques commonly applied, highlighting their benefits and shortcomings.
## 1. The VM as the Source of Truth
A common naive approach is to use a virtual machine (VM) or container as the source of truth for your code. In this model, your users/AI develop directly on the VM, and it serves as the central repository for your customers' code.
This is a simple and straightforward approach from an implementation perspective, but it has several significant drawbacks:
- **Lack of Version Control**: You lose the ability to track changes, revert to previous versions, and collaborate effectively. If something goes wrong, you have no history to fall back on.
- **Lack of Automation**: Developing directly on a VM often leads to missed opportunities for automation, as you may not integrate with CI/CD pipelines effectively.
- **Security Risks**: Sensitive information may be stored directly on the VM, making it harder to manage access and permissions securely.
- **Lack of Portability**: If you need to move your code to a different environment or share it with others, you have to manually copy files, which is error-prone and cumbersome.
- **No Backup**: If the VM fails or is lost, you risk losing all your code and data without any backup.
- **Difficulty in Testing**: Testing changes becomes more challenging, as you may not have a clear way to isolate and test specific features or bug fixes.
- **Complex and Expensive APIs**: Building APIs becomes inherently complex, slow, brittle, and expensive since the VM must be awakened for every request, requiring custom scripts for basic operations and creating significant latency and resource overhead.
- **Hard to Debug**: Debugging issues becomes difficult, as you may not have a clear view of the code's history or an easy way to access the code outside of the VM.
### 1b. Git on the VM as the Source of Truth
Using Git on the VM as the source of truth is a step up from the previous approach, as it introduces version control. However, it still suffers from many of the same drawbacks.
While you can track changes and collaborate better, you still face issues with scalability, portability, and backup. The VM must still be accessed for every Git operation, which can lead to performance bottlenecks and increased runtime cost.
## 2. S3 + Database as the Source of Truth
A more advanced approach is to use a combination of S3 (or similar object storage) and a database as the source of truth. In this model, your customers' code is stored in S3, and you use a database to track metadata, versions, and changes.
When the VM needs to access the code, it retrieves it from S3, and any changes are written back to S3.
This approach has several advantages compared to using the VM state as the source of truth:
- **Version Control**: You can track changes, revert to previous versions, and collaborate more effectively.
- **Security**: Storing your code in S3 and using a database enhances security by managing access and permissions more effectively.
- **Portability**: Moving or sharing your code is simplified as S3 handles file storage independently.
- **Durability**: Utilizing S3 ensures that your code is highly available and can be backed up and restored in case of data loss.
- **API Simplicity**: Building APIs becomes simpler, as you can directly access code files in S3 and metadata from the database without needing to wake up a VM for every request.
However, this approach has drawbacks of its own:
- **Data Transfer Overhead**: Full files/directories need to be regularly read from and written to S3, which can be slow and costly due to data transfer and storage costs.
- **Complexity**: Managing the interaction between S3, the database, and the VM can introduce complexity, especially when handling concurrent updates.
- **Manual versioning**: You need to implement your own versioning system, which can be error-prone and requires additional development effort.
## 3. Git as the Source of Truth
Using a hosted Git API service (like GitHub or Freestyle Git) as the source of truth is a popular and effective approach. In this model, your customers' code is stored in Git repositories, and you use Git's built-in version control features to manage changes. The VM maintains a local clone of the repository, which it syncs with the remote repository when changes are made.
Git (hosted) as the source of truth has several advantages:
- **Easy Integration**: Manage Git repositories via API and SDKs, without managing the underlying infrastructure. Faster development cycles and easier integration with existing tools.
- **Optimized Data Transfer**: Git uses efficient data transfer mechanisms, such as delta encoding and compression, to minimize the amount of data transferred during operations.
- **Stateless Operations**: Git operations are stateless, meaning you can perform actions like cloning, pushing, and pulling without needing to maintain a persistent connection to a VM.
### 3a. GitHub
GitHub is a popular choice for hosting Git repositories, but it has some limitations for AI app builders:
- **Lack of Ownership**: You do not own the infrastructure or the data, which can lead to vendor lock-in and potential data loss if GitHub changes its policies or services.
- **Cost/Licensing**: You are dependent on GitHub's infrastructure and policies, which may not align with your needs.
- **Complex API**: GitHub Apps require managing multi-step authentication flows, handling token expiration, and coordinating installations across different organizations, adding significant complexity for programmatic repository management.
- **Limited Customization**: You may have restricted control over the repository setup and features.
### 3b. Freestyle Git
Freestyle provides a comprehensive Git API that enables you to manage Git repositories, control access permissions, and set up automation triggers.
Freestyle's Git API offers unique advantages tailored for AI app builders. Key features include:
- **Natively Multi-Tenant**: Freestyle's Git API is designed for multi-tenant applications, allowing you to manage repositories for multiple users and organizations seamlessly.
- **Robust Identity Management**: Freestyle provides built-in identity management, allowing you to create and manage identities for different purposes (e.g., CI/CD, team members) with fine-grained access control.
- **Seamless Integration**: Freestyle's triggers system facilitates easy collaboration with CI/CD systems and external services.
- **GitHub Sync**: Built-in synchronization with GitHub, including app/auth management, allowing you to maintain synchronized code across both platforms while leveraging Freestyle's infrastructure.
## All Together
Put together, depending on your needs and the scale of your application, you can choose from various source control methods. Below is a comparison table summarizing the strengths and weaknesses of each approach:
| Feature | VM | Git on VM | S3 + DB | GitHub | Freestyle Git |
| ------------------------ | --------------- | --------------- | --------------- | --------------- | -------------- |
| Version Control | | | | | |
| Time Travel | | | | | |
| Branching/Forking | | | | | |
| Fine-Grained Permissions | | | | | |
| Identity Management | | | | | |
| Data Accessibility | | | | | |
| API Integration | | | | | |
| CI/CD Support | | | | | |
| Debuggability | | | | | |
| Security | | | | | |
| Portability | | | | | |
| Backup/Durability | | | | | |
| Multi-Tenant Support | | | | | |
| Data Ownership | | | | | |
| Cost-Effectiveness | | | | | |
| Customization | | | | | |
| GitHub Sync | | | | N/A | |
| Automation Triggers | | | | | |
We've built this API specifically for multi tenant apps, ensuring that it meets the unique needs of managing codebases on behalf of users and organizations. It provides a powerful and flexible solution for source control, enabling you to focus on building your AI applications without worrying about the underlying infrastructure.
If you're interested in trying it, you should read the [Getting Started Guide](/v2/git)
}
#
URL: https://docs.freestyle.sh/v2/git/github-sync.md
Content:
---
title: GitHub Sync
---
Freestyle provides bidirectional synchronization between repositories on Freestyle and
GitHub, allowing you to maintain synchronized code across both platforms.
## How It Works
When you push code to either platform, changes are automatically synchronized to
the other. Freestyle uses GitHub Apps to provide secure, repository-specific
access and prevents data loss by detecting diverged branches before applying
changes.
## Setup Process
### Step 1: Create a GitHub App
Navigate to the **Git > Sync** section in your [Freestyle Admin
Dashboard](https://admin.freestyle.sh) and click **"Create GitHub App"**.
Choose a custom name for your app and click **"Create App"**. You'll be
redirected to GitHub to confirm, then back to Freestyle where your credentials
are automatically stored.
You can also bring your own GitHub App by clicking the dropdown and selecting
**"Use Existing App"**. You'll need to provide the App ID and Private Key.
### Step 2: Install the App
The GitHub App must be installed on repositories you want to sync.
From the sync page, click **"Install on GitHub"** and choose which repositories
to grant access to. You can grant access to all repositories or select specific
ones.
For app builders: Share your GitHub App's installation page URL with users so
they can install it on their repositories.
## Linking Repositories
Once your GitHub App is installed, you can configure repository synchronization.
### Via Admin Dashboard
1. Navigate to **Git > Repositories**
2. Select the Freestyle repository
3. Click **"Configure GitHub Sync"**
4. Choose the corresponding GitHub repository
5. Save the configuration
### Via SDK
Configure sync programmatically using the repository instance.
```ts
import { freestyle } from "freestyle-sandboxes";
const { repo } = await freestyle.git.repos.create();
// Enable GitHub sync (the GitHub repository must have your GitHub App installed)
await repo.githubSync.enable({ githubRepoName: "owner/repo" });
// Disable GitHub sync
await repo.githubSync.disable();
```
### Getting Sync Configuration
Check if a repository has GitHub sync configured.
```ts
const syncConfig = await repo.githubSync.get();
if (syncConfig) {
console.log(`Synced with GitHub: ${syncConfig.githubRepoName}`);
}
```
## Sync Behavior
### Automatic Triggers
Synchronization happens automatically when you push to either repository,
including branch operations like creation, updates, and deletions.
### What Gets Synced
- **All branches**: Including main, feature branches, and release branches
- **Commit history**: Complete Git history is preserved
- **Tags**: Git tags are synchronized between repositories
- **Branch deletions**: Removing branches on one side removes them on the other
## Conflict Handling
When conflicts are detected, Freestyle prioritizes data safety and will not
automatically merge conflicting branches. Conflicts must be resolved manually in either repository.
Once resolved, sync resumes automatically.
Freestyle never force pushes or overwrites branches to prevent data loss.
}
#
URL: https://docs.freestyle.sh/v2/git/hooks.md
Content:
---
title: Triggers and Webhooks
---
Triggers let you automate actions when events occur in your git repositories,
such as pushes to specific branches.
## Creating a Trigger
Create a webhook trigger that fires on push events.
```ts
import { freestyle } from "freestyle-sandboxes";
const { repo } = await freestyle.git.repos.create();
await repo.triggers.create({
trigger: {
event: "push",
branch: ["main"], // optional: filter by branch
globs: ["*.js"], // optional: filter by file patterns
},
action: {
type: "webhook",
url: "https://your-webhook-url.com",
},
});
```
When your trigger is activated, a payload is sent to the specified URL in the following format.
```ts
interface GitTriggerPayload {
event: "branchUpdate";
repoId: string;
branch: string; // The branch that was updated
commit: string; // The SHA of the commit
}
```
## Local Development
For local development, use a tool like [Tailscale](https://tailscale.com) to
create a secure tunnel to your localhost.
Install Tailscale following the [quickstart
guide](https://tailscale.com/kb/1017/install), then expose your local server:
```bash
# Replace 3000 with your server's port
tailscale funnel 3000
```
The output provides a public URL you can use as the webhook URL in your trigger
configuration.
## Webhook Signing
Webhooks include a signature in the `x-freestyle-signature` header for verifying
request authenticity. The signature is a JWT (EdDSA) containing a `body_sha256`
field with the SHA256 hash of the webhook payload.
The public key for verification is available at
`https://git.freestyle.sh/.well-known/jwks.json`.
```ts
import crypto from 'crypto';
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://git.freestyle.sh/.well-known/jwks.json')
);
async function verifyWebhook(request) {
const signature = request.headers['x-freestyle-signature'];
const bodyText = await request.text(); // Get raw body text
// Verify JWT signature
const { payload } = await jwtVerify(signature, JWKS, {
algorithms: ['EdDSA'],
});
// Verify payload hash
const payloadHash = crypto
.createHash('sha256')
.update(bodyText)
.digest('hex');
if (payload.body_sha256 !== payloadHash) {
throw new Error('Payload verification failed');
}
// Parse body after verification
const body = JSON.parse(bodyText);
return { payload, body };
}
```
## Listing Triggers
List all triggers for a repository.
```ts
const triggers = await repo.triggers.list();
console.log(triggers);
```
## Deleting Triggers
Remove a trigger by its ID.
```ts
await repo.triggers.delete({ triggerId: "trigger-id" });
```
}
#
URL: https://docs.freestyle.sh/v2/git.md
Content:
---
title: Getting Started with Git
---
import { SiTypescript } from '@icons-pack/react-simple-icons';
This document provides a technical introduction to using Freestyle's Git service.
For a high-level overview of the service, its features and why to use it see [About Freestyle Git](/v2/git/about).
../../../content-snippets/setup-env.mdx
## Creating a Git Repository
Calling `create` in the `git.repos` namespace will create a new empty git
repository.
```ts
import { freestyle } from "freestyle-sandboxes";
const { repo } = await freestyle.git.repos.create();
```
You can also fork an existing repository and all its history by providing the
URL and branch.
```ts
await freestyle.git.repos.create({
source: {
url: "https://github.com/user/repo.git",
}
});
```
Or you can import a repository's files directly without any history. Raw files,
tar urls, and zip urls are also supported types under `import`.
Imports require a commit message to be provided for the initial commit.
```ts tab=" git.ts"
await freestyle.git.repos.create({
import: {
type: "git",
commitMessage: "import from git",
url: "https://github.com/user/repo.git",
},
});
```
```ts tab=" files.ts"
await freestyle.git.repos.create({
import: {
type: "files",
commitMessage: "import from files",
files: {
"README.md": {
content: "# Hello World\nThis is my repo.",
},
},
},
});
```
```ts tab=" tar.ts"
await freestyle.git.repos.create({
import: {
type: "tar",
commitMessage: "import from tar",
url: "https://example.com/files.tar.gz",
},
});
```
```ts tab=" zip.ts"
await freestyle.git.repos.create({
import: {
type: "zip",
commitMessage: "import from zip",
url: "https://example.com/files.zip",
},
});
```
## Authentication
By default git repositories are private. To clone one, so you'll need to
create a freestyle identity and access token.
```ts
const { identity } = await freestyle.identities.create();
await identity.permissions.git.grant({
permission: "write",
repoId: repoId,
});
const { token } = await identity.tokens.create();
console.log(`git clone https://x-access-token:${token}@git.freestyle.sh/${repoId}`);
```
You can also use bearer auth and your freestyle api key to clone a repository.
This is mostly useful for testing.
```bash
git -c http.extraHeader "Authorization: Bearer " clone https://git.freestyle.sh/
```
Public repositories can be cloned without authentication, but you'll still need
authentication to push changes.
```ts
await freestyle.git.repos.create({
public: true,
});
```
## Accessing Repository Contents
You can use the contents namespace to read files in a git repository without cloning it.
```ts
const repo = freestyle.git.repos.ref({ repoId: "your-repo-id" });
// Get a file's contents (base64 encoded)
const file = await repo.contents.get({ path: "README.md" });
const content = atob(file.content);
// Get contents at a specific branch, tag, or commit
const oldFile = await repo.contents.get({
path: "src/index.ts",
rev: "v1.0.0",
});
// Get a directory listing
const dir = await repo.contents.get({ path: "src" });
```
Download an entire repository as a tarball or zip file.
```ts
// Download as tarball
const tarball = await repo.contents.downloadTarball({ rev: "main" });
// Download as zip
const zip = await repo.contents.downloadZip({ rev: "main" });
```
## Comparing Commits
Compare two revisions (branches, tags, or commit SHAs) to see the differences.
```ts
const repo = freestyle.git.repos.ref({ repoId: "your-repo-id" });
const diff = await repo.compare({
base: "main",
head: "feature-branch",
});
console.log(diff.status); // "identical" | "ahead" | "behind" | "diverged"
console.log(diff.ahead_by); // commits head is ahead of base
console.log(diff.behind_by); // commits head is behind base
console.log(diff.total_commits);
for (const file of diff.files) {
console.log(`${file.status}: ${file.filename} (+${file.additions} -${file.deletions})`);
}
```
}
#
URL: https://docs.freestyle.sh/v2/vms/about.md
Content:
---
title: About Freestyle VMs
description: An introduction to Freestyle's Virtual Machine (VM) service and what makes it different.
---
This document provides a high-level overview of Freestyle's VM service, its features, and why to use it.
To get started with Freestyle's VM service, check out the [Getting Started Guide](/v2/vms).
## Overview
Freestyle VMs are full Linux virtual machines designed for speed and flexibility. They start in under a second, pause and resume instantly, and can be forked mid-execution without interruption.
## Features
### Sub-Second Startup
VMs provision in under 800ms from API request to running machine. Traditional cloud VMs take minutes. This speed comes from memory snapshots—when you get a VM, it's already booted and running, not starting from a powered-off state.
### Pause and Resume
VMs can be paused in their running state and resumed later in under 100ms, returning to the exact same memory state. A game server can pause when no players are connected and resume instantly when someone joins—no boot time, no lost state.
### Forking
A running VM can be forked without noticeably pausing the original. Fork it N times with minimal performance impact. If an AI agent is browsing a website and wants to explore 20 different paths, fork the VM 20 times and let each copy explore independently—all from the same browser state.
### Templates and Caching
VMs are built from layered templates. You can create custom layers, and the system automatically caches them for fast startup. See [Templates and Snapshots](/v2/vms/templates-snapshots) for details.
### Integrations
Integrations add common functionality to VMs: language runtimes like [Node.js](/v2/vms/integrations/node), [Python](/v2/vms/integrations/python), [Bun](/v2/vms/integrations/bun), and [Ruby](/v2/vms/integrations/ruby), plus databases, browsers, and more. Each integration includes caching for fast startup and a client SDK for easy use. You can also [build custom integrations](/v2/vms/custom-integrations).
### Access Control
VMs support multiple Linux users and groups, [SSH access](/v2/vms/ssh-access), and API access via identities and tokens.
## Beyond Sandboxes
Many sandbox platforms restrict what you can run to keep things simple. Freestyle VMs are full Linux environments with low-level access: SSH, systemd, multiple users and groups, and configurable networking. Some sandboxes limit you to building around containers, Freestyle lets you run any Linux-compatible software, including any container, or multiple containers in one VM. Other sandboxes also build out the utilities like `runCode` as a core part of their platform, which limits what their users can build. Freestyle's flexible integration scheme means our base images are minimal, and you can add exactly what you need. You can see this in how Freestyle has integrations for tons of programming languages, while other sandboxing platforms often support just TypeScript or Python.
## Beyond EC2s and Traditional VMs
Traditional cloud VMs are slow — they take minutes to start. Kubernetes on AWS/GCP can take hours to get a new node. They are built around large chunks of compute without secure virtualized isolation of small pieces in mind. While this makes sense for big applications, if you're trying to run lots of small VMs for AI workloads, the overhead is significant and the performance is poor.
}
#
URL: https://docs.freestyle.sh/v2/vms/custom-integrations.md
Content:
---
title: Custom Integrations
description: Build your own integrations to extend VMs.
---
You can create custom integrations by extending the `VmWith` class. Integrations can provide any configuration you would otherwise pass when creating a VM, plus helper methods for interacting with the VM.
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
import { VmPython } from "@freestyle-sh/with-python";
const { vm } = await freestyle.vms.create({
with: {
js: new VmNodeJs(),
python: new VmPython(),
},
});
// the properties defined under `with` are now available on the vm class.
await vm.js.run(`console.log("Hello World!")`);
```
Importantly, these classes encapsulate their own caching logic via templates.
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
// first time is slow because this configuration has never been created and cached before
const { vm } = await freestyle.vms.create({
with: {
js: new VmNodeJs({ nodeVersion: "20"}),
},
});
// second time is fast because the configuration has been cached
const { vm: vm2 } = await freestyle.vms.create({
with: {
js: new VmNodeJs({ nodeVersion: "20" }),
},
});
```
## How it works
VmWith uses a builder pattern to compose VM configurations like middleware. Each
`VmWith` class has two main responsibilities:
### 1. Configuration (`configure` method)
The `configure` method transforms VM configuration by merging your component's
settings with existing configuration. It receives the current `CreateVmOptions`
and returns a modified version with your changes applied.
A common pattern is to put most of your configuration inside `VmTemplate` so
that it will be cached next time the same configuration is used. For example
with NodeJs, we can add run an installation script via systemd inside the
template. For more details on caching, see [Templates and
Snapshots](/v2/vms/templates-snapshots).
```ts
class VmNodeJs extends VmWith {
configure(existingConfig: CreateVmOptions): CreateVmOptions {
const nodeJsConfig: CreateVmOptions = {
template: new VmTemplate({
additionalFiles: {
"/opt/install-nodejs.sh": { content: installScript },
"/etc/profile.d/nvm.sh": { content: nvmInit },
},
systemd: {
services: [
{
name: "install-nodejs",
mode: "oneshot",
exec: ["bash /opt/install-nodejs.sh"],
},
],
},
}),
};
// Merge with existing config using compose helper
return this.compose(existingConfig, nodeJsConfig);
}
}
```
The `compose` method semantically merges configurations:
- **Simple fields** (like `workdir`, `idleTimeoutSeconds`) - last value wins
- **Arrays** (like `ports`, `users`, `groups`) - concatenated and deduplicated by key
- **Objects** (like `additionalFiles`) - merged with last value winning per key
- **Nested arrays** (like `systemd.services`) - merged by name with last value winning
### 2. Runtime Instance
The `createInstance` method returns an instance that will be attached to the VM,
providing convenient interaction methods. This instance has access to the VM via
the `vm` property.
```ts
class NodeJsRuntimeInstance extends VmWithInstance {
async exec(script: string) {
return await this.vm.exec({
command: `npm run '${script}'`,
});
}
}
class VmNodeJs extends VmWith {
createInstance(): NodeJsRuntimeInstance {
return new NodeJsRuntimeInstance();
}
}
```
## Dependencies and Generics
If you're building a VmWith that depends on another VmWith, we recommend
requiring it in the constructor. For example, a VmDevServer depends on the
generic VmJavaScriptRuntime so that users choose between NodeJs, Deno, Bun, etc.
```ts
const js = new VmNodeJs();
const devServer = new VmDevServer({
placeholderRepo: "https://github.com/example/placeholder",
repo: "https://github.com/example/app",
runtime: js,
});
const { vm } = await freestyle.vms.create({
with: {
js,
devServer,
},
});
```
Internally, VmDevServer can then use the runtime's helper methods to configure it's own installation correctly.
```ts
export class VmDevServer extends VmWith {
constructor(private options: { runtime: VmJavaScriptRuntime }) { super() }
configure(existingConfig: CreateVmOptions): CreateVmOptions {
const devServerConfig: CreateVmOptions = {
template: new VmTemplate({
systemd: {
services: [
{
name: "dev-server-install-dependencies",
mode: "oneshot",
// use the install command provided by the runtime
exec: [this.options.runtime.installCommand()],
// ensure we don't run npm install before the runtime is installed
after: [ this.options.runtime.installServiceName(),],
// ...
},
// ...
],
},
}),
};
return this.compose(existingConfig, devServerConfig);
}
// ...
}
```
If you need to access a dependencies' instance, you can do so using the
`.instance` property. This will only be available after the VM has been created,
so you must read it inside a VmWithInstance.
```ts
class VmDevServer extends VmWith {
// ...
createInstance(): VmDevServerInstance {
return new VmDevServerInstance(
this.options.runtime
);
}
}
class VmDevServerInstance extends VmWithInstance {
constructor(public runtime: VmJavaScriptRuntime) {
super();
}
helloWorld() {
return this.runtime.instance.runCode("console.log('Hello World!')");
}
}
```
}
#
URL: https://docs.freestyle.sh/v2/vms.md
Content:
---
title: Getting Started with VMs
---
This document provides a technical introduction to using Freestyle's VM service.
For a high-level overview of the service, its features and why to use it see [About Freestyle VMs](/v2/vms/about).
../../../content-snippets/setup-env.mdx
## Creating a VM
Calling `create` in the vms namespace will create and start a vm. This VM will
be created from the default snapshot configured for your account. This should
usually take less than 2 seconds, but your first VM may take longer while we
provision dedicated cache for your account.
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm } = await freestyle.vms.create();
// exec a shell script
await vm.exec("echo 'hello world'");
await vm.fs.writeTextFile("/tmp/hello-world.txt", "Hello World!");
```
## Code Execution
VMs can execute code with built-in language support for [Node.js](/v2/vms/integrations/node), [Python](/v2/vms/integrations/python), [Bun](/v2/vms/integrations/bun), [Ruby](/v2/vms/integrations/ruby), [Java](/v2/vms/integrations/java), and more. See [Integrations](/v2/vms/integrations) for the full list.
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
import { VmPython } from "@freestyle-sh/with-python";
const { vm } = await freestyle.vms.create({
with: {
js: new VmNodeJs(),
python: new VmPython(),
},
});
await vm.js.runCode(`console.log("Hello World!")`).then(console.log);
await vm.python.runCode(`print("Hello World!")`).then(console.log);
```
The first time you create a VM with a unique configuration, it may take longer while we build and cache the environment. Subsequent VMs with the same configuration start instantly.
For languages without a prebuilt integration, you can [build your own](/v2/vms/custom-integrations). If you exclusively need to execute JavaScript or TypeScript without a full VM, consider using [Serverless Runs](/v2/serverless/runs) for lower latency.
## More complex VM creation
We allow you to configure files, repos, workdir, and much more in the same
request you create the VM so you can minimize chatter and maximize reliability.
In addition to the VM class, we also return data specific to the create api call
which can be accessed in the returned object. See [VM Configuration](/v2/vms/configuration) for all available options, or [Templates and Snapshots](/v2/vms/templates-snapshots) if you plan to make many VMs with similar configuration.
```ts
import { freestyle } from "freestyle-sandboxes";
const { vmId, vm, domains, consoleUrl } = await freestyle.vms.create({
additionalFiles: {
"/repo/README.md": {
content: "# Hello World",
},
},
gitRepos: [
{
repo: "https://github.com/freestyle-sh/freestyle-base-nextjs-shadcn",
path: "/repo",
},
],
workdir: "/repo",
});
```
}
#
URL: https://docs.freestyle.sh/v2/vms/ssh-access.md
Content:
---
title: SSH Access
description: SSH into your Freestyle VMs.
---
## SSH Format
```
ssh vm-id@vm-ssh.freestyle.sh
ssh vm-id:access-token@vm-ssh.freestyle.sh
ssh vm-id+user:access-token@vm-ssh.freestyle.sh
```
## SSH as Root
To SSH into a VM as root, create an identity with permission to access the VM, then create an access token for that identity:
```ts
import { freestyle } from "freestyle-sandboxes";
const { vmId } = await freestyle.vms.create();
const { identity } = await freestyle.identities.create();
await identity.permissions.vms.create({
vmId: vmId,
});
const { token } = await identity.createToken();
console.log(`ssh ${vmId}:${token}@vm-ssh.freestyle.sh`);
```
If you omit the token from the URL, you will be prompted for it as a password:
```bash
ssh {vm-id}@vm-ssh.freestyle.sh
# You'll be prompted: Enter your access token as the password
```
## SSH as a Specific User
Create an identity with permission to access the VM as a specific Linux user:
```ts
const { vmId } = await freestyle.vms.create({
users: [
{ name: "developer" },
{ name: "admin" },
],
});
const { identity } = await freestyle.identities.create();
await identity.permissions.vms.create({
vmId: vmId,
allowedUsers: ["developer"],
});
const { token } = await identity.createToken();
console.log(`ssh ${vmId}+developer:${token}@vm-ssh.freestyle.sh`);
```
## Multiple Developers
Create separate identities for each developer with their own permissions:
```ts
const { vmId } = await freestyle.vms.create({
users: [
{
name: "alice",
gecos: "Alice Smith",
groups: ["sudo", "docker"],
},
{
name: "bob",
gecos: "Bob Jones",
groups: ["sudo", "docker"],
},
],
});
// Create separate identities for each developer
const { identity: aliceIdentity } = await freestyle.identities.create();
await aliceIdentity.permissions.vms.create({
vmId: vmId,
allowedUsers: ["alice"],
});
const { identity: bobIdentity } = await freestyle.identities.create();
await bobIdentity.permissions.vms.create({
vmId: vmId,
allowedUsers: ["bob"],
});
const { token: aliceToken } = await aliceIdentity.createToken();
const { token: bobToken } = await bobIdentity.createToken();
console.log(`Alice: ssh ${vmId}+alice:${aliceToken}@vm-ssh.freestyle.sh`);
console.log(`Bob: ssh ${vmId}+bob:${bobToken}@vm-ssh.freestyle.sh`);
```
## Related
- [Users and Groups](/v2/vms/configuration/users-groups) - Create Linux users and groups in your VMs
}
#
URL: https://docs.freestyle.sh/v2/vms/templates-snapshots.md
Content:
---
title: Templates and Snapshots
description: Use Templates and snapshots to create similar VMs quickly.
---
## What is a VmTemplate?
A `VmTemplate` is a declarative configuration that defines how to set up a VM. Templates can include:
- **Files** - Add files to the filesystem
- **Git repositories** - Clone repos into the VM
- **Systemd services** - Define background services and startup tasks
- **Users and groups** - Create system users with specific permissions
- **Environment variables** - Set environment configuration
Templates are automatically cached as snapshots, making subsequent VM creation nearly instant.
## Quick Start
```ts
import { freestyle, VmTemplate } from "freestyle-sandboxes";
const template = new VmTemplate({
// Add files to the VM
additionalFiles: {
"/tmp/hello-world.txt": {
content: "Hello World!"
}
},
// Clone a git repository
gitRepos: [{
repo: "https://github.com/owner/repo",
path: "/app"
}],
// Define systemd services
systemd: {
services: [{
name: "my-app",
mode: "service",
exec: ["npm start"],
workdir: "/app"
}]
}
});
// First call creates and caches the template (~10s)
const { vm } = await freestyle.vms.create({ template });
// Subsequent calls are instant (~100ms)
const { vm: vm2 } = await freestyle.vms.create({ template });
```
## What is a snapshot?
A snapshot is an immutable saved state of a VM, including its memory, disk, and CPU registers. Once created, snapshots are replicated across our network for fast VM creation anywhere. You can create snapshots in two ways:
## 1. Snapshot an existing VM
Call `snapshot()` on any VM (running, suspended, or stopped). Running/suspended VMs create "live" snapshots that resume instantly.
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm } = await freestyle.vms.create();
// Set up the VM
await vm.exec("apt-get update && apt-get install -y nginx");
await vm.fs.writeTextFile("/etc/nginx/sites-available/default", nginxConfig);
// Create a reusable snapshot
const { snapshotId } = await vm.snapshot();
```
> **Tip:** If you just need copies of the current VM for parallel work, use `vm.fork()` instead. It's faster for one-time duplication but doesn't create a reusable snapshot.
## 2. Create a snapshot declaratively with VmTemplate
Use `VmTemplate` to define your VM configuration. Freestyle builds the VM, snapshots it, and caches the result. Calling with the same template returns the cached snapshot instantly.
```ts
const template = new VmTemplate({
gitRepos: [{
repo: "owner/my-app",
path: "/app"
}],
additionalFiles: {
"/app/.env": {
content: "NODE_ENV=production"
}
},
systemd: {
services: [{
name: "install-deps",
mode: "oneshot",
exec: ["npm install"],
workdir: "/app",
wantedBy: ["multi-user.target"]
}]
}
});
// First call builds and caches
const { snapshotId } = await freestyle.vms.snapshots.ensure({ template });
// Second call returns cached snapshot immediately
const { snapshotId: cachedId } = await freestyle.vms.snapshots.ensure({ template });
// snapshotId === cachedId
```
## Using snapshots and templates
### From a snapshot ID
```ts
const { vm } = await freestyle.vms.create({
snapshotId,
});
```
### Directly from a template (recommended)
Pass the template directly to `freestyle.vms.create()`. Freestyle automatically handles snapshotting and caching.
```ts
const { vm } = await freestyle.vms.create({
template,
});
```
## Layering additional configuration
Apply extra configuration on top of a snapshot or template without rebuilding. The VM won't reboot—changes are applied instantly.
```ts
const { vm } = await freestyle.vms.create({
template, // or snapshotId
additionalFiles: {
"/tmp/goodbye-world.txt": {
content: "Goodbye World!"
}
},
env: {
API_KEY: "secret-key"
}
});
```
## Building layered templates
Create reusable base templates and extend them for specific use cases.
### Base template with a snapshot
```ts
const baseTemplate = new VmTemplate({
snapshotId: "snap-ubuntu-with-docker",
systemd: {
services: [{
name: "docker-compose-up",
mode: "service",
exec: ["docker-compose up"],
workdir: "/app"
}]
}
});
```
### Composing templates
```ts
// Base: Node.js runtime
const nodeTemplate = new VmTemplate({
additionalFiles: {
"/opt/install-node.sh": {
content: installScript
}
},
systemd: {
services: [{
name: "install-nodejs",
mode: "oneshot",
exec: ["bash /opt/install-node.sh"],
timeoutSec: 300
}]
}
});
// Extended: Node.js + your application
const appTemplate = new VmTemplate({
template: nodeTemplate,
gitRepos: [{
repo: "owner/my-app",
path: "/app"
}],
systemd: {
services: [{
name: "app-server",
mode: "service",
exec: ["npm start"],
workdir: "/app",
after: ["install-nodejs.service"]
}]
}
});
```
This creates two cache layers: one for Node.js (reusable across apps) and one for your specific application.
}
#
URL: https://docs.freestyle.sh/v2/git/advanced/contents-api.md
Content:
---
title: Git Contents API
description: Access and explore Git repository data.
---
import { SiTypescript, SiGnubash } from '@icons-pack/react-simple-icons';
# Accessing Repository Contents
## Get File or Directory Contents
Retrieve the contents of a file or directory at a specific ref (branch, commit, tag):
```typescript tab=" contents.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get contents of a specific file at a ref
const file = await repo.contents.get({
path: "src/index.ts",
rev: "main"
});
// Decode base64 content
const content = atob(file.content);
console.log(content);
```
```bash tab=" contents.bash"
curl https://api.freestyle.sh/git/v1/repo/${REPO_ID}/contents/${FILE_PATH}?ref=${REF} \
-H "Authorization: Bearer ${FREESTYLE_API_KEY}"
```
The response is a discriminated union based on the `type` field:
```typescript
// When path points to a file
interface FileContents {
type: "file";
name: string;
path: string;
sha: string; // Git object hash
size: number; // File size in bytes
content: string; // Base64-encoded content
}
// When path points to a directory
interface DirContents {
type: "dir";
name: string;
path: string;
sha: string; // Git tree hash
entries: DirEntry[];
}
// Directory entries (recursive)
type DirEntry =
| { type: "file"; name: string; path: string; sha: string; size: number }
| { type: "dir"; name: string; path: string; sha: string; entries: DirEntry[] };
```
## Branches
Get branch references:
```typescript tab=" branches.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get a specific branch
const branch = await repo.branches.get({ branchName: "main" });
console.log(branch.sha); // Commit hash the branch points to
// List all branches
const branches = await repo.branches.list();
for (const b of branches) {
console.log(b.name);
}
```
```bash tab=" branches.bash"
# Get a specific branch
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/refs/heads/${branchName} \
-H "Authorization: Bearer ${apiKey}"
# List all branches
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/refs/heads/ \
-H "Authorization: Bearer ${apiKey}"
```
### Default Branch
Get or set the repository's default branch:
```typescript tab=" default-branch.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get the current default branch
const { defaultBranch } = await repo.branches.getDefaultBranch();
console.log(defaultBranch); // "main"
// Set a new default branch
await repo.branches.setDefaultBranch({ defaultBranch: "develop" });
```
```bash tab=" default-branch.bash"
# Get default branch
curl https://api.freestyle.sh/git/v1/repo/${repoId}/default-branch \
-H "Authorization: Bearer ${apiKey}"
# Set default branch
curl -X PUT https://api.freestyle.sh/git/v1/repo/${repoId}/default-branch \
-H "Authorization: Bearer ${apiKey}" \
-H "Content-Type: application/json" \
-d '{"defaultBranch": "main"}'
```
## Tags
Get tags:
```typescript tab=" tags.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get a specific tag by name
const tag = await repo.tags.get({ tagName: "v1.0.0" });
console.log(tag.sha); // Commit hash the tag points to
// List all tags in the repository
const tags = await repo.tags.list();
for (const t of tags) {
console.log(t.name);
}
```
```bash tab=" tags.bash"
# Get a specific tag
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/refs/tags/${tagName} \
-H "Authorization: Bearer ${apiKey}"
# List all tags
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/refs/tags/ \
-H "Authorization: Bearer ${apiKey}"
```
## Download Archives
Download repository contents as compressed archives:
```typescript tab=" archives.ts"
import { freestyle } from "freestyle-sandboxes";
import { writeFile } from "fs/promises";
const repo = freestyle.git.repos.ref({ repoId: "your-repo-id" });
// Download repository as a tarball
const tarball = await repo.contents.downloadTarball({ rev: "main" });
await writeFile("repo.tar", Buffer.from(tarball));
// Download repository as a zip file
const zip = await repo.contents.downloadZip({ rev: "main" });
await writeFile("repo.zip", Buffer.from(zip));
```
```bash tab=" archives.bash"
# Download as tar
curl -H "Authorization: Bearer ${apiKey}" \
"https://api.freestyle.sh/git/v1/repo/${repoId}/tarball?ref=${ref}" \
-o repo.tar
# Download as zip
curl -H "Authorization: Bearer ${apiKey}" \
"https://api.freestyle.sh/git/v1/repo/${repoId}/zip?ref=${ref}" \
-o repo.zip
```
## Comparing Commits
Compare two revisions to see the differences between them:
```typescript tab=" compare.ts"
import { freestyle } from "freestyle-sandboxes";
const repo = freestyle.git.repos.ref({ repoId: "your-repo-id" });
const diff = await repo.compare({
base: "main",
head: "feature-branch",
});
console.log(`Status: ${diff.status}`);
console.log(`Ahead by: ${diff.ahead_by}, Behind by: ${diff.behind_by}`);
for (const file of diff.files) {
console.log(`${file.status}: ${file.filename} (+${file.additions} -${file.deletions})`);
}
```
```bash tab=" compare.bash"
curl "https://api.freestyle.sh/git/v1/repo/${repoId}/compare?base=main&head=feature-branch" \
-H "Authorization: Bearer ${apiKey}"
```
Response structure:
```typescript
interface CommitComparison {
// Relationship between head and base
status: "identical" | "ahead" | "behind" | "diverged";
// Number of commits head is ahead of base
ahead_by: number;
// Number of commits head is behind base
behind_by: number;
// Total commits in the comparison
total_commits: number;
// Files changed between base and head
files: DiffFile[];
}
interface DiffFile {
sha: string | null;
filename: string;
status: "added" | "removed" | "modified" | "renamed" | "copied" | "changed" | "unchanged";
additions: number;
deletions: number;
changes: number;
// Present for renamed/copied files
previous_filename?: string;
}
```
## Git Database API
For advanced use cases requiring direct access to Git objects (blobs, trees, commits, annotated tags), see the [Git Database API](/v2/git/advanced/database-api) guide.
}
#
URL: https://docs.freestyle.sh/v2/git/advanced/database-api.md
Content:
---
title: Git Database API
description: Low-level API for working with Git objects (blobs, commits, trees, tags) in Freestyle repositories.
---
import { SiTypescript, SiGnubash } from '@icons-pack/react-simple-icons';
The Git Database API provides low-level access to Git objects in your repositories. This API is useful for building tools that need to understand Git repository internals, inspect commit history, traverse directory trees, and read file contents at specific commits.
## Overview
Git stores all data as objects of four fundamental types:
1. **Blobs** - Raw file content
2. **Trees** - Directory listings mapping names to blobs or other trees
3. **Commits** - Snapshots of the repository at a specific point in time
4. **Tags** - References to specific commits with additional metadata
This API provides direct access to these Git objects through REST endpoints.
## Usage
### Blobs
Blobs represent the content of files in Git. When you retrieve a blob, you get the raw file content (always base64 encoded for binary safety).
#### Get a Blob
```typescript tab=" blobs.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get a blob by its SHA hash
const blob = await repo.blobs.get({
sha: "abc123..."
});
// Decode the base64 content
const decodedContent = atob(blob.content);
console.log(decodedContent);
```
```bash tab=" blobs.bash"
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/blobs/${blobHash} \
-H "Authorization: Bearer ${FREESTYLE_API_KEY}"
```
Response structure:
```typescript
interface BlobObject {
// The blob content (base64 encoded)
content: string;
// Always "base64"
encoding: "base64";
// The blob's hash
sha: string;
}
```
### Commits
Commits represent snapshots of your repository at specific points in time.
#### Get a Commit
```typescript tab=" commits.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get a specific commit by its SHA hash
const commit = await repo.commits.get({
sha: "a1b2c3d4e5f6..."
});
console.log(commit.message); // "feat: add new feature"
console.log(commit.author.name); // "John Doe"
console.log(commit.tree.sha); // Hash of the root tree
```
```bash tab=" commits.bash"
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits/${commitHash} \
-H "Authorization: Bearer ${FREESTYLE_API_KEY}"
```
Response structure:
```typescript
interface CommitObject {
// The commit author
author: {
date: string;
name: string;
email: string;
};
// The committer (may be different from author)
committer: {
date: string;
name: string;
email: string;
};
// The commit message
message: string;
// The tree this commit points to
tree: {
sha: string;
};
// Parent commits (usually one, multiple for merge commits)
parents: Array<{
sha: string;
}>;
// The commit's hash
sha: string;
}
```
#### List Commits
```typescript tab=" list-commits.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// List recent commits
const commits = await repo.commits.list();
for (const commit of commits) {
console.log(`${commit.sha.slice(0, 7)} - ${commit.message}`);
}
```
```bash tab=" list-commits.bash"
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits \
-H "Authorization: Bearer ${FREESTYLE_API_KEY}"
```
### Trees
Trees represent directories in Git. A tree object contains a list of entries, each with a name, type (blob or tree), and hash.
#### Get a Tree
```typescript tab=" trees.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get a commit to find its tree hash
const commit = await repo.commits.get({
sha: "a1b2c3d4e5f6..."
});
// Fetch the tree (directory listing) for that commit
const tree = await repo.trees.get({
sha: commit.tree.sha
});
// Inspect files and subdirectories
for (const entry of tree.tree) {
if (entry.type === "blob") {
console.log(`File: ${entry.path}`);
} else if (entry.type === "tree") {
console.log(`Directory: ${entry.path}`);
}
}
```
```bash tab=" trees.bash"
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${treeHash} \
-H "Authorization: Bearer ${FREESTYLE_API_KEY}"
```
Response structure:
```typescript
interface TreeObject {
// The tree's entries (files and subdirectories)
tree: Array<{
// The entry's type: "blob" (file) or "tree" (directory)
type: "blob" | "tree";
// The entry's path (filename or directory name)
path: string;
// The entry's hash
sha: string;
}>;
// The tree's hash
sha: string;
}
```
### Tags
Tags are references to specific objects (usually commits) with additional metadata like tagger information and a message.
#### Get a Tag
```typescript tab=" tags.ts"
import { freestyle } from "freestyle-sandboxes";
// Get reference to an existing repository
const repo = freestyle.git.repos.ref({
repoId: "your-repo-id"
});
// Get an annotated tag by its object hash
const tag = await repo.tags.getByHash({
sha: "f1e2d3c4b5a6..."
});
console.log(tag.name); // "v1.0.0"
console.log(tag.message); // "Release version 1.0.0"
console.log(tag.target.sha); // Hash of the tagged commit
```
```bash tab=" tags.bash"
curl https://api.freestyle.sh/git/v1/repo/${repoId}/git/tags/${tagHash} \
-H "Authorization: Bearer ${FREESTYLE_API_KEY}"
```
Response structure:
```typescript
interface TagObject {
// The tag name
name: string;
// The tagger (may be null for lightweight tags)
tagger?: {
date: string;
name: string;
email: string;
};
// The tag message (may be null for lightweight tags)
message?: string;
// The object this tag points to (usually a commit)
target: {
sha: string;
};
// The tag's hash
sha: string;
}
```
## Common Use Cases
### Processing Files from a Git Trigger
When a Git trigger is invoked by a push to your repository, Freestyle sends a payload containing information about the event, including the commit hash. You can use this to inspect files that were changed in the commit:
```typescript
import { freestyle } from "freestyle-sandboxes";
// This function would be called by your webhook handler
async function processGitTriggerWebhook(webhookPayload: { repoId: string; commit: string }) {
const repo = freestyle.git.repos.ref({ repoId: webhookPayload.repoId });
// Get the commit
const commit = await repo.commits.get({ sha: webhookPayload.commit });
console.log(`Processing commit: ${commit.message}`);
console.log(`Author: ${commit.author.name} <${commit.author.email}>`);
// Get the tree pointed to by the commit
const rootTree = await repo.trees.get({ sha: commit.tree.sha });
// Example: Find package.json in the repository
const packageJsonEntry = rootTree.tree.find(
(entry) => entry.type === "blob" && entry.path === "package.json"
);
if (packageJsonEntry) {
// Get the content of package.json
const blob = await repo.blobs.get({ sha: packageJsonEntry.sha });
// Parse the package.json content (base64 decode first)
const packageJson = JSON.parse(atob(blob.content));
console.log(`Project name: ${packageJson.name}`);
console.log(`Dependencies: ${Object.keys(packageJson.dependencies || {}).length}`);
}
}
```
### Building a File Browser
You can build a recursive file browser:
```typescript
import { freestyle, GitRepo } from "freestyle-sandboxes";
async function exploreDirectory(repo: GitRepo, treeSha: string, currentPath = "") {
const tree = await repo.trees.get({ sha: treeSha });
for (const entry of tree.tree) {
const entryPath = currentPath ? `${currentPath}/${entry.path}` : entry.path;
if (entry.type === "tree") {
// Recursively explore subdirectories
await exploreDirectory(repo, entry.sha, entryPath);
} else if (entry.type === "blob") {
// Process files
console.log(`File: ${entryPath}`);
// Optionally fetch the blob content
// const blob = await repo.blobs.get({ sha: entry.sha });
// const content = atob(blob.content);
}
}
}
// Usage
const repo = freestyle.git.repos.ref({ repoId: "your-repo-id" });
const mainBranch = await repo.branches.get({ branchName: "main" });
const commit = await repo.commits.get({ sha: mainBranch.sha });
await exploreDirectory(repo, commit.tree.sha);
```
### Viewing File Contents from a Specific Commit
The simplest way to get file contents at a specific commit is using `contents.get()`:
```typescript
import { freestyle } from "freestyle-sandboxes";
const repo = freestyle.git.repos.ref({ repoId: "your-repo-id" });
// Get file contents at a specific commit
const file = await repo.contents.get({
path: "src/index.ts",
rev: "a1b2c3d4e5f6..." // Can be a commit SHA, branch name, or tag
});
// Decode the base64 content
const fileContent = atob(file.content);
console.log(fileContent);
```
For more advanced use cases, you can manually traverse the Git object database:
```typescript
import { freestyle } from "freestyle-sandboxes";
async function viewFileAtCommit(repoId: string, commitHash: string, filePath: string) {
const repo = freestyle.git.repos.ref({ repoId });
// Get the commit
const commit = await repo.commits.get({ sha: commitHash });
// Get the root tree
let currentTree = await repo.trees.get({ sha: commit.tree.sha });
// Navigate the directory structure
const pathParts = filePath.split('/');
const fileName = pathParts.pop()!;
for (const directory of pathParts) {
const dirEntry = currentTree.tree.find(
(entry) => entry.type === "tree" && entry.path === directory
);
if (!dirEntry) {
throw new Error(`Directory not found: ${directory}`);
}
currentTree = await repo.trees.get({ sha: dirEntry.sha });
}
// Find the file in the current directory
const fileEntry = currentTree.tree.find(
(entry) => entry.type === "blob" && entry.path === fileName
);
if (!fileEntry) {
throw new Error(`File not found: ${fileName}`);
}
// Get the file content and decode
const blob = await repo.blobs.get({ sha: fileEntry.sha });
return atob(blob.content);
}
// Usage
const content = await viewFileAtCommit(
"your-repo-id",
"a1b2c3d4e5f6...",
"src/index.ts"
);
console.log(content);
```
## Best Practices
1. **Cache results when possible** - Git objects are immutable, so you can safely cache them
2. **Handle binary data correctly** - Remember that blob content is base64 encoded
## API Reference
For detailed API reference documentation for these endpoints, see:
- [Get Blob](/API-Reference/git/handle_get_blob)
- [Get Commit](/API-Reference/git/handle_get_commit)
- [Get Tree](/API-Reference/git/handle_get_tree)
- [Get Tag](/API-Reference/git/handle_get_tag)
}
#
URL: https://docs.freestyle.sh/v2/serverless/deployments/about.md
Content:
---
title: About Freestyle Serverless Deployments
description: An introduction to Freestyle's Serverless Deployments service and its capabilities.
---
This document provides a high-level overview of Freestyle's Serverless Deployments service.
To get started, check out the [Getting Started Guide](/v2/serverless/deployments).
## Overview
Serverless Deployments run Node.js web applications with automatic scaling, wildcard subdomains, and framework detection. Deploy from Git, code snippets, or tarballs via API. They are used to host Next.js websites, Hono servers, Expo servers and anything else TypeScript or JavaScript.
## Features
### API-First
No CLI or dashboard required. Create, update, and manage deployments entirely through the API. This makes Freestyle deployments easy to integrate into your own products—AI agents can deploy apps, platforms can offer hosting to their users.
### Sub-Second Deploys
For deployments without a build step, we aim to go from API call to live production URL in under one second. This works even with node modules—deploy an Express server with dependencies and it's running instantly. This is possible because deployments aren't container-based and because we [cache your dependencies](#cached-modules) across deploys.
### Multiple Source Types
Deploy from a Git repository (public or authenticated), a code snippet, or a tarball URL. Git is recommended for observability—you can trace deployments back to commits.
### Domain Management
Freestyle handles domains for you and your users. Point your customers' custom domains at Freestyle and we provision SSL automatically. Set up wildcard certificates for preview deploys so every branch gets its own subdomain without certificate delays. For testing, any `*.style.dev` subdomain is available for free.
### Websocket Support
Timeouts are based on the last TCP packet, not the last HTTP request. Websockets stay alive as long as you ping faster than the timeout—no workarounds needed.
### Cached Modules
Include your lockfile and Freestyle installs dependencies on our end. Your upload is just your source code—typically a few megabytes instead of hundreds. Smaller bundles mean faster uploads, faster propagation to our edge, and lower costs. When we scale your deployment up, we're not copying hundreds of megabytes of dependencies around. This keeps cold starts fast and your bill low.
### Framework Detection
Freestyle auto-detects and configures frameworks like Next.js, Vite, and Expo. No build configuration required for common setups. TypeScript works out of the box—no compile step needed.
## Beyond Vercel and Netlify
Vercel and Netlify are built for developers deploying their own projects. They assume a human is pushing code through a CLI or connecting a GitHub repo through a dashboard.
Freestyle is built for programmatic deployment for platforms. We make it easy to manage your customers domains and code through our API. We've completely decoupled domains from deployments making it easy for you to create custom flows for how you want to manage your users' apps. We've also built a deep integration with our [Multi-Tenant Git service](/v2/git) so you can manage their deployments alongside their code repositories.
This matters when you're building a product that deploys on behalf of users—an AI coding assistant that ships what it builds, a platform that gives each customer their own hosted app, or a tool that needs to spin up preview environments on demand. Traditional platforms rate-limit their APIs for occasional use. Freestyle expects you to deploy programmatically at scale.
## When Not to Use Deployments
Serverless Deployments are for web applications that serve HTTP traffic. If your use case doesn't fit that model, consider:
### One-Shot Code Execution
If you need to run code once and get a result back (not serve HTTP), use [Serverless Runs](/v2/serverless/runs). Runs are lighter weight and return results directly.
### Non-Node Workloads
Deployments only support Node.js. If you need Python, Ruby, Go, or other languages, use [VMs](/v2/vms). VMs are full Linux environments that run anything.
### Low-Level System Access
Deployments don't expose SSH, systemd, or filesystem persistence. If you need to install system packages, run background daemons, or persist files across restarts, use [VMs](/v2/vms).
### Browser Automation
If you need a browser for scraping or testing, use [VMs](/v2/vms) with a browser integration. Deployments don't include browsers.
}
#
URL: https://docs.freestyle.sh/v2/serverless/deployments/configuration.md
Content:
---
title: Configuration
description: All available options for serverless deployments.
---
When deploying to Freestyle, there are two distinct parameter groups:
1. [**Source**](#deployment-source): Where your code comes from—a Git repository, raw files, or a tarball link.
2. [**Options**](#deployment-options): Settings like domains, build configuration, entrypoint, timeout, and more.
## Deployment Source
There are 3 types of sources you can use to deploy:
### Git
A Git repository containing your code. This can be a public repository, a repository with basic authentication in the URL, or any repository in your account on [Freestyle Git](/v2/git). This is the most common and recommended approach because it provides observability and debuggability.
```ts
import { freestyle } from "freestyle-sandboxes";
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
branch: "main", // optional, defaults to default branch
rootPath: "./web", // optional, defaults to root
domains: ["app.style.dev"],
build: true,
});
```
### Code
A code snippet to deploy directly. Useful for simple deployments or rapid iteration.
```ts
import { freestyle } from "freestyle-sandboxes";
await freestyle.serverless.deployments.create({
code: `
import express from 'express';
const app = express();
app.get('/', (req, res) => res.send('Hello World'));
app.listen(3000);
`,
nodeModules: {
express: "^4.18.2",
},
domains: ["api.style.dev"],
});
```
### Tar Link
A link to a gzipped tarball containing your files. This is useful when your main app is hosted on a serverless platform with request size limits. Instead of uploading files directly, your users upload to a storage service (like S3) and provide a signed link.
```ts
import { freestyle } from "freestyle-sandboxes";
await freestyle.serverless.deployments.create({
tarUrl: "https://s3.example.com/signed-url/app.tar.gz",
domains: ["app.style.dev"],
build: true,
});
```
When deploying to Freestyle you **never upload node_modules**. Include your
`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lock` file in
your source. Freestyle automatically installs dependencies, keeping
deployments small and fast.
## Deployment Options
### Domains
Specify which domains to deploy to. You can use any `*.style.dev` subdomain that isn't taken, or any [verified custom domain](/v2/domains).
```ts
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
domains: ["app.style.dev", "app.yourdomain.com"],
});
```
### Entrypoint
The main file of your application. By default, Freestyle automatically detects the entrypoint for frameworks like Next.js, Vite, and Expo. For custom setups, specify it manually.
```ts
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
entrypoint: "server.js",
domains: ["app.style.dev"],
});
```
### Node Modules
By default, Freestyle installs dependencies from your lockfile. You can also specify additional packages directly.
```ts
await freestyle.serverless.deployments.create({
code: `import express from 'express'; /* ... */`,
nodeModules: {
express: "^4.18.2",
cors: "^2.8.5",
},
domains: ["api.style.dev"],
});
```
### Environment Variables
Environment variables available at runtime. These are **not available at build time**—use build options for that. Environment variables are tied to deployments; to change them, create a new deployment.
```ts
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
envVars: {
API_KEY: "your-api-key",
DATABASE_URL: "postgres://...",
},
domains: ["app.style.dev"],
});
```
### Timeout
The maximum idle time before your deployment is scaled down. Unlike most serverless platforms that timeout after the last request, Freestyle timeouts after the **last TCP packet**. This means websockets can stay alive indefinitely as long as you ping faster than the timeout.
```ts
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
timeout: 60, // seconds
domains: ["app.style.dev"],
});
```
### Build
By default, Freestyle deploys files as-is. Set `build: true` to have Freestyle build your code. It automatically detects frameworks like Next.js, Vite, and Expo.
```ts
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
build: true,
domains: ["app.style.dev"],
});
```
For more control, pass build options:
```ts
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
build: {
command: "npm run build", // custom build command
outDir: "dist", // output directory
envVars: { // build-time environment variables
NEXT_PUBLIC_API_URL: "https://api.example.com",
},
},
domains: ["app.style.dev"],
});
```
### Await
By default, the deployment request waits until the deployment is built and propagated before returning. Set `await: false` to return immediately with a deployment ID for polling.
```ts
const { deploymentId } = await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
await: false,
domains: ["app.style.dev"],
});
// Poll for status
const status = await freestyle.serverless.deployments.get({ deploymentId });
```
## Combined Example
All options can be combined:
```ts
import { freestyle } from "freestyle-sandboxes";
await freestyle.serverless.deployments.create({
repo: "https://github.com/your-org/your-repo",
branch: "production",
rootPath: "./apps/web",
domains: ["app.yourdomain.com", "app.style.dev"],
entrypoint: "server.js",
timeout: 120,
envVars: {
API_KEY: "runtime-secret",
},
build: {
command: "npm run build",
outDir: "dist",
envVars: {
NEXT_PUBLIC_API_URL: "https://api.example.com",
},
},
});
```
}
#
URL: https://docs.freestyle.sh/v2/serverless/deployments.md
Content:
---
title: Getting started with Serverless Deployments
description: TypeScript applications and web hosting.
---
This document provides a technical introduction to using Freestyle's Serverless Deployments service.
For a high-level overview of the service, its features and why to use it see [About Freestyle Serverless Deployments](/v2/serverless/deployments/about).
../../../../content-snippets/setup-env.mdx
## Deploy code
The most basic way to create a deployment is with a code snippet. Serverless
deployments are compatible with node and support node modules, so you can use
popular frameworks like Express. TypeScript is also supported out of the box
with no configuration. Freestyle provides free subdomains under `style.dev` for
testing. To configure custom domains, see [Domains](../domains).
```ts
import { freestyle } from "freestyle-sandboxes";
const { deployment, domains } = await freestyle.serverless.deployments.create({
code: `
import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello from Serverless Deployment!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
`,
nodeModules: {
express: "^4.18.2",
},
domains: [`${crypto.randomUUID()}.style.dev`],
});
console.log(domains);
```
## Deploy NextJS from a Git Repository
You can deploy directly from a public repository url or a freestyle repo id. See
[Git](../git) for more details on repositories. Freestyle has builtin support for
builds. You can optionally provide a branch and rootPath to target specific
directories in a monorepo. By default, the deployment will not be built, so make
sure to set `build: true` for frameworks like NextJS that require a build step.
```ts
import { freestyle } from "freestyle-sandboxes";
const { deployment, domains } = await freestyle.serverless.deployments.create({
repo: "https://github.com/freestyle-sh/freestyle-next.git", // or freestyle repo id
// branch: "main",
// rootPath: "./path/to/app",
domains: [`${crypto.randomUUID()}.style.dev`],
build: true,
});
```
}
#
URL: https://docs.freestyle.sh/v2/serverless/runs/about.md
Content:
---
title: About Freestyle Serverless Runs
description: An introduction to Freestyle's Serverless Runs service and its capabilities.
---
This document provides a high-level overview of Freestyle's Serverless Runs service.
To get started, check out the [Getting Started Guide](/v2/serverless/runs).
## Overview
Serverless Runs execute JavaScript or TypeScript code and return a result. No deployment, no HTTP server—just send code and get output back.
## Features
### API-First
Send code in an API call, get a result in the response. No build step, no deployment process, no infrastructure to manage. Runs are designed for programmatic use at scale.
### Fast Execution
Runs start in milliseconds. Module caching means subsequent runs with the same dependencies are even faster—we don't reinstall packages you've already used.
### Network Controls
By default, runs have full internet access. Restrict access with allow/deny rules on specific domains, or route all traffic through your own proxy. Block your users from calling APIs they shouldn't, or funnel requests through your infrastructure for logging and rate limiting.
### Cached Modules
Specify node modules and we install them once, then cache them for future runs. Your code stays small and execution stays fast.
### Environment Variables
Pass secrets and configuration at runtime. Environment variables are scoped to the run—nothing persists between executions.
### TypeScript
TypeScript works out of the box. No compile step, no tsconfig required.
## Beyond Lambda
AWS Lambda requires you to deploy a function before you can invoke it. You write code, zip it, upload it, configure triggers, then call it.
Serverless Runs skip all of that. Send code in the request body, get the result in the response. There's no function to manage, no deployment to track, no cold start penalty after periods of inactivity.
This is useful when code is dynamic—generated by an AI, provided by a user, or constructed at runtime. You don't want to deploy a new Lambda every time the code changes. You just want to run it.
## Beyond Sandboxes
Sandboxes like [Freestyle VMs](/v2/vms) are often used for AI single use code execution. While sandboxes work for this, they are expensive and comparatively slow. The fastest sandbox platforms will claim 90ms cold starts, Freestyle Serverless Runs have cold starts **under 10ms**. Serverless Runs are optimized extremely heavily for single use code execution, our average execution lasts less than \<150ms total — making Serverless Run response times equivalent to the time it takes sandbox platforms to start.
Further, when your code is done executing it shuts down immediately. You are only ever billed for the time and memory when your code is running. This makes it exponentially cheaper than sandbox alternatives.
Sandbox platforms generally have tight limits on parallel VMs. Freestyle Serverless Execution concurrency limits are between 100 and 1000 times higher depending on the platform. This makes it ideal for lots of isolated agents needing their own code isolation.
## When Not to Use Runs
Serverless Runs are for one-shot code execution. If your use case doesn't fit that model, consider:
### TypeScript HTTP Servers
If you need to serve HTTP traffic from any TypeScript server like Hono, NextJS, Express with a persistent URL, use [Serverless Deployments](/v2/serverless/deployments). Deployments give you domains, websockets, and long-running processes.
### Non-JavaScript Workloads
Serverless Runs only support JavaScript and TypeScript. If you need Python, Ruby, Go, or other languages, use [VMs](/v2/vms).
### Persistent State
Runs are stateless—nothing persists between executions. If you need to maintain state across invocations, use [VMs](/v2/vms) or a database.
### Long-Running Processes
Runs have a timeout. If you need processes that run for minutes or hours, use [VMs](/v2/vms).
### Binaries
Freestyle Serverless Runs do not support any binaries, if you need to run the binaries in dev servers, image processors like sharp, or other heavy workloads use [VMs](/v2/vms).
}
#
URL: https://docs.freestyle.sh/v2/serverless/runs/errors.md
Content:
---
title: Handling Errors
description: How to deal with errors in serverless runs
---
When you send a script to execute via `freestyle.serverless.runs.create()`, the script might contain invalid code that fails to parse, or it might encounter an error while running. The SDK throws typed errors for each case so you can handle them appropriately:
- **Syntax errors** occur when your script has invalid JavaScript or TypeScript that can't be parsed
- **Runtime errors** occur when your script executes but throws an uncaught exception
## Error Types
Import the `Errors` namespace from the SDK to access error classes:
```ts
import { freestyle, Errors } from "freestyle-sandboxes";
```
### Syntax Errors
A `SyntaxErrorError` is thrown when your script contains invalid JavaScript or TypeScript syntax. The error is detected before execution begins.
```ts
try {
await freestyle.serverless.runs.create({
code: `export default () z`, // Invalid syntax
});
} catch (error) {
if (error instanceof Errors.SyntaxErrorError) {
console.log("Syntax error:", error.message);
}
}
```
### Runtime Errors
A `RuntimeErrorError` is thrown when your script fails during execution. This includes uncaught exceptions, failed assertions, or any error thrown by your code.
```ts
try {
await freestyle.serverless.runs.create({
code: `
export default () => {
throw new Error("Something went wrong");
}
`,
});
} catch (error) {
if (error instanceof Errors.RuntimeErrorError) {
console.log("Runtime error:", error.message);
console.log("Stack trace:", error.body.stack);
}
}
```
Runtime errors include any logs captured before the error occurred:
```ts
try {
await freestyle.serverless.runs.create({
code: `
export default () => {
console.log("Starting...");
console.log("Processing data...");
throw new Error("Failed to process");
}
`,
});
} catch (error) {
if (error instanceof Errors.RuntimeErrorError) {
// Logs captured before the error
for (const log of error.body.logs) {
console.log(`[${log.type}] ${log.message}`);
}
}
}
```
## Error Properties
### SyntaxErrorError
| Property | Type | Description |
|----------|------|-------------|
| `message` | `string` | The full error message |
| `body.message` | `string` | The syntax error details |
### RuntimeErrorError
| Property | Type | Description |
|----------|------|-------------|
| `message` | `string` | The full error message |
| `body.message` | `string` | The runtime error details |
| `body.stack` | `string \| null` | Stack trace from the error |
| `body.logs` | `Array` | Logs captured before the error |
Each log entry in `body.logs` has:
- `message`: The log message
- `type`: Either `"log"` or `"error"`
- `callstack`: Optional callstack for the log
}
#
URL: https://docs.freestyle.sh/v2/serverless/runs.md
Content:
---
title: Getting started with Serverless Runs
description: Run code in our lightweight serverless environment.
---
../../../../content-snippets/setup-env.mdx
Serverless runs allow you to execute code in a lightweight javascript environment.
## Running Code
To create an Serverless Run, provide a script exporting the function you want to run.
You'll be returned the result of the function execution along with any logs
generated during the run. The result will be serialized using `JSON.stringify`,
so make sure your return value is serializable.
```ts
import { freestyle } from "freestyle-sandboxes";
const { result, logs } = await freestyle.serverless.runs.create({
code: `export default () => {
return "Hello from Serverless!";
}`,
config: {
timeout: 3000, // Optional timeout in milliseconds
}
});
console.log(result); // "Hello from Serverless!"
console.log(logs); // Logs generated during execution
```
## Node Modules
Serverless runs have first class support for node modules. You can specify the modules
you want to use in the `config.nodeModules` field. These modules will be cached
for future runs, making subsequent executions faster.
```ts
import { freestyle } from "freestyle-sandboxes";
const { result } = await freestyle.serverless.runs.create({
code: `
import { z } from 'zod';
export default () => {
const User = z.object({ name: z.string(), email: z.string().email() });
return User.parse({ name: "Alice", email: "alice@example.com" });
}
`,
config: {
nodeModules: {
zod: "3.22.4",
},
},
});
```
Peer dependencies are automatically resolved, but you can also specify how they
should be handled using the `peerDependencyResolution` field.
## Env Variables
You can also pass environment variables to your serverless runs using the `config.env` field.
```ts
import { freestyle } from "freestyle-sandboxes";
const { result } = await freestyle.serverless.runs.create({
code: `
export default () => {
return process.env.MY_SECRET;
}
`,
config: {
env: {
MY_SECRET: "supersecretvalue",
},
},
});
```
## Network Security and proxying
By default, serverless runs have complete access to the internet. However, you can
restrict access using the `networkPermissions` field in the config. Permissions
are matched against domains. If you specify allow rules, only those rules will
be allowed, and all other requests will be blocked. If a request is blocked, the
request will throw an error.
```ts
import { freestyle } from "freestyle-sandboxes";
freestyle.serverless.runs.create({
code: `
export default async () => {
await fetch("https://not-example.com");
return "Hello from Serverless!";
}
`,
config: {
networkPermissions: [
{
action: "allow",
domain: "example.com",
behavior: "exact",
},
]
}
});
```
If you want to deny specific domains while allowing all others, you can use the `deny` action.
```ts
import { freestyle } from "freestyle-sandboxes";
freestyle.serverless.runs.create({
code: `
export default async () => {
const err = await fetch("https://blocked-domain.com").catch(e => e.message);
return err;
}
`,
config: {
networkPermissions: [
{
action: "deny",
domain: "blocked-domain.com",
behavior: "exact",
},
]
}
});
```
You can also proxy network requests through your own proxy server using the
`proxy` field. It's sometimes useful to also include custom headers, which will
be applied to all requests made inside the serverless run.
```ts
import { freestyle } from "freestyle-sandboxes";
freestyle.serverless.runs.create({
code: ``,
config: {
proxy: "http://my-proxy-server.com",
},
headers: {
"Proxy-Authorization": "Basic base64encodedcredentials"
}
});
```
## Listing Serverless Runs
```ts
import { freestyle } from "freestyle-sandboxes";
const { runs } = await freestyle.serverless.runs.list({
limit: 10,
});
console.log(runs);
```
```json
[
{
runId: "6cfcae22-f9a0-46d5-89bf-2b55eb1e6c14",
createdAt: "2025-11-07T21:58:48.511978Z",
startedAt?: "2025-11-07T21:58:48.536538Z",
status: "complete"
}
]
```
You can paginate through serverless runs using the `cursor` field.
```ts
import { freestyle } from "freestyle-sandboxes";
let hasMore = true;
let nextCursor = undefined;
while (hasMore) {
const { runs, nextCursor: newCursor } = await freestyle.serverless.runs.list({
limit: 10,
cursor: nextCursor,
});
console.log(runs);
nextCursor = newCursor;
hasMore = !!nextCursor;
}
```
{/* You can organize and categorize your serverless runs using tags. Tags are simple
strings that you can attach to your serverless runs when creating them. This allows
you to easily filter and manage your serverless runs based on their tags.
```ts
import { freestyle } from "freestyle-sandboxes";
freestyle.serverless.runs.create({
code: ``,
config: {
tags: ["user-a"]
}
});
``` */}
}
#
URL: https://docs.freestyle.sh/v2/vms/integrations/bun.md
Content:
---
title: Bun
description: Add Bun runtime to your VMs.
---
The Bun integration installs [Bun](https://bun.sh), a fast JavaScript runtime and toolkit, and provides helper methods for running code and installing packages.
## Installation
```bash
npm install @freestyle-sh/with-bun freestyle-sandboxes
```
## Usage
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmBun } from "@freestyle-sh/with-bun";
const { vm } = await freestyle.vms.create({
with: {
js: new VmBun(),
},
});
const res = await vm.js.runCode({
code: "console.log(JSON.stringify({ hello: 'world' }));"
});
console.log(res);
// { result: { hello: 'world' }, stdout: '{"hello":"world"}\n', statusCode: 0 }
```
## Options
```ts
new VmBun({
version: "1.1.0", // Optional: specific Bun version (default: latest)
})
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `version` | `string` | `undefined` | Bun version to install. If not specified, installs the latest version. |
## API
### `vm.js.runCode({ code })`
Executes JavaScript/TypeScript code in the Bun runtime.
```ts
const res = await vm.js.runCode({
code: `
const data = { sum: 1 + 2, timestamp: Date.now() };
console.log(JSON.stringify(data));
`
});
console.log(res.result); // { sum: 3, timestamp: 1234567890 }
```
**Returns:** `Promise`
```ts
type RunCodeResponse = {
result: Result; // Parsed JSON from stdout (if valid JSON)
stdout?: string; // Raw stdout output
stderr?: string; // Raw stderr output
statusCode?: number; // Exit code
};
```
### `vm.js.install(options?)`
Installs packages using Bun.
```ts
// Install from package.json in current directory
await vm.js.install();
// Install from package.json in specific directory
await vm.js.install({ directory: "/app" });
// Install specific packages
await vm.js.install({ deps: ["lodash", "express"] });
// Install with specific versions
await vm.js.install({ deps: { "lodash": "^4.0.0", "express": "~5.0.0" } });
// Install as dev dependencies
await vm.js.install({ deps: ["typescript"], dev: true });
// Install globally
await vm.js.install({ global: true, deps: ["typescript"] });
```
**Returns:** `Promise`
```ts
type InstallResult = {
success: boolean;
stdout?: string;
stderr?: string;
};
```
## Why Bun?
Bun is significantly faster than Node.js for many operations:
- **Faster startup** - Bun starts up to 4x faster than Node.js
- **Faster installs** - `bun install` is much faster than `npm install`
- **Built-in bundler** - No need for webpack or esbuild
- **TypeScript support** - Run `.ts` files directly without compilation
## Example: Run Code and Get Result
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmBun } from "@freestyle-sh/with-bun";
const { vm } = await freestyle.vms.create({
with: {
js: new VmBun(),
},
});
// Run TypeScript code directly
const res = await vm.js.runCode({
code: `
interface User { name: string; age: number }
const user: User = { name: "Alice", age: 30 };
console.log(JSON.stringify(user));
`
});
console.log(res.result); // { name: "Alice", age: 30 }
```
}
#
URL: https://docs.freestyle.sh/v2/vms/integrations.md
Content:
---
title: Integrations
description: Pre-built packages that extend your VMs with languages, databases, browsers, and more.
---
Integrations are composable packages that extend your VMs with additional capabilities. They handle installation, configuration, caching, and expose convenient helper methods. Use them to add language runtimes, databases, headless browsers, third-party services, and more.
You can add as many integrations as you need to a single VM.
## Available Integrations
| Integration | Package | Description |
|-------------|---------|-------------|
| [Node.js](/v2/vms/integrations/node) | `@freestyle-sh/with-nodejs` | JavaScript/TypeScript runtime via NVM |
| [Python](/v2/vms/integrations/python) | `@freestyle-sh/with-python` | Python 3 runtime |
| [Bun](/v2/vms/integrations/bun) | `@freestyle-sh/with-bun` | Fast JavaScript runtime and toolkit |
| [Ruby](/v2/vms/integrations/ruby) | `@freestyle-sh/with-ruby` | Ruby runtime via RVM |
| [uv](/v2/vms/integrations/python/uv) | `@freestyle-sh/with-uv` | Fast Python package management and runtime |
| [Java](/v2/vms/integrations/java) | `@freestyle-sh/with-java` | Java runtime via Amazon Corretto |
## Usage
Add integrations using the `with` property when creating a VM:
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
const { vm } = await freestyle.vms.create({
with: {
js: new VmNodeJs(),
},
});
// Use the integration's helper methods
const res = await vm.js.runCode({
code: 'console.log(JSON.stringify({ hello: "world" }))'
});
console.log(res.result); // { hello: "world" }
```
## Caching
Integrations are automatically cached. The first VM with a specific integration configuration takes longer while the environment is built. Subsequent VMs with the same configuration start instantly.
## Multiple Integrations
You can combine as many integrations as you need in a single VM:
```ts
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
import { VmPython } from "@freestyle-sh/with-python";
import { VmBun } from "@freestyle-sh/with-bun";
const { vm } = await freestyle.vms.create({
with: {
node: new VmNodeJs(),
python: new VmPython(),
bun: new VmBun(),
},
});
// Each integration is available on the vm object
await vm.node.runCode({ code: 'console.log("Node.js")' });
await vm.python.runCode({ code: 'print("Python")' });
await vm.bun.runCode({ code: 'console.log("Bun")' });
```
## Custom Integrations
You can create your own integrations by extending the `VmWith` class. See [Custom Integrations](/v2/vms/custom-integrations) for details.
}
#
URL: https://docs.freestyle.sh/v2/vms/integrations/java.md
Content:
---
title: Java
description: Add Java runtime to your VMs using Amazon Corretto JDK.
---
## Installation
```bash
npm install @freestyle-sh/with-java
```
## Usage
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmJava } from "@freestyle-sh/with-java";
const { vm } = await freestyle.vms.create({
with: {
java: new VmJava(),
},
});
const result = await vm.java.runCode({
code: 'System.out.println("Hello World!");',
});
console.log(result.stdout); // Hello World!
```
## Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `version` | `string` | `"21"` | Java version to install |
### Specifying a Version
```ts
const { vm } = await freestyle.vms.create({
with: {
java: new VmJava({ version: "17" }),
},
});
```
## Methods
### `runCode`
Execute Java code and capture the output.
```ts
const result = await vm.java.runCode({
code: `
import java.util.*;
public class Main {
public static void main(String[] args) {
System.out.println("Hello from Java!");
}
}
`,
});
console.log(result.stdout);
console.log(result.stderr);
console.log(result.statusCode);
```
## Runtime
This integration installs [Amazon Corretto](https://aws.amazon.com/corretto/), a no-cost, production-ready distribution of OpenJDK.
}
#
URL: https://docs.freestyle.sh/v2/vms/integrations/node.md
Content:
---
title: Node.js
description: Add Node.js runtime to your VMs.
---
The Node.js integration installs Node.js via [NVM](https://github.com/nvm-sh/nvm) and provides helper methods for running JavaScript code and installing packages.
## Installation
```bash
npm install @freestyle-sh/with-nodejs freestyle-sandboxes
```
## Usage
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
const { vm } = await freestyle.vms.create({
with: {
node: new VmNodeJs(),
},
});
const res = await vm.node.runCode({
code: "console.log(JSON.stringify({ hello: 'world' }));"
});
console.log(res);
// { result: { hello: 'world' }, stdout: '{"hello":"world"}\n', statusCode: 0 }
```
## Options
```ts
new VmNodeJs({
version: "22", // Optional: Node.js version (default: "24")
})
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `version` | `string` | `"24"` | Node.js version to install via NVM |
## API
### `vm.node.runCode({ code })`
Executes JavaScript code in the Node.js runtime.
```ts
const res = await vm.node.runCode({
code: `
const data = { sum: 1 + 2, timestamp: Date.now() };
console.log(JSON.stringify(data));
`
});
console.log(res.result); // { sum: 3, timestamp: 1234567890 }
```
**Returns:** `Promise`
```ts
type RunCodeResponse = {
result: Result; // Parsed JSON from stdout (if valid JSON)
stdout?: string; // Raw stdout output
stderr?: string; // Raw stderr output
statusCode?: number; // Exit code
};
```
### `vm.node.install(options?)`
Installs npm packages.
```ts
// Install from package.json in current directory
await vm.node.install();
// Install from package.json in specific directory
await vm.node.install({ directory: "/app" });
// Install specific packages
await vm.node.install({ deps: ["lodash", "express"] });
// Install with specific versions
await vm.node.install({ deps: { "lodash": "^4.0.0", "express": "~5.0.0" } });
// Install as dev dependencies
await vm.node.install({ deps: ["typescript"], dev: true });
// Install globally
await vm.node.install({ global: true, deps: ["typescript"] });
```
**Returns:** `Promise`
```ts
type InstallResult = {
success: boolean;
stdout?: string;
stderr?: string;
};
```
## Example: Run Code and Get Result
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmNodeJs } from "@freestyle-sh/with-nodejs";
const { vm } = await freestyle.vms.create({
with: {
node: new VmNodeJs({ version: "22" }),
},
});
// Run code that outputs JSON
const res = await vm.node.runCode({
code: `
const result = Array.from({ length: 5 }, (_, i) => i * 2);
console.log(JSON.stringify(result));
`
});
console.log(res.result); // [0, 2, 4, 6, 8]
```
}
#
URL: https://docs.freestyle.sh/v2/vms/integrations/ruby.md
Content:
---
title: Ruby
description: Add Ruby runtime to your VMs.
---
The Ruby integration installs Ruby via [RVM](https://rvm.io) and provides helper methods for running Ruby code and installing gems.
## Installation
```bash
npm install @freestyle-sh/with-ruby freestyle-sandboxes
```
## Usage
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmRuby } from "@freestyle-sh/with-ruby";
const { vm } = await freestyle.vms.create({
with: {
ruby: new VmRuby(),
},
});
const res = await vm.ruby.runCode({
code: "require 'json'; puts JSON.generate({ hello: 'world' })"
});
console.log(res);
// { result: { hello: 'world' }, stdout: '{"hello":"world"}\n', statusCode: 0 }
```
## Options
```ts
new VmRuby({
version: "3.3.6", // Optional: specific Ruby version (default: "3.4.8")
})
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `version` | `string` | `"3.4.8"` | Ruby version to install via RVM |
## API
### `vm.ruby.runCode({ code })`
Executes Ruby code in the RVM-managed Ruby runtime.
```ts
const res = await vm.ruby.runCode({
code: `
require 'json'
data = { sum: 1 + 2, list: [1, 2, 3] }
puts JSON.generate(data)
`
});
console.log(res.result); // { sum: 3, list: [1, 2, 3] }
```
**Returns:** `Promise`
```ts
type RunCodeResponse = {
result: Result; // Parsed JSON from stdout (if valid JSON)
stdout?: string; // Raw stdout output
stderr?: string; // Raw stderr output
statusCode?: number; // Exit code
};
```
### `vm.ruby.install(options?)`
Installs gems via `gem install` or `bundle install`.
```ts
// Install specific gems
await vm.ruby.install({ deps: ["nokogiri", "colorize"] });
// Install from Gemfile (bundle install)
await vm.ruby.install();
await vm.ruby.install({ directory: "/app" });
```
**Returns:** `Promise`
```ts
type InstallResult = {
success: boolean;
stdout?: string;
stderr?: string;
};
```
## Example: Run Code and Get Result
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmRuby } from "@freestyle-sh/with-ruby";
const { vm } = await freestyle.vms.create({
with: {
ruby: new VmRuby({ version: "3.3.6" }),
},
});
const res = await vm.ruby.runCode({
code: `
require 'json'
numbers = [1, 2, 3, 4, 5]
result = {
sum: numbers.sum,
average: numbers.sum.to_f / numbers.length,
squared: numbers.map { |n| n ** 2 }
}
puts JSON.generate(result)
`
});
console.log(res.result);
// { sum: 15, average: 3.0, squared: [1, 4, 9, 16, 25] }
```
}
#
URL: https://docs.freestyle.sh/v2/vms/configuration/files-and-repos.md
Content:
---
title: Files and Repositories
description: Add files and clone git repositories into your VMs.
---
## Additional Files
Add files to the VM filesystem during creation:
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm } = await freestyle.vms.create({
additionalFiles: {
"/app/config.json": {
content: JSON.stringify({ apiKey: "secret" }),
},
"/etc/app/settings.conf": {
content: "debug=true\nport=3000",
},
},
});
```
### Binary Files
Use base64 encoding for binary files:
```ts
const { vm } = await freestyle.vms.create({
additionalFiles: {
"/app/image.png": {
content: "iVBORw0KGgoAAAANSUhEUgA...", // Base64 encoded
encoding: "base64",
},
},
});
```
### Large Files
For large files, consider:
1. Using git repositories (see below)
2. Downloading during VM startup
3. Using external storage
```ts
const { vm } = await freestyle.vms.create({
systemd: {
services: [
{
name: "download-assets",
mode: "oneshot",
exec: ["curl -o /app/data.zip https://example.com/data.zip"],
wantedBy: ["multi-user.target"],
},
],
},
});
```
## Git Repositories
Clone git repositories into your VMs automatically during creation.
### Basic Usage
First, create a repo reference, then use it when creating VMs:
```ts
import { freestyle } from "freestyle-sandboxes";
const { repoId } = await freestyle.git.repos.create({
source: {
url: "https://github.com/owner/repo",
},
});
const { vm } = await freestyle.vms.create({
gitRepos: [
{ repo: repoId, path: "/app" },
],
});
```
### Multiple Repositories
Clone multiple repos into different paths:
```ts
const { vm } = await freestyle.vms.create({
gitRepos: [
{ repo: frontendRepoId, path: "/app/frontend" },
{ repo: backendRepoId, path: "/app/backend" },
{ repo: sharedRepoId, path: "/app/shared" },
],
workdir: "/app",
});
```
### Specifying a Branch or Commit
Clone a specific revision:
```ts
const { vm } = await freestyle.vms.create({
gitRepos: [
{
repo: repoId,
path: "/app",
rev: "main", // Branch, tag, or commit SHA
},
],
});
```
### With Systemd Services
A common pattern is to clone a repo and run services from it:
```ts
const { vm } = await freestyle.vms.create({
gitRepos: [
{ repo: repoId, path: "/app" },
],
workdir: "/app",
systemd: {
services: [
{
name: "install-deps",
mode: "oneshot",
exec: ["npm install"],
workdir: "/app",
after: ["freestyle-git-sync.service"], // Wait for repo to be cloned
wantedBy: ["multi-user.target"],
},
{
name: "web-server",
mode: "service",
exec: ["npm start"],
workdir: "/app",
after: ["install-deps.service"],
},
],
},
});
```
## Using Templates
Put file and repo configuration in templates for caching:
```ts
import { VmTemplate } from "freestyle-sandboxes";
const template = new VmTemplate({
gitRepos: [
{ repo: repoId, path: "/app" },
],
additionalFiles: {
"/app/.env.example": { content: "API_KEY=\nDATABASE_URL=" },
},
systemd: {
services: [
{
name: "install-deps",
mode: "oneshot",
exec: ["npm install"],
workdir: "/app",
after: ["freestyle-git-sync.service"],
wantedBy: ["multi-user.target"],
},
],
},
});
// First call builds and caches
const { vm } = await freestyle.vms.create({ template });
// Add environment-specific files on top
const { vm: prodVm } = await freestyle.vms.create({
template,
additionalFiles: {
"/app/.env": { content: "API_KEY=prod-key\nDATABASE_URL=prod-db" },
},
});
```
}
#
URL: https://docs.freestyle.sh/v2/vms/configuration.md
Content:
---
title: VM Configuration
description: Overview of all configuration options for Freestyle VMs.
---
When creating a VM, you can configure many aspects of its behavior and contents. This section covers all available configuration options.
## Quick Reference
| Option | Description | Page |
|--------|-------------|------|
| `additionalFiles` | Add files to the VM filesystem | [Files and Repos](/v2/vms/configuration/files-and-repos) |
| `gitRepos` | Clone git repositories | [Files and Repos](/v2/vms/configuration/files-and-repos) |
| `systemd` | Define background services and startup tasks | [Systemd Services](/v2/vms/configuration/systemd-services) |
| `ports` | Expose services to the internet | [Ports and Networking](/v2/vms/configuration/ports-networking) |
| `users` | Create Linux users | [Users and Groups](/v2/vms/configuration/users-groups) |
| `groups` | Create Linux groups | [Users and Groups](/v2/vms/configuration/users-groups) |
| `persistence` | Control VM storage strategy | [Persistence](/v2/vms/configuration/persistence) |
| `idleTimeoutSeconds` | Auto-suspend after inactivity | [Lifecycle](/v2/vms/configuration/lifecycle) |
| `rootfsSizeGb` | Root filesystem size | [Lifecycle](/v2/vms/configuration/lifecycle) |
| `workdir` | Default working directory | [Lifecycle](/v2/vms/configuration/lifecycle) |
| `waitForReadySignal` | Wait for VM to be fully ready | [Lifecycle](/v2/vms/configuration/lifecycle) |
| `recreate` | Allow automatic recreation | [Lifecycle](/v2/vms/configuration/lifecycle) |
## Configuration Methods
### Direct Configuration
Pass options directly to `freestyle.vms.create()`:
```ts
const { vm } = await freestyle.vms.create({
workdir: "/app",
idleTimeoutSeconds: 600,
additionalFiles: {
"/app/config.json": { content: "{}" },
},
});
```
### Using Templates
Put configuration in a `VmTemplate` for automatic caching:
```ts
import { VmTemplate } from "freestyle-sandboxes";
const template = new VmTemplate({
workdir: "/app",
gitRepos: [{ repo: "owner/app", path: "/app" }],
systemd: {
services: [
{ name: "web", mode: "service", exec: ["npm start"], workdir: "/app" },
],
},
});
// First call builds and caches the template
const { vm } = await freestyle.vms.create({ template });
// Subsequent calls are instant
const { vm: vm2 } = await freestyle.vms.create({ template });
```
See [Templates and Snapshots](/v2/vms/templates-snapshots) for more details on caching.
### Combining Both
Apply dynamic configuration on top of a template:
```ts
const { vm } = await freestyle.vms.create({
template, // Base configuration (cached)
// Dynamic config applied on top
additionalFiles: {
"/app/.env": { content: `API_KEY=${process.env.API_KEY}` },
},
idleTimeoutSeconds: 300,
});
```
}
#
URL: https://docs.freestyle.sh/v2/vms/configuration/lifecycle.md
Content:
---
title: Lifecycle
description: Configure VM timeouts, filesystem size, working directory, and recreation behavior.
---
## Working Directory
Set the default directory for commands and file operations:
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm } = await freestyle.vms.create({
workdir: "/app",
gitRepos: [
{ repo: repoId, path: "/app" },
],
});
// Commands run in /app by default
await vm.exec("npm install"); // Runs in /app
await vm.exec("ls"); // Lists /app contents
```
Without a working directory, commands run in `/root`:
```ts
const { vm } = await freestyle.vms.create();
await vm.exec("pwd"); // Output: /root
```
## Idle Timeout
Automatically suspend VMs after a period of network inactivity (default: 300 seconds):
```ts
const { vm } = await freestyle.vms.create({
idleTimeoutSeconds: 600, // 10 minutes
});
```
Set to `null` to disable idle timeout:
```ts
const { vm } = await freestyle.vms.create({
idleTimeoutSeconds: null, // Never auto-suspend
});
```
> **Cost consideration:** VMs with no idle timeout continue running (and billing) indefinitely. Use [persistence](/v2/vms/configuration/persistence) settings to control storage costs when suspended.
## Root Filesystem Size
Control the size of the VM's root filesystem (default: 16 GB):
```ts
import { VmTemplate } from "freestyle-sandboxes";
const template = new VmTemplate({
rootfsSizeGb: 32,
});
const { vm } = await freestyle.vms.create({ template });
```
### Resizing After Creation
The filesystem can be resized after creation:
```ts
await vm.resize({ rootfsSizeGb: 64 }); // Resize to 64 GB
```
> **Note:** You can only increase the size, not decrease it.
## Ready Signals
Control when the VM creation API call returns by waiting for the VM to be fully ready:
```ts
const { vm } = await freestyle.vms.create({
waitForReadySignal: true,
readySignalTimeoutSeconds: 120, // Default: 120 seconds
});
```
By default (`waitForReadySignal: false`), the API returns as soon as the serial console shows the login prompt. This typically takes 1-2 seconds.
When `waitForReadySignal: true`, the API waits for the VM to be fully booted and ready to accept commands before returning.
> **Note:** In the future, this will be configurable to wait for custom readiness conditions like systemd service health checks or application-specific signals.
## Recreate
Allow the VM to be recreated automatically if deleted:
```ts
const { vm, vmId } = await freestyle.vms.create({
recreate: true,
gitRepos: [
{ repo: repoId, path: "/app" },
],
});
// Delete the VM
await freestyle.vms.delete({ vmId });
// The VM can be recreated with the same ID and configuration
const recreated = await vm.start(); // Automatically recreates
```
This is useful for:
- Serverless-style workloads
- Cost optimization (delete when not in use)
- Automatic recovery from failures
## Common Patterns
### Development VM with long timeout
```ts
const { vm } = await freestyle.vms.create({
workdir: "/workspace",
idleTimeoutSeconds: 3600, // 1 hour
gitRepos: [
{ repo: repoId, path: "/workspace" },
],
});
```
### Production VM (never suspend)
```ts
const { vm } = await freestyle.vms.create({
idleTimeoutSeconds: null,
persistence: { type: "persistent" },
waitForReadySignal: true,
});
```
### Serverless-style VM
```ts
const { vm } = await freestyle.vms.create({
recreate: true,
idleTimeoutSeconds: 60, // Suspend quickly
persistence: { type: "ephemeral" },
});
```
### Large data processing VM
```ts
const template = new VmTemplate({
rootfsSizeGb: 100,
});
const { vm } = await freestyle.vms.create({
template,
idleTimeoutSeconds: null, // Don't suspend during processing
});
// Resize if needed later
await vm.resize({ rootfsSizeGb: 200 });
```
}
#
URL: https://docs.freestyle.sh/v2/vms/configuration/persistence.md
Content:
---
title: Persistence
---
Freestyle lets you control how long your VMs are stored after they're suspended.
You can choose between ephemeral, cache, and persistent storage strategies.
## Persistence Types
### Ephemeral
VMs are deleted after they're suspended or when they reach their idle timeout.
No storage costs after the VM is suspended.
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm } = await freestyle.vms.create({
persistence: {
type: "ephemeral",
},
});
```
You can also specify when the VM should be deleted with `deleteEvent`:
```ts
const { vm } = await freestyle.vms.create({
persistence: {
type: "ephemeral",
deleteEvent: "onSuspend", // or "onIdleTimeout"
},
});
```
### Cache (Sticky)
VMs are stored temporarily with a priority system. Lower priority VMs are
deleted first, followed by higher priority VMs with the oldest last access time.
Not guaranteed to persist indefinitely.
```ts
const { vm } = await freestyle.vms.create({
persistence: {
type: "sticky",
priority: 5, // 0-10, default is 5
},
});
```
Higher priority values mean the VM is less likely to be evicted:
```ts
// High priority - keep around longer
const { vm: highPriority } = await freestyle.vms.create({
persistence: {
type: "sticky",
priority: 10,
},
});
// Low priority - evict first
const { vm: lowPriority } = await freestyle.vms.create({
persistence: {
type: "sticky",
priority: 1,
},
});
```
### Persistent
VMs are stored indefinitely until you manually delete them. Use with caution as
you won't be able to create new VMs if your storage quota is reached.
```ts
const { vm } = await freestyle.vms.create({
persistence: {
type: "persistent",
},
});
```
## When to Use Each Type
**Ephemeral** is ideal for:
- Temporary workbenches that don't need to persist
- Long-running tasks where the result is stored elsewhere
- Testing and experimentation
- Cost optimization when storage isn't needed
**Cache (Sticky)** is ideal for:
- AI coding agents and dev environments
- Scenarios where the VM is not the source of truth
- Fast startup times with pre-configured environments
- Balancing performance and cost
**Persistent** is ideal for:
- VMs that are the source of truth for data
- Long-running applications requiring state across restarts
- Production workloads
- Critical development environments
## Default Behavior
If you don't specify persistence, VMs default to sticky with priority 5:
```ts
// These are equivalent
const { vm: vm1 } = await freestyle.vms.create();
const { vm: vm2 } = await freestyle.vms.create({
persistence: { type: "sticky", priority: 5 },
});
```
}
#
URL: https://docs.freestyle.sh/v2/vms/configuration/ports-networking.md
Content:
---
title: Ports and Networking
description: Expose services running in your VMs to the internet.
---
## Overview
Freestyle automatically provides HTTPS endpoints for services running in your VMs. By default, port 3000 in your VM is exposed on port 443 (HTTPS) with automatic SSL/TLS termination.
## Default Behavior
Without any configuration, your VM gets:
- Port 3000 (internal) → Port 443 (external, HTTPS)
- Automatic SSL/TLS certificate
- A unique domain like `vm-abc123.freestyle.run`
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm, domains } = await freestyle.vms.create();
// Start a web server on port 3000
await vm.exec("python3 -m http.server 3000");
// Access it at domains[0]
console.log(`Visit: https://${domains[0]}`);
```
## Custom Port Mapping
Map any internal port to external ports 443 or 8081:
```ts
const { vm, domains } = await freestyle.vms.create({
ports: [
{
port: 443, // External (HTTPS)
targetPort: 8080, // Internal
},
],
});
// Start server on port 8080
await vm.exec("node server.js"); // Listens on 8080
// Access at domains[0]:443 (or just domains[0])
console.log(`Visit: https://${domains[0]}`);
```
## Multiple Ports
Expose multiple services from the same VM:
```ts
const { vm, domains } = await freestyle.vms.create({
ports: [
{
port: 443, // Main app
targetPort: 3000,
},
{
port: 8081, // Admin panel
targetPort: 4000,
},
],
});
console.log(`App: https://${domains[0]}`);
console.log(`Admin: https://${domains[0]}:8081`);
```
## No External Ports
Disable external access by passing an empty array:
```ts
const { vm } = await freestyle.vms.create({
ports: [], // No external ports
});
// VM is only accessible via SDK/SSH
// Useful for background workers, data processing, etc.
```
## Available External Ports
Currently, you can expose services on:
- **Port 443** (HTTPS) - Standard web traffic
- **Port 8081** (HTTPS) - Additional service
Any internal port can be mapped to these external ports.
## Domain Access
Each VM gets one or more domains:
```ts
const { vm, domains } = await freestyle.vms.create({
ports: [
{ port: 443, targetPort: 3000 },
{ port: 8081, targetPort: 4000 },
],
});
// All domains point to the same VM
console.log(domains); // ['vm-abc123.freestyle.run']
// Access different services via ports
// https://vm-abc123.freestyle.run (port 443)
// https://vm-abc123.freestyle.run:8081 (port 8081)
```
## SSL/TLS Certificates
All external ports automatically get:
- Valid SSL/TLS certificates
- Automatic certificate renewal
- HTTPS enforcement
No configuration needed!
## Common Patterns
### Web application
```ts
const { vm, domains } = await freestyle.vms.create({
ports: [
{ port: 443, targetPort: 3000 },
],
gitRepos: [
{ repo: "https://github.com/owner/webapp", path: "/app" },
],
systemd: {
services: [
{
name: "web-server",
mode: "service",
exec: ["npm start"],
workdir: "/app",
env: {
PORT: "3000",
},
},
],
},
});
console.log(`App running at https://${domains[0]}`);
```
### API + Admin panel
```ts
const { vm, domains } = await freestyle.vms.create({
ports: [
{ port: 443, targetPort: 3000 }, // Public API
{ port: 8081, targetPort: 4000 }, // Admin
],
systemd: {
services: [
{
name: "api",
mode: "service",
exec: ["node api.js"],
env: { PORT: "3000" },
},
{
name: "admin",
mode: "service",
exec: ["node admin.js"],
env: { PORT: "4000" },
},
],
},
});
console.log(`API: https://${domains[0]}`);
console.log(`Admin: https://${domains[0]}:8081`);
```
### Background worker (no external access)
```ts
const { vm } = await freestyle.vms.create({
ports: [], // No external ports
systemd: {
services: [
{
name: "worker",
mode: "service",
exec: ["python worker.py"],
env: {
QUEUE_URL: "redis://...",
},
},
],
},
});
// Only accessible via vm.exec() and SSH
```
### Development server
```ts
const { vm, domains } = await freestyle.vms.create({
ports: [
{ port: 443, targetPort: 5173 }, // Vite default
],
gitRepos: [
{ repo: "owner/frontend", path: "/app" },
],
systemd: {
services: [
{
name: "dev-server",
mode: "service",
exec: ["npm run dev", "--", "--host", "0.0.0.0"],
workdir: "/app",
env: {
PORT: "5173",
},
},
],
},
});
console.log(`Dev server: https://${domains[0]}`);
```
## Internal Networking
Services within the VM can communicate freely on any port:
```ts
const { vm } = await freestyle.vms.create({
ports: [
{ port: 443, targetPort: 3000 }, // Only expose frontend
],
systemd: {
services: [
{
name: "database",
mode: "service",
exec: ["redis-server"],
// Listens on 6379 (not exposed externally)
},
{
name: "api",
mode: "service",
exec: ["node server.js"],
env: {
PORT: "3000",
REDIS_URL: "redis://localhost:6379", // Internal access
},
},
],
},
});
```
## Port Configuration in Templates
Include port configuration in templates for consistent caching:
```ts
import { freestyle, VmTemplate } from "freestyle-sandboxes";
const webTemplate = new VmTemplate({
ports: [
{ port: 443, targetPort: 3000 },
],
systemd: {
services: [
{
name: "web",
mode: "service",
exec: ["npm start"],
workdir: "/app",
},
],
},
});
const { vm, domains } = await freestyle.vms.create({
template: webTemplate,
gitRepos: [
{ repo: "owner/app", path: "/app" },
],
});
```
## Checking Open Ports
List what's listening in your VM:
```ts
// Check listening ports
await vm.exec("ss -tlnp");
// Check specific port
await vm.exec("lsof -i :3000");
// Test internal connectivity
await vm.exec("curl http://localhost:3000");
```
## Troubleshooting
### Service not accessible
1. **Check the service is running:**
```ts
await vm.exec("systemctl status my-service");
```
2. **Verify it's listening on the right port:**
```ts
await vm.exec("ss -tlnp | grep :3000");
```
3. **Test internal access:**
```ts
await vm.exec("curl http://localhost:3000");
```
4. **Check port mapping:**
```ts
const { vm, domains } = await freestyle.vms.create({
ports: [
{ port: 443, targetPort: 3000 }, // Make sure this matches
],
});
```
### Wrong port exposed
Make sure your application listens on `0.0.0.0`, not just `localhost`:
```ts
// ❌ Won't work
exec: ["node server.js"] // If it only binds to 127.0.0.1
// ✅ Works
exec: ["node server.js", "--host", "0.0.0.0"]
```
Or configure it via environment:
```ts
env: {
HOST: "0.0.0.0",
PORT: "3000",
}
```
}
#
URL: https://docs.freestyle.sh/v2/vms/configuration/systemd-services.md
Content:
---
title: Systemd Services
description: Configure background services and startup tasks in your VMs using systemd.
---
## What is systemd?
[Systemd](https://systemd.io/) is the standard init system and service manager for Linux. Freestyle VMs use systemd to manage services, startup tasks, and dependencies. You can define:
- **Background services** - Long-running processes like web servers
- **One-time setup tasks** - Installation scripts that run once
- **Service dependencies** - Control startup order and requirements
- **Automatic restarts** - Keep services running after failures
This documentation covers the systemd features most relevant to Freestyle VMs. For complete systemd documentation, see [systemd.io](https://systemd.io/) and [freedesktop.org systemd documentation](https://www.freedesktop.org/software/systemd/man/latest/).
## Quick Start
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm } = await freestyle.vms.create({
systemd: {
services: [
{
name: "my-web-server",
mode: "service",
exec: ["python3 -m http.server 8000"],
workdir: "/app",
},
],
},
});
```
## Service Modes
### `oneshot` - Run Once
Use for installation scripts, setup tasks, or one-time operations.
```ts
systemd: {
services: [
{
name: "install-dependencies",
mode: "oneshot",
exec: ["npm install"],
workdir: "/app",
wantedBy: ["multi-user.target"], // Start automatically on boot
timeoutSec: 300,
},
],
}
```
### `service` - Keep Running
Use for web servers, APIs, background workers, or any long-running process.
```ts
systemd: {
services: [
{
name: "api-server",
mode: "service",
exec: ["node server.js"],
workdir: "/app",
env: {
PORT: "3000",
NODE_ENV: "production",
},
},
],
}
```
## Service Dependencies
Control the order services start and their requirements.
```ts
systemd: {
services: [
{
name: "setup-database",
mode: "oneshot",
exec: ["./scripts/init-db.sh"],
wantedBy: ["multi-user.target"],
},
{
name: "web-app",
mode: "service",
exec: ["npm start"],
workdir: "/app",
// Wait for these to complete before starting
after: [
"setup-database.service",
"network-online.target",
],
// Require these services - fail if they fail
requires: ["setup-database.service"],
},
],
}
```
### Common systemd targets
- `multi-user.target` - Standard boot target for servers
- `network-online.target` - Network is fully configured
- `freestyle-git-sync.service` - Git repositories have been cloned
## Environment Variables
Set environment variables for your services:
```ts
systemd: {
services: [
{
name: "app",
mode: "service",
exec: ["python app.py"],
env: {
DATABASE_URL: "postgresql://localhost/mydb",
API_KEY: "secret-key",
LOG_LEVEL: "info",
},
},
],
}
```
## User and Permissions
Run services as specific users:
```ts
systemd: {
services: [
{
name: "web-server",
mode: "service",
exec: ["./server"],
user: "www-data",
group: "www-data",
workdir: "/var/www",
},
],
}
```
## Restart Policies
Configure automatic restart behavior for services:
```ts
systemd: {
services: [
{
name: "api-server",
mode: "service",
exec: ["node server.js"],
restartPolicy: {
policy: "on-failure", // "no" | "on-failure" | "always" | "on-abnormal"
restartSec: 5, // Wait 5 seconds before restarting
startLimitBurst: 3, // Max 3 restarts in the interval
startLimitIntervalSec: 60, // Within 60 seconds
},
},
],
}
```
### Restart policy options:
- `no` - Never restart
- `on-failure` - Restart on non-zero exit code
- `always` - Always restart (except clean shutdown)
- `on-abnormal` - Restart on crashes/signals
## Cleanup Options
### Delete after success
Useful for one-time setup that doesn't need to persist:
```ts
systemd: {
services: [
{
name: "install-nodejs",
mode: "oneshot",
exec: ["bash /opt/install-nodejs.sh"],
deleteAfterSuccess: true, // Remove after successful completion
timeoutSec: 300,
},
],
}
```
### Keep alive after exit
For oneshot services that should keep the system waiting:
```ts
systemd: {
services: [
{
name: "init-app",
mode: "oneshot",
exec: ["./initialize.sh"],
remainAfterExit: true, // Mark as active even after exiting
wantedBy: ["multi-user.target"],
},
],
}
```
## Watchdog and Health Checks
### Ready signals
Signal when your service is fully initialized:
```ts
systemd: {
services: [
{
name: "web-server",
mode: "service",
exec: ["python server.py"],
readySignal: true, // Service must call sd_notify("READY=1")
},
],
}
```
### Watchdog
Monitor service health and restart if unresponsive:
```ts
systemd: {
services: [
{
name: "background-worker",
mode: "service",
exec: ["./worker"],
watchdogSec: 30, // Expect heartbeat every 30 seconds
restartPolicy: {
policy: "on-failure",
},
},
],
}
```
## Triggering Other Services
Chain services on success or failure:
```ts
systemd: {
services: [
{
name: "deploy-app",
mode: "oneshot",
exec: ["./deploy.sh"],
onFailure: ["send-alert.service", "rollback.service"],
},
{
name: "send-alert",
mode: "oneshot",
exec: ["./notify-team.sh"],
},
{
name: "rollback",
mode: "oneshot",
exec: ["./rollback.sh"],
},
],
}
```
## Patching Existing Services
Modify services that are already defined in the base snapshot:
```ts
systemd: {
patchedServices: [
{
name: "existing-service",
env: {
NEW_VAR: "value", // Add environment variable
},
after: ["another-service.service"], // Add dependency
},
],
}
```
## Enable/Disable Services
Control whether services start automatically:
```ts
systemd: {
services: [
{
name: "optional-service",
mode: "service",
exec: ["./service"],
enable: false, // Don't start automatically
},
],
}
```
Start it manually later:
```ts
await vm.exec("systemctl start optional-service");
```
## Common Patterns
### Web server with installation
```ts
systemd: {
services: [
{
name: "install-deps",
mode: "oneshot",
exec: ["npm install"],
workdir: "/app",
wantedBy: ["multi-user.target"],
},
{
name: "web-server",
mode: "service",
exec: ["npm start"],
workdir: "/app",
after: ["install-deps.service"],
env: {
PORT: "3000",
NODE_ENV: "production",
},
restartPolicy: {
policy: "on-failure",
restartSec: 5,
},
},
],
}
```
### Database initialization
```ts
systemd: {
services: [
{
name: "init-db",
mode: "oneshot",
exec: ["./scripts/create-tables.sql"],
wantedBy: ["multi-user.target"],
remainAfterExit: true,
},
{
name: "run-migrations",
mode: "oneshot",
exec: ["./scripts/migrate.sh"],
after: ["init-db.service"],
requires: ["init-db.service"],
wantedBy: ["multi-user.target"],
},
],
}
```
### Background worker with health check
```ts
systemd: {
services: [
{
name: "queue-worker",
mode: "service",
exec: ["python worker.py"],
workdir: "/app",
env: {
QUEUE_URL: "redis://localhost:6379",
},
watchdogSec: 60,
restartPolicy: {
policy: "on-failure",
restartSec: 10,
startLimitBurst: 5,
startLimitIntervalSec: 300,
},
},
],
}
```
## Debugging Services
Check service status:
```ts
await vm.exec("systemctl status my-service");
```
View service logs:
```ts
await vm.exec("journalctl -u my-service -n 50");
```
Follow logs in real-time:
```ts
await vm.exec("journalctl -u my-service -f");
```
List all services:
```ts
await vm.exec("systemctl list-units --type=service");
```
Restart a service:
```ts
await vm.exec("systemctl restart my-service");
```
}
#
URL: https://docs.freestyle.sh/v2/vms/configuration/users-groups.md
Content:
---
title: Users and Groups
description: Create and manage Linux users and groups in your VMs.
---
## Linux Users
Create Linux users in your VM with specific permissions and configurations:
```ts
import { freestyle } from "freestyle-sandboxes";
const { vm } = await freestyle.vms.create({
users: [
{
name: "developer",
uid: 1000,
gecos: "Developer Account",
groups: ["docker", "sudo"],
home: "/home/developer",
shell: "/bin/bash",
},
{
name: "app",
system: true, // System user (UID < 1000)
home: "/var/app",
shell: "/usr/sbin/nologin",
},
],
});
```
### User Properties
- **name** (required) - Username
- **uid** (optional) - Fixed user ID. If not provided, one is allocated automatically
- **gecos** (optional) - Full name or description (e.g., "John Doe" or "Application User")
- **groups** (optional) - Array of groups the user should belong to
- **home** (optional) - Home directory path. Defaults:
- Regular users: `/home/{username}`
- System users: `/`
- **shell** (optional) - Login shell. Defaults:
- Regular users: `/bin/bash`
- System users: `/usr/sbin/nologin`
- **system** (optional) - If true, creates a system user (allocated from system UID range)
## Linux Groups
Create Linux groups for organizing user permissions:
```ts
const { vm } = await freestyle.vms.create({
groups: [
{
name: "developers",
gid: 1001,
},
{
name: "app-users",
system: true, // System group (GID < 1000)
},
],
users: [
{
name: "alice",
groups: ["developers", "docker"],
},
{
name: "bob",
groups: ["developers"],
},
],
});
```
### Group Properties
- **name** (required) - Group name
- **gid** (optional) - Fixed group ID. If not provided, one is allocated automatically
- **system** (optional) - If true, creates a system group (allocated from system GID range)
## Common Patterns
### Web server with dedicated user
```ts
const { vm } = await freestyle.vms.create({
groups: [
{ name: "www-data" },
],
users: [
{
name: "webapp",
groups: ["www-data"],
home: "/var/www",
shell: "/bin/bash",
},
],
systemd: {
services: [
{
name: "web-server",
mode: "service",
exec: ["node server.js"],
user: "webapp",
group: "www-data",
workdir: "/var/www",
},
],
},
});
```
### Application user with restricted permissions
```ts
const { vm } = await freestyle.vms.create({
groups: [
{ name: "appgroup", system: true },
],
users: [
{
name: "appuser",
system: true,
groups: ["appgroup"],
home: "/opt/app",
shell: "/usr/sbin/nologin", // No interactive login
},
],
systemd: {
services: [
{
name: "background-worker",
mode: "service",
exec: ["python worker.py"],
user: "appuser",
group: "appgroup",
workdir: "/opt/app",
},
],
},
});
```
### Shared file permissions
```ts
const { vm } = await freestyle.vms.create({
groups: [
{ name: "shared", gid: 2000 },
],
users: [
{
name: "user1",
groups: ["shared"],
},
{
name: "user2",
groups: ["shared"],
},
],
additionalFiles: {
"/shared/data.txt": {
content: "Shared data",
},
},
});
// Set group ownership
await vm.exec("chgrp -R shared /shared");
await vm.exec("chmod -R g+w /shared");
```
## User Management After Creation
You can also manage users after the VM is created:
```ts
// Add a new user
await vm.exec("useradd -m -s /bin/bash newuser");
// Add user to group
await vm.exec("usermod -aG docker newuser");
// Change user's shell
await vm.exec("chsh -s /bin/zsh newuser");
// Set password (if needed)
await vm.exec("echo 'newuser:password' | chpasswd");
// Create a new group
await vm.exec("groupadd mygroup");
```
## Checking User and Group Information
```ts
// List all users
await vm.exec("cat /etc/passwd");
// List all groups
await vm.exec("cat /etc/group");
// Check current user
await vm.exec("whoami");
// Check user's groups
await vm.exec("groups username");
// Get detailed user info
await vm.exec("id username");
```
## Best Practices
1. **Use system users for services** - Set `system: true` for application users to keep them separate from human users
2. **Disable shell for service users** - Use `shell: "/usr/sbin/nologin"` for users that only need to run services
3. **Use groups for permissions** - Create groups for shared access instead of adding individual permissions
4. **Specify UIDs/GIDs for consistency** - When working with shared storage or multiple VMs, use fixed UIDs/GIDs
5. **Create users in templates** - Put user configuration in templates for consistent caching:
```ts
const template = new VmTemplate({
users: [
{ name: "webapp", system: true },
],
groups: [
{ name: "www-data" },
],
});
```
}
#
URL: https://docs.freestyle.sh/v2/vms/integrations/python.md
Content:
---
title: Python
description: Add Python runtime to your VMs.
---
The Python integration adds Python 3 to your VM and provides helper methods for running Python code.
## Installation
```bash
npm install @freestyle-sh/with-python freestyle-sandboxes
```
## Usage
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmPython } from "@freestyle-sh/with-python";
const { vm } = await freestyle.vms.create({
with: {
python: new VmPython(),
},
});
const res = await vm.python.runCode({
code: 'import json; print(json.dumps({"hello": "world"}))'
});
console.log(res);
// { result: { hello: 'world' }, stdout: '{"hello": "world"}\n', statusCode: 0 }
```
## API
### `vm.python.runCode({ code })`
Executes Python code in the runtime.
```ts
const res = await vm.python.runCode({
code: `
import json
data = {"sum": 1 + 2, "list": [1, 2, 3]}
print(json.dumps(data))
`
});
console.log(res.result); // { sum: 3, list: [1, 2, 3] }
```
**Returns:** `Promise`
```ts
type RunCodeResponse = {
result: Result; // Parsed JSON from stdout (if valid JSON)
stdout?: string; // Raw stdout output
stderr?: string; // Raw stderr output
statusCode?: number; // Exit code
};
```
## Example: Data Processing
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmPython } from "@freestyle-sh/with-python";
const { vm } = await freestyle.vms.create({
with: {
python: new VmPython(),
},
});
const res = await vm.python.runCode({
code: `
import json
# Process some data
numbers = [1, 2, 3, 4, 5]
result = {
"sum": sum(numbers),
"average": sum(numbers) / len(numbers),
"squared": [n ** 2 for n in numbers]
}
print(json.dumps(result))
`
});
console.log(res.result);
// { sum: 15, average: 3, squared: [1, 4, 9, 16, 25] }
```
}
#
URL: https://docs.freestyle.sh/v2/vms/integrations/python/uv.md
Content:
---
title: uv
description: Add Python runtime via uv to your VMs.
---
The uv integration installs Python via [uv](https://github.com/astral-sh/uv), an extremely fast Python package installer and resolver, and provides helper methods for running Python code.
If you're using uv, you don't need to also add the base Python integration. uv manages its own Python installation.
## Installation
```bash
npm install @freestyle-sh/with-uv freestyle-sandboxes
```
## Usage
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmUv } from "@freestyle-sh/with-uv";
const { vm } = await freestyle.vms.create({
with: {
uv: new VmUv(),
},
});
const res = await vm.uv.runCode({
code: "import json; print(json.dumps({ 'hello': 'world' }))"
});
console.log(res);
// { result: { hello: 'world' }, stdout: '{"hello": "world"}\n', statusCode: 0 }
```
## Options
```ts
new VmUv({
version: "0.5.0", // Optional: specific uv version
pythonVersion: "3.13", // Optional: Python version (default: "3.14")
})
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `version` | `string` | `undefined` | uv version to install. If not specified, installs the latest version. |
| `pythonVersion` | `string` | `"3.14"` | Python version to install via uv. |
## API
### `vm.uv.runCode({ code })`
Executes Python code using uv's managed Python runtime.
```ts
const res = await vm.uv.runCode({
code: `
import json
data = {"sum": 1 + 2, "list": [1, 2, 3]}
print(json.dumps(data))
`
});
console.log(res.result); // { sum: 3, list: [1, 2, 3] }
```
**Returns:** `Promise`
```ts
type RunCodeResponse = {
result: Result; // Parsed JSON from stdout (if valid JSON)
stdout?: string; // Raw stdout output
stderr?: string; // Raw stderr output
statusCode?: number; // Exit code
};
```
## Why uv?
[uv](https://docs.astral.sh/uv/) is significantly faster than traditional Python tools:
- **10-100x faster** than pip for package installation
- **Built-in Python version management** - no need for pyenv
- **Rust-powered** - fast and reliable
- **Drop-in replacement** for pip, pip-tools, and virtualenv
## Example: Data Processing
```ts
import { freestyle } from "freestyle-sandboxes";
import { VmUv } from "@freestyle-sh/with-uv";
const { vm } = await freestyle.vms.create({
with: {
uv: new VmUv({ pythonVersion: "3.12" }),
},
});
const res = await vm.uv.runCode({
code: `
import json
numbers = [1, 2, 3, 4, 5]
result = {
"sum": sum(numbers),
"average": sum(numbers) / len(numbers),
"squared": [n ** 2 for n in numbers]
}
print(json.dumps(result))
`
});
console.log(res.result);
// { sum: 15, average: 3, squared: [1, 4, 9, 16, 25] }
```
}