# URL: https://docs.freestyle.sh/getting-started.md Content: --- title: Getting Started description: Setting up Freestyle for your development environment --- 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"; This guide will go through everything you'll need to set up your development environment for using Freestyle. ## Installing Freestyle SDK If you're using `TypeScript`, `JavaScript`, or `Python`, you should install the Freestyle SDK into your project. Otherwise, you'll want to be aware of the [Freestyle API Reference](/API-Reference/web/handle_deploy_web_v2) and how to use it with your preferred HTTP client. ## Get an API Key You can get your API key from the [Freestyle Dashboard](https://admin.freestyle.sh). This key is used to authenticate your requests to the Freestyle APIs. We recommend storing it in an environment variable named `FREESTYLE_API_KEY`, this way our SDKs can automatically detect it. ## Adding to your Vibe Coding Setup ### Add to Cursor Click this link to add Freestyle documentation to your Cursor setup. This will allow you to easily reference our API documentation while coding. Add to Cursor ### Add to VSCode Click this link to add Freestyle documentation to your VSCode setup. This will allow you to access our API documentation directly within your code editor.
Add to VSCode
### Add to Claude Code You can add Freestyle documentation to [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) by running the following command: ```bash claude mcp add --transport http freestyle-docs https://docs.freestyle.sh/api/mcp/mcp ``` ### Add to Claude Desktop For adding to Claude Desktop, you'll need to create or edit the `claude_desktop_config.json` file in your Claude Desktop configuration directory. Add the following configuration: ```json { "mcpServers": { "freestyle-docs": { "command": "npx", "args": ["-y", "mcp-remote", "https://docs.freestyle.sh/api/mcp/mcp"] } } } ``` ## Talk to Us! If you have any questions or need help, you can reach out to us on our [Discord server](https://discord.gg/YTRprVkdnz) ## Where to go now? - [Building an AI App Builder on Freestyle](/guides/app-builder): Learn how to build an AI app builder using Freestyle. - [Web Deployments](/web/web): Explore how to deploy websites using Freestyle. - [Git](/git): Understand how to manage Git repositories with Freestyle. - [Dev Servers](/dev-servers/dev-servers): Learn about running development servers for live previews of code. - [Code Execution](/code-execution/overview): Discover how to run code serverlessly with Freestyle. } # URL: https://docs.freestyle.sh/.md Content: --- title: Welcome to Freestyle description: So what is Freestyle... --- ## What is Freestyle? Freestyle provides a set of tools to help you manage code you didn't write. This can be for your users or for your AI. Managing code you didn't write presents different challenges than running code you did write, we help solve those challenges in a scalable and debuggable way by offering a set of APIs that work with the code you didn't write in different parts of its lifecycle. They are: - [Git](/git): For managing Git repositories on your users' behalf to store the code you didn't write - [Web](/web/web): For deploying websites you didn't write - [Dev Servers](/dev-servers/dev-servers): For running development servers for live previews of code you didn't write - [Domains](/web/domains): For managing domains you don't own - [DNS](/API-Reference/domains/handle_create_domain_verification): For managing your users DNS records - [Execute](/code-execution/overview): For serverlessly running code you didn't write Together, these APIs provide baseline infrastructure for managing your users code. ## Common Use Cases - **AI App Builders**: AI App Builders use Freestyle to manage their code in [Git](/git), iterate on it with Dev Servers, and deploy it to [Web](/web/web) with [custom domains](/web/domains). If you're building an AI App Builder, check out this guide on [Building an AI App Builder on Freestyle](/guides/app-builder). - **Workflow Engines**: Workflow Engines use Freestyle's [Serverless Code Execution](/code-execution/overview) to run custom code to power their workflows. They can also store their workflows using Freestyle's [Git API](/git) in order to get forking, branching and versioning for their workflows. - **Asyncronous AI SWEs**: Asyncronous AI SWEs use Freestyle Dev Boxes to iterate on code, run browser tests and commit their changes to [Git](/git). - **Task Specific AI Agent**: Chatbots integrate with [Serverless Code Execution](/code-execution/overview) to run code in order to do data transformations, pull data, or interact with foreign APIs. ## Why Freestyle? Freestyle is not just a place to run, or deploy, or store code — it's all of that and more. It's a set of tools to help you **manage** code you didn't write. As you deal with the challenges of code you didn't write, lots of companies can help with small parts of the lifecycle, but as you scale those disperate parts become harder and harder to manage. All of Freestyle's APIs are designed to work together, allowing you to trace your code through its lifecycle, debug faster, and scale easily. ## Benefits - **Life Cycle Management**: All of Freestyle's APIs interop with Freestyle Git, allowing you to see what code is deployed in production, where it came from, how it was written, and everything about its history. - **Super Fast Deploys**: When you upload code to Freestyle you **never upload node modules**, instead, we have a cache of them that we share across all our projects. This system makes our deploys noticeably faster than other platforms or what is possible with a traditional deployment pipeline. - **Multi Tenant Management**: Managing 1000s of other people's code, or 1000s of generated codebases from AI has many observability challenges that are not present when managing your own code. You might want to rate limit a user, or a specific deployment, or a specific job an AI does. Tracking these independently is a challenge, we make it easy. - **Battle Tested APIs**: The APIs we provide here used to be our internal API, these docs are deployed through our Web API, and this domain is managed through our Domains API. We know these APIs work because we work with them every day. ## Ready to get started? First, check out our [Getting Started](/getting-started) guide to set up Freestyle for your development environment. Then, dive into our [Building an AI App Builder](/guides/app-builder) guide, or check out our guides for [Web Deployments](/web/web), [Git](/git), [Dev Servers](/dev-servers/dev-servers) or [Code Execution](/code-execution/overview). ## About Freestyle Freestyle started out as a cloud for fullstack TypeScript apps. We had ideas about building fullstack reactivity primitives, but we never had time to build them. Instead, all of our time was sucked into scaling our users projects. We were forced unwillingly into becoming infrastructure experts, and became experts in running code we didn't write. Now, thats what we do. } # URL: https://docs.freestyle.sh/roadmap.md Content: --- title: Roadmap description: Whats coming next for Freestyle --- ## What's coming next for Freestyle? Last updated: 2025-6-25 - [x] **Usage/Metrics**: We are working on generalized metrics for all our API - [ ] **Usage/Metrics V2**: We are working on more granular metrics for all our APIs, including specific domain, user and deployment tracking. We're also examining the concept of script injection for granular metrics on your user's behavior across the apps you deploy. - [x] **Git Triggers**: We are working on Git triggers to allow you to run code actions are taken on a Git repository you manage - [x] **Python SDK**: We are working on a Python SDK to allow you to interact with Freestyle from Python more easily (Released, feature incomplete) - [ ] **Better DNS**: We are working on improvements to our DNS API to make it more powerful, support more record types, be easier to delete records and have a UI in our Dashboard - [ ] ~~**Hot Module Replacement**: We are working on a Serverless HMR solution to give instant deploys for those who can't wait for builds~~ (Cancelled in favor of dev servers) - [ ] ~~**Firecracker Service**: We are working on a Firecracker service to give you the ability to run Firecrackers on your users behalf~~ (Cancelled in favor of dev servers) - [x] **Dev Servers**: New service to run development servers, display their previews to your users, and give your AI Access to control them. - [ ] **Dev Servers Computer Use**: We've already shipped chromium into our dev servers, making them prepared to run your playwright in VM without any external services, but we're beta testing adding a pre-built computer use tool for our customers to be able to let their AI try their apps in dev. - [ ] **Dev Server File System Decoupling**: Running dev servers is expensive, we're working to decouple the filesystem from the dev server so you can read/write from it without starting the machine — this will mean you don't have to start the server until your AI is done generating code, saving you a lot of compute hours and money. - [ ] **Dev Server Stingy Mode**: This is a dev server mode we're working on to exponentially cut down on your dev server costs. We're not sharing too much about it yet, but its going to mean you only pay for the compute you use — think serverless dev servers. - [ ] **Freestyle MCP**: We're working on Freestyle Cloud MCP to allow your AI to pull your debug logs and help you build with Freestyle more easily. If you have any other ideas, we want to hear them, email [ben@freestyle.sh](mailto:ben@freestyle.sh) or [join our Discord](https://discord.gg/YTRprVkdnz) and let us know! } # URL: https://docs.freestyle.sh/blog/docs-revamp.md Content: --- title: We Revamped our Docs for AI Driven Development description: What do docs look like if people don't read them? --- import { cursorUrl, vscodeUrl } from "../../../src/lib/cursor-link"; ## Background Freestyle is [the cloud to run AI Code](/). The tools we're building are weird, and without a lot of explanation they can be hard to understand. We try to regularly have coffee, and hack days with our customers. Over the course of hack days a few weeks ago, we noticed the way we were being used had changed. Our users didn't want to read our docs, they wanted to ask AI questions about them — and AI was giving them the wrong answers. We went through a pivot 11 months ago, and half the time it was still responding about our old product. Sometimes it hallucinated APIs we didn't have, and we even once heard of it giving code snippets referencing how to use one of our competitors! ## How we revamped our Docs for AI We decided to add a series of functionalities to our docs to make them more accessible to AI, and therefore our users. They are as follows: ### LLMs.txt We started with an [llms.txt](/llms.txt) and [llms-full.txt](/llms-full.txt) that we generated from our content. However, we did it badly. Our first generation of the llms.txt files used **relative urls**. This meant that users who copy and pasted the file into their own agents couldn't get anything. Further, a lot of AI docs scrapers are bad, and just didn't handle relative urls at all. Once we fixed that, we still originally linked directly to docs pages. This made AI clients reading the pages read the full html/css/js of the page and not just the text. The worst side effect of this is that we have lots of tab views in our docs that default to JavaScript, when AI Scrapers would read these pages they wouldn't get any Python documentation for our Python users. So we re-exported all of our docs files with a `.md` extension — any page on this website can be gone to with .md at the end of the URL and it will return the raw markdown content of the page, here's [this page](/blog/docs-revamp.md) as markdown. ### AI Buttons AFAIK [Mintlify](https://mintlify.com/) pioneered the copy button at the top of docs pages. For our first iteration we copied it from them. The copy button lets users get the content of the page in a raw text format for the purpose of copying it into their AI's context. A lot of these vibe coders are copying it directly into ChatGPT or Claude, we can do that for them by linking into them. - To link into ChatGPT, we create a link like [https://chatgpt.com/?q=yourprompt](https://chatgpt.com/?q=read%20docs.freestyle.sh/.md). This will open a new chat with the content of the page in it. You can also add `hints=search` to tell it to search the web — this helps it pull in more context around the docs. - To link into Claude, we create a link like [https://claude.ai/new?q=yourprompt](https://claude.ai/new?q=read%20docs.freestyle.sh/.md). This will open a new chat with the content of the page in it. - To link to Perplexity, we create a link like [https://www.perplexity.ai?q=yourprompt](https://www.perplexity.ai/q=explain%20docs.freestyle.sh/). This will open a new search with the content of the page in it. - To link to T3 Chat, we create a link like [https://t3.chat/new?q=yourprompt](https://t3.chat/new?q=What%20is%20Freestyle%20Cloud%20https://freestyle.sh). This will open a new chat with the content of the page in it. We decided not to link to Perplexity or T3 Chat because it caused too much noise. We wanted to figure out how to link to Gemini but we couldn't find a link format for it. Note, you don't want to send the exact same content to each of these. Claude has the ability to fetch the raw MD content directly, whereas ChatGPT has web search only, and T3 Chat has nothing by default. ### Talking Directly to Agents We made our docs accessible via [MCP](https://docs.anthropic.com/en/docs/mcp). This let our vibe coder users add our documentation [directly to their workflows](/getting-started#adding-to-your-vibe-coding-setup). However, at launch this didn't work at all. Our MCP has two tools: - `listDocs`: This lists all of the docs we have available. - `getDocById`: This gets a specific doc by its ID. When we launched them, their descriptions looked like: - `listDocs`: "List the available Freestyle documentation" - `getDocById`: "Get a specific documentation page by its id" This lead to the AI never using these tools — Cursor/AI Agents had access to our documentation, but they never read them. In version 2 of our MCP, we changed the descriptions to: - `listDocs`: "This tool lists all available Freestyle documentation pages. Whenever you're using Freestyle and you aren't sure how to do something, you should use this tool to check if there is documentation for it." - `getDocById`: "This tool gets a specific Freestyle documentation page by its id. You should use this tool whenever you are working with something in Freestyle to be sure you always have up to date information on how to use it." By making our descriptions more actionable and telling the AI when to use them, we saw a huge uptick in their usage, and in our users' ability to debug with agentic systems — now when something isn't working and Freestyle shows up in the logs, AI proactively reads our documentation to see if we have already explained how this issue could've happened. We also have been able to make this incredibly accessible to our users, by offering **deep links** into Cursor and VSCode to auto-install it. - This link installs our MCP in Cursor: You can create one like it by following the [Cursor Deeplink Docs](https://docs.cursor.com/tools/developers#mcp) - This link installs our MCP in VSCode: You can create one like it by following the [VSCode Deeplink Docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_url-handler) We added these links at the top of our `Copy Page` button dropdown, because we believe our users are more likely to want to chat in Cursor or VSCode than anywhere else. ### AI Chat However, some of our users might want to chat in the docs directly. We added AI Chat to our docs without any expensive vendors in a day. Our AI Chat is based on the work of [Stack Auth](https://stack-auth.com/). We use `gemini-2.5-flash`, and we connected it to our MCP so it has access to our docs. We instructed it to always read the relevant docs and reference them in it answers, and we built a simple UI to display the tool calls it makes so users who want more details can refer to the original docs page. ## What we're working on next - We're working on a DevOps MCP that will allow AI Agents to query logs for a specific user's Freestyle project, allowing it to get a better understanding of whats going on and how to help them. For example when a deploy fails, the AI can pull the logs for that deployment along with the docs to help the user debug it. - We are adding a `askQuestion` tool to our MCP that allows AI agents to ask questions and give more context, such as what language they're working with, what other technologies are involved, and in general what they're trying to do. We'll use this to run a semantic search and find the most relevant docs for them. - We're working on an `escalateIssue` tool that will allow AI Agents to report issues directly to us, so we can know they are happening faster and fix them. - We're working on upgrades to our in docs chatbot, including more UI Components accessible to it for it to suggest joining our discord, going to our dashboard, or other actions that might help the user. - We're working on more guides, we've found that step by step examples work really well for showing AI how Freestyle fits into context. - We're on the lookout for more deep link schemes that would allow one click installation of our MCP into tools like Windsurf, Claude Desktop, Devin, and others. - We (want) to have generated SDK documentation beyond our OpenAPI generated docs. We have tried to do this repeatedly and have failed. } # URL: https://docs.freestyle.sh/dev-servers/dev-servers.md Content: --- title: Run a Dev Server description: Use a git repo and dev server to create a hot reload environment. --- import { CodeBlock } from "fumadocs-ui/components/codeblock"; import { CodeTabs } from "../../../src/components/code-tabs"; Dev Servers are instant development and preview environments for your [Git Repositories](/git). They come with everything you need to show a live preview to your users, while giving your agents the ability to work with the code. Dev Servers on Freestyle Dev Servers: - Automatically keep `npm run dev` alive — if it crashes we detect that and restart it. - An [MCP](#model-context-protocol-mcp) server that makes connecting your agents to the dev server easy. - A managed Git Identity for your dev server, so it can push/pull code from the repo. Special Features: - VSCode Web Interface accessible for human collaboration on dev servers. - Chromium + Playwright setup for testing Coming Soon: - VSCode Server Interface to open Dev Servers in VSCode and Chromium - VSCode Language Server Interface to run LSP commands (which the MCP will have access to) ## Creating a Dev Server In order to create a dev server, you'll need a Git Repository to base it on. Then, you can request a dev server for the repo you just created. This will give you a dev server that is automatically running `npm run dev`. If you don't keep it alive, **it will shut itself down**. ## Working with Dev Servers When you run a dev server, you get access to the following utilities: ## The URLs Dev Servers provide a series of URLs that you can use to get different interfaces from the dev server. All these URLs are **ephemeral**, we do not guarantee that they will be available, or the same at any future point. In order to work with them, we recommend re-requesting the dev server every time you want to use them. | URL | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `ephemeralUrl` | This url displays whatever is on **port 3000** of the dev server, or a loading indicator until that shows up | | `mcpEphemeralUrl` | This url is an MCP that lets your AI work with the dev server | | `codeServerUrl` | This url opens a VSCode window in the browser that is inside the dev server, useful for letting you/your users collaborate with the AI. | ## The File System Interface The dev server provides a file system interface that lets you read and write files in the dev server. ### Writing files You can write files using the `fs`. The default encoding is utf-8, but you can specify another one (like `base64`) if you want to upload something like an image. ### Reading files You can read files using the `fs`. The default encoding is utf-8, but you can specify another one (like `base64`) if you want to download something like an image. ### Listing files You can list files in a directory using the `fs`. This is not a recursive listing, it only lists files in the specified directory. If you want to list files recursively, you'll want to use the `process` interface to run a command like `ls -R` or `find .`. ## Executing Commands You can execute any command on the dev server using the `process` interface. ### Running background tasks You can run background tasks using the `process.exec`, by passing a second argument `true` to the `exec` function. This will run the task in the background. ## Committing and Pushing Changes You can commit and push changes to the repo using the `commitAndPush` function. This will commit all changes in the dev server and push them to the repo. The commit will go to the branch that the dev server is currently on, which is usually `main`. > ## Using in NextJS When building a web interface for your dev server, we provide a `FreestyleDevServer` component for NextJS. The component automatically keeps the dev server alive. To use it, you'll first need to create a server action to handle the request. This action will create a dev server for the repo if one isn't already running or return the status if one is already running. ```tsx title="preview-actions.ts" "use server"; import { freestyle } from "@/lib/freestyle"; export async function requestDevServer({ repoId }: { repoId: string }) { const { ephemeralUrl, devCommandRunning, installCommandRunning } = await freestyle.requestDevServer({ repoId }); return { ephemeralUrl, devCommandRunning, installCommandRunning }; } ``` Then, you can use the `FreestyleDevServer` component in your NextJS app with the `requestDevServer` action you just created. ```tsx import { FreestyleDevServer } from "freestyle-sandboxes/react/dev-server"; import { requestDevServer } from "./preview-actions"; export function Preview({ repoId }: { repoId: string }) { ; } ``` ## Working in Parallel You can clone the repo locally and try pushing to it. You should see the dev server update in realtime. Note this will only work if you made the repo public, otherwise, you'll need to create git credentials to access the repo. See the [Git Documentation](/git) for more information. ```bash git clone https://git.freestyle.sh/ ``` For production use in App Builders, we suggest using isomorphic-git to manage git from serverless JavaScript environments. ```ts import git from "isomorphic-git"; import fs from "fs"; import http from "isomorphic-git/http/node"; git.clone({ fs, url: "https://git.freestyle.sh/", singleBranch: true, depth: 1, http, }); ``` ## Model Context Protocol (MCP) MCP is a protocol for allowing AI agents to discover and use tools. Dev servers automatically expose a set of tools for interacting with the file system and other core operations such as installing npm modules, running commands, and testing code. You can get the url for this server in the dev server response. We provide the following tools by default: - readFile: Read a file from the dev server - writeFile: Write a file to the dev server - editFile: Search and replace based file editing - ls: List files in a directory - exec: Execute a command on the dev server - commitAndPush: Commit and push changes to the repo - npmInstall: Install an npm module on the dev server - npmLint: Lint the code on the dev server Together, these tools make it easy to get your agents started on development. They do not handle everything, but we recommend the MCP as a good starting point for building your own tools. ## On Current Limitations Dev Servers are primarily made to run **JavaScript/typescript Dev Servers**. When we start a dev server, we run `npm run dev` and expect it to start a server on port 3000. If you want to add more on startup, you can change the script in `npm run dev` to run whatever you want. We automatically keep track of the process and restart it if it crashes. This approach makes it theoretically work with other languages, but in practice not well. We are actively working on improving this. For the best experience today, a good rule of thumb is, "Would this be a part of my npm run dev workflow locally?" If the answer is yes, then it will work well on a dev server. If not, let us know, we'd like to make it work better. } # URL: https://docs.freestyle.sh/code-execution/overview.md Content: --- title: Overview description: When and why to use serverless code execution --- ## Overview Serverless code execution runs on a simple system — you send your TypeScript code and you get the output back. If the code has a syntax error, you get a nice trace of what that error was back. It is made for you to run code you only want to run once — or very few times. ### Why Use it? It is noticeably faster and cheaper than any VM based systems on the market because it doesn't run on VMs. Instead, it uses Freestyle's Serverless Code Execution Engine. The same technology that Google Chrome uses to isolate code between tabs is what we use to isolate code between users. This means that while other companies start whole VMs to run your code, for us running your code is like opening a tab — except we also always keep lots of tabs pre-opened to make it even faster. The fastest cold start time of any competitors we've seen is 90ms, our average full execution time is **\<150ms** total It also lets you use **arbitrary npm packages** without performance impact. You can list the packages you want in the `nodeModules` field of the `configuration` of the API call, and we'll cache them for all future uses. This means that sometimes you'll run code with new modules for the first time and it will take >10 seconds, but for all future runs that code would be instant. ## When **Not** to Use it? For code you want to be called repeatedly, you should deploy it to a [Web Deployment](/web/web). Serverless code execution is great for code that doesn't need a code, but you **cannot run binaries** in it. You also cannot run code with persistent state in it, once a script finishes running it's state is lost. For code that needs to run a long time, run binaries, have persistent state, or generally needs a proper VM, you should use a [Dev Server](/dev-servers/dev-servers). While we store the execution code for some amount of time after it has been run, it can be deleted at any time. If you want to store the code for a long time, you should do it on your side, or use a [Git Repository](/git). ## How to Use it? - You can check out the [Run Code](/code-execution/run) page for a full example of how to use the API We also provide a series of integrations with common AI Agent Frameworks to make it easy to run with your AI, including [the Vercel AI SDK](/code-execution/integrations/vercel), [Mastra](/code-execution/integrations/mastra), [LangGraphJS](/code-execution/integrations/langgraphjs), [LanggraphPy](/code-execution/integrations/langgraphpy), [the OpenAI Python SDK](/code-execution/integrations/openai), and [the Gemini Python SDK](/code-execution/integrations/gemini). } # URL: https://docs.freestyle.sh/code-execution/run.md Content: --- title: Run Code description: Run code you didn't write --- import InstallSandboxes from "../../../src/components/installSandboxes"; import { CodeTabs } from "../../../src/components/code-tabs"; import { Steps, Step } from "fumadocs-ui/components/steps"; ## Simple Code Execution ### Install the required dependencies ### Create a Freestyle Client ### Run the code { // calculate the factorial of 543 return Array.from({ length: 543 }, (\_, i) => i + 1).reduce((acc, cur) => acc \* cur, 1); }\`; api.executeScript(code).then((result) => { console.log("Result: ", result); }); `}} python={{ title: "run.py", code: ` import freestyle client = freestyle.Freestyle("YOUR_FREESTYLE_API_KEY") # make sure to set this const code = """ export default () => { // calculate the factorial of 543 return Array.from({ length: 543 }, (\_, i) => i + 1).reduce((acc, cur) => acc \* cur, 1); } """ response = client.execute_script(code) print(f"Result: {response.result}") `}} /> ## Advanced Code Execution ### Custom Node Modules { // Fetch data from an external API const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1'); return res.data; }\`, { nodeModules: { axios: "0.21.1", // specify the version of the module you want to use }, } ) .then((result) => { console.log("Result: ", result); }); `, title: "run.js" }} python={{ code: ` import freestyle client = freestyle.Freestyle("YOUR_FREESTYLE_API_KEY") result = client.execute_script( code=""" import axios from 'axios'; export default async () => { // Fetch data from an external API const res = await axios.get('https://jsonplaceholder.typicode.com/todos/1'); return res.data; } """, freestyle.FreestyleExecuteScriptParamsConfiguration( node_modules={ "axios": "0.21.1" # specify the version of the module you want to use } } ), ) print(f"Result: {result.result}") `, title: "run.py" }} /> This pattern can be used for any node modules, and can be used to connect to any API or service. ### Custom Environment Variables { console.log("Result: ", result); }); `, title: "run.js" }} python={{ code: ` import freestyle client = freestyle.Freestyle("YOUR_FREESTYLE_API_KEY") result = client.execute_script( code=""" return process.env.SOME_ENV_VAR; """, freestyle.FreestyleExecuteScriptParamsConfiguration( env_vars={ "SOME_ENV_VAR": "Hello, World!" } ) ) print(f"Result: {result.result}") `, title: "run.py" }} /> Environment variables are accessible via the `process.env` object in the code execution environment. ## Running Code with AI Check out our integrations — we have support for all major AI Agent frameworks. AI models today have gotten incredibly good at writing code, when you give your Agents the ability to run code the scope of problems they can solve. Specifically it makes, data integration and analytical questions. When connecting to an external tool you can build 20 different tools, or you can give your AI the docs and let it figure out how to connect — the ladder is much more adaptable. Most people who use us start with the prebuilt AI integrations liners, but then move towards a more fine grained approach executing the code themselves with custom definitions. } # URL: https://docs.freestyle.sh/git/git-objects-api.md Content: --- title: The Git Objects API description: A comprehensive guide to working with Git objects (blobs, commits, trees, tags, and refs) in Freestyle --- # Git Objects API The Git Objects API allows you to access and explore Git objects directly from your repositories. This API is useful for building tools that need to understand Git repository structure, inspect files, visualize commit history, and more. ## 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 The Git Objects API in Freestyle provides access to all these object types through a consistent REST API. ## 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 ```javascript // Using fetch directly with the API fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/blobs/${blobHash}`, { headers: { "Authorization": "Bearer your-api-key" } }) .then(response => response.json()) .then(blob => { // blob.content is base64 encoded const decodedContent = atob(blob.content); console.log(decodedContent); }); ``` 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 ```javascript fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits/${commitHash}`, { headers: { "Authorization": "Bearer your-api-key" } }) .then(response => response.json()) .then(commit => { console.log(commit.message); console.log(commit.author); console.log(commit.tree.sha); }); ``` 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; } ``` ### 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 ```javascript fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${treeHash}`, { headers: { "Authorization": "Bearer your-api-key" } }) .then(response => response.json()) .then(tree => { // Inspect files and subdirectories tree.tree.forEach(entry => { if (entry.type === "blob") { console.log(`File: ${entry.path}`); } else if (entry.type === "tree") { console.log(`Directory: ${entry.path}`); } }); }); ``` 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 ```javascript fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/tags/${tagHash}`, { headers: { "Authorization": "Bearer your-api-key" } }) .then(response => response.json()) .then(tag => { console.log(tag.name); console.log(tag.message); console.log(tag.target.sha); }); ``` 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: ```javascript // This function would be called by your webhook handler async function processGitTriggerWebhook(webhookPayload, apiKey) { const repoId = webhookPayload.repoId; const commitHash = webhookPayload.commit; const headers = { "Authorization": `Bearer ${apiKey}` }; // Get the commit to find what was changed const commitResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits/${commitHash}`, { headers } ); const commit = await commitResponse.json(); console.log(`Processing commit: ${commit.message}`); console.log(`Author: ${commit.author.name} <${commit.author.email}>`); // Get the tree pointed to by the commit const treeResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${commit.tree.sha}`, { headers } ); const rootTree = await treeResponse.json(); // Example: Find package.json in the repository const packageJsonEntry = treeEntries.find(entry => entry.type === "blob" && entry.path === "package.json") if (packageJsonEntry) { // Get the content of package.json const blobResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/blobs/${packageJsonEntry.sha}`, { headers } ); const blob = await blobResponse.json(); // Parse the package.json content 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: ```javascript async function exploreDirectory(repoId, treeSha, apiKey, currentPath = "") { const headers = { "Authorization": `Bearer ${apiKey}` }; const treeResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${treeSha}`, { headers } ); const tree = await treeResponse.json(); for (const entry of tree.tree) { const entryPath = currentPath ? `${currentPath}/${entry.path}` : entry.path; if (entry.type === "tree") { // Recursively explore subdirectories await exploreDirectory(repoId, entry.sha, apiKey, entryPath); } else if (entry.type === "blob") { // Process files console.log(`File: ${entryPath}`); // You could fetch the blob content here if needed } } } ``` ### Viewing File Contents from a Specific Commit ```javascript async function viewFileAtCommit(repoId, commitHash, filePath, apiKey) { const headers = { "Authorization": `Bearer ${apiKey}` }; // Get the commit const commitResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits/${commitHash}`, { headers } ); const commit = await commitResponse.json(); // Get the root tree let treeResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${commit.tree.sha}`, { headers } ); let currentTree = await treeResponse.json(); // 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}`); } treeResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${dirEntry.sha}`, { headers } ); currentTree = await treeResponse.json(); } // 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 const blobResponse = await fetch( `https://api.freestyle.sh/git/v1/repo/${repoId}/git/blobs/${fileEntry.sha}`, { headers } ); const blob = await blobResponse.json(); // Decode and return the content return atob(blob.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 ## Conclusion The Git Objects API provides low-level access to Git repository data, enabling you to build powerful tools for Git repository management, visualization, and integration with other systems. For detailed API reference documentation, see the [API Reference](/API-Reference/git/handle_get_blob) section. Refs API coming soon. } # URL: https://docs.freestyle.sh/git/git-overview.md Content: --- title: Using Git with Freestyle description: Learn how to use Freestyle's Git API to manage and deploy code from Git repositories. --- # Git with Freestyle Freestyle provides a comprehensive Git API that enables you to manage Git repositories, control access permissions, and set up automation triggers. This document covers everything you need to know about using Git with Freestyle. ## Overview Freestyle's Git support allows you to: - Create and manage Git repositories - Control repository access with identity-based permissions - Set up automation triggers for repository events - Integrate with CI/CD workflows - Deploy applications directly from Git ## Git Repositories ### Creating a Repository To create a new Git repository: ```javascript import { FreestyleSandboxes } from "freestyle-sandboxes"; const sandboxes = new FreestyleSandboxes({ apiKey: "your-api-key", }); // Create a basic repository sandboxes .createGitRepository({ name: "example-repo", }) .then((res) => { console.log(res.repoId); }); ``` Note that the name of the repository is optional and is for display purposes only. The actual repository ID is generated by Freestyle. Create a public repository: ```javascript sandboxes .createGitRepository({ name: "public-example", public: true, }) .then((res) => { console.log(res.repoId); }); ``` Create a repository from an existing Git repository ```javascript sandboxes.createGitRepository({ name: 'cloned-example', source: { type: 'git', url: 'https://github.com/freestyle-sh/cloudstate', branch: 'main' // Optional: specify branch to checkout depth: 0, // Optional: shallow clone } }).then(res => { console.log(res.repoId); }); ``` Note that there is currently no support for private repositories from outside of Freestyle. After creating a repository, you can push code to it using the standard Git CLI: ```bash # Add the repository as a remote git remote add freestyle https://git.freestyle.sh/your-repo-id # Push your code git push freestyle main ``` ### Listing Repositories You can list all repositories associated with your account: ```javascript sandboxes.listGitRepositories().then((repos) => { console.log(repos); }); ``` ### Deleting Repositories When you no longer need a repository, you can delete it: ```javascript sandboxes .deleteGitRepository({ repoId: "repo-id", }) .then(() => { console.log("Repository deleted"); }); ``` ## Identity and Access Management Freestyle uses identity-based access control for Git repositories. This allows you to create separate identities for different purposes (e.g., CI/CD, team members) and grant them specific permissions. ### Creating an Identity ```javascript sandboxes.createGitIdentity().then((identity) => { console.log(identity.id); }); ``` ### Managing Access Tokens Each identity can have one or more access tokens used for authentication: ```javascript // Create a token for an identity sandboxes .createGitToken({ identityId: "identity-id", }) .then((token) => { console.log(token.value); // Store this securely }); // List tokens for an identity sandboxes .listGitTokens({ identityId: "identity-id", }) .then((tokens) => { console.log(tokens); }); // Revoke a token sandboxes .revokeGitToken({ identityId: "identity-id", tokenId: "token-id", }) .then(() => { console.log("Token revoked"); }); ``` ### Managing Permissions You can grant identities different levels of access to your repositories: ```javascript // Grant read-only access sandboxes .grantPermission({ identityId: "identity-id", repoId: "repo-id", permission: "read", }) .then(() => { console.log("Read access granted"); }); // Grant read-write access sandboxes .grantPermission({ identityId: "identity-id", repoId: "repo-id", permission: "write", }) .then(() => { console.log("Write access granted"); }); // List permissions for an identity sandboxes .listPermissions({ identityId: "identity-id", }) .then((permissions) => { console.log(permissions); }); // Revoke access sandboxes .revokePermission({ identityId: "identity-id", repoId: "repo-id", }) .then(() => { console.log("Access revoked"); }); ``` ## Git Triggers Git triggers allow you to automate actions when certain events occur in your repositories, such as a push to a specific branch. ### Creating a Trigger ```javascript // Create a webhook trigger for all pushes to the main branch sandboxes .createGitTrigger({ repoId: "repo-id", trigger: { event: "push", branch: ["main"], // Optional: filter by branch fileGlob: ["*.js"], // Optional: filter by file patterns }, action: { type: "webhook", url: "https://your-webhook-url.com", }, }) .then((result) => { console.log(`Trigger created: ${result.triggerId}`); }); ``` The `action` currently only supports webhooks. The webhook payload includes the following fields: ```typescript interface GitTriggerPayload { repoId: string; // The name of the branch that was updated branch: string; // The SHA of the commit that triggered the webhook commit: string; } ``` #### Local Development For local development, you can use a tool like [tailscale](https://tailscale.com) to create a secure tunnel to your localhost, allowing your webhook to receive events from Freestyle. You can setup Tailscale by following the quickstart guide [here](https://tailscale.com/kb/1017/install). To set up a tunnel once you have `tailscale` installed, you can use the following command (Replace `3000` with the port your server is listening on): ```bash tailscale funnel 3000 ``` This exposes your server to the public internet on a url given to you by Tailscale. The output should look like this: ``` Available on the internet: https://..ts.net/ |-- proxy http://127.0.0.1:3000 Press Ctrl+C to exit. ``` Use the provided url as the webhook URL in your trigger configuration. ### Listing Triggers ```javascript sandboxes .listGitTriggers({ repoId: "repo-id", }) .then((triggers) => { console.log(triggers); }); ``` ### Deleting Triggers ```javascript sandboxes .deleteGitTrigger({ repoId: "repo-id", triggerId: "trigger-id", }) .then(() => { console.log("Trigger deleted"); }); ``` ## Authentication for Git Operations To authenticate Git CLI operations with Freestyle, you'll need to configure Git to use your access token: ```bash # Set up credential helper for freestyle domains git config --global credential.helper store echo "https://x-access-token:your-token@git.freestyle.sh" >> ~/.git-credentials ``` For non-standard Git clients that only provide an `access token` field, just use the token. The username is your identity ID, and the password is your access token. The access token ID is only used for revoking the token and isn't needed here. Don't do this on a shared machine, as it will set your git credentials globally. To do this locally, you can use the `--local` flag with `git config`. ## Continuous Integration Example Here's an example of how to set up a CI workflow with Freestyle Git: 1. Create a dedicated Git identity for CI: ```javascript const ciIdentity = await sandboxes.createGitIdentity(); const token = await sandboxes.createGitToken({ identityId: ciIdentity.id, }); console.log(`CI token: ${token.value}`); ``` 2. Grant the identity write access to your repository: ```javascript await sandboxes.grantPermission({ identityId: ciIdentity.id, repoId: "repo-id", permission: "write", }); ``` 3. Set up a webhook trigger to notify your CI system: ```javascript await sandboxes.createGitTrigger({ repoId: "repo-id", trigger: { event: "push", branch: ["main"], }, action: { type: "webhook", url: "https://your-ci-system.com/webhook", }, }); ``` 4. Configure your CI system to clone the repository, run tests, and deploy if successful. ## Deployment with Git Freestyle makes it easy to deploy applications directly from Git repositories: ```javascript // TODO: This is not yet implemented // Deploy a web application from a Git repository const yourRepoId = "your-repo-id"; sandboxes .deployWeb({ source: { type: "git", url: `https://git.freestyle.sh/${yourRepoId}`, branch: "main", // Optional: defaults to main }, { entrypoint: "index.js", // Optional: defaults to index.js domains: ["example.style.dev"], // Optional: specify domains build: true // automatically detect your framework and build for you } }) .then((deployment) => { console.log(`Deployed to: ${deployment.url}`); }); ``` ## Git Objects API Freestyle provides a Git Objects API that allows you to access and explore Git objects directly from your repositories. This API is useful for building tools that need to understand Git repository structure, inspect files, visualize commit history, and more. For a detailed guide on working with Git objects, check out our [Git Objects API Guide](/git/git-objects-api). ## GitHub Synchronization Freestyle provides seamless bidirectional synchronization between your Freestyle repositories and GitHub repositories. This integration allows you to maintain synchronized code across both platforms while leveraging Freestyle's infrastructure. For complete setup instructions and usage details, see our [GitHub Sync Guide](/git/github-sync). ## API Reference For complete details on all available Git API endpoints, refer to the [API Reference](/api-reference/git) section. ## Best Practices 1. **Use dedicated identities** for different purposes (CI/CD, team members, etc.) 2. **Regularly rotate access tokens** for security 3. **Limit permissions** to the minimum required access level 4. **Use webhooks** to integrate with your existing CI/CD workflows 5. **Secure sensitive variables** using environment variables rather than committing them to your repository } # URL: https://docs.freestyle.sh/git/github-sync.md Content: --- title: GitHub Synchronization description: Seamlessly sync your Freestyle repositories with GitHub using bidirectional synchronization --- import { CodeTabs } from "../../../src/components/code-tabs"; # GitHub Synchronization Freestyle provides seamless bidirectional synchronization between your Freestyle repositories and GitHub repositories. This integration allows you to maintain synchronized code across both platforms while leveraging Freestyle's infrastructure capabilities alongside GitHub's collaboration features. ## Overview The GitHub sync feature enables you to: - **Automatically sync changes** between Freestyle and GitHub repositories - **Leverage GitHub workflows** on the Github side while using Freestyle's infrastructure - **Collaborate on GitHub** your users' teams can continue to use Github without interruption while you utilize Freestyle's infrastructure - **Avoid conflicts** with intelligent conflict detection ## How It Works Freestyle's GitHub integration uses GitHub Apps to provide secure, repository-specific access. When you push code to either platform, changes are automatically synchronized to the other, ensuring both repositories stay in sync. ### Architecture 1. **GitHub App**: You own the custom Github app with repository permissions for secure access to the repositories on Github 2. **Webhook Processing**: You get real-time notifications when code changes occur on Github or on Freestyle 3. **Bidirectional Sync**: When a change is made on the Github repository, its proactively synced to the Freestyle repository, and vice versa 4. **Conflict Detection**: We prevents data loss by detecting diverged branches before applying changes ## Setup Process ### Step 1: Create a GitHub App **Coming Soon**: Bring-your-own GitHub App support will be available soon, allowing you to use your existing GitHub Apps with Freestyle's sync system. 1. Navigate to the **Git > Sync** section in your [Freestyle Admin Dashboard](https://admin.freestyle.sh) 2. Click **"Create GitHub App"** 3. Choose a custom name for your GitHub App (this will be visible to users when they install it) 4. Click **"Create App"** - you'll be redirected to GitHub to confirm app creation 5. After confirming on GitHub, you'll be redirected back to Freestyle where your app credentials are automatically encrypted and stored ### Step 2: Configure App Settings (Optional) You can customize your GitHub App settings if needed: 1. From the sync page, click **"Configure on GitHub"** or navigate directly to your app on GitHub 2. Update **App Name**, **Homepage URL**, or **Description** as desired **Important**: Do not change the webhook URL or remove any permissions, as this will break synchronization functionality. ### Step 3: Install the App To sync repositories, the GitHub App must be installed on the repositories you want to sync: **For App Builders**: You'll need to have your users install your GitHub App on their repositories. Each user must install the app to enable sync with their own repos. **Installation Process**: 1. From the sync page, click **"Install on GitHub"** or go to your GitHub App's page 2. Click **"Install"** (or **"Configure"**, if already installed) 3. Choose which repositories or organizations to grant access to: - **All repositories**: Grants access to all current and future repos - **Selected repositories**: Choose specific repositories to sync **Sharing with Users**: You can share your GitHub App's installation page with users by providing them with the app URL from your GitHub App settings. ## Repository Configuration Once your GitHub App is installed on the target repositories, you can configure repository synchronization: ### Linking Repositories #### Via Admin Dashboard 1. In your Freestyle admin dashboard, navigate to **Git > Repositories** 2. Select the Freestyle repository you want to sync 3. Click **"Configure GitHub Sync"** 4. Choose the corresponding GitHub repository from the repositories where your app is installed 5. Save the configuration #### Via API To configure sync programmatically, you can use the GitHub sync endpoint or our SDK. For complete API documentation on configuring GitHub sync, see the [GitHub Sync Configuration API Reference](/API-Reference/git/configure_github_sync). **For App Builders**: You can only link repositories where your users have installed your GitHub App. Make sure your users have completed the app installation process first. ### Sync Behavior When repositories are linked, synchronization happens automatically: #### Automatic Sync Triggers - **GitHub → Freestyle**: Triggered when you push to GitHub - **Freestyle → GitHub**: Triggered when you push to your Freestyle repository - **Branch Operations**: New branches, branch deletions, and updates #### 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 ## Sync Process Details ### Bidirectional Synchronization Freestyle's sync engine performs intelligent bidirectional synchronization: 1. **Fetch Updates**: Retrieves all changes from both repositories 2. **Analyze Branches**: Checks each branch for conflicts or divergence 3. **Fast-Forward Merges**: Applies updates where branches haven't diverged 4. **Conflict Detection**: Identifies branches that have conflicting changes 5. **Safe Updates**: Only applies changes that won't cause data loss ### Conflict Handling When conflicts are detected, Freestyle prioritizes data safety: - **No Automatic Merges**: Conflicting branches are not automatically merged - **Conflict Detection**: Conflicts can be viewed in the admin dashboard - **Manual Resolution**: You can resolve conflicts manually in either repository - **Resume Sync**: Once conflicts are resolved, sync resumes automatically **Note**: Automatic conflict notifications and monitoring are not yet implemented. You'll need to check the admin dashboard or a clone of the repo to monitor for conflicts. ### Branch Management The sync engine handles various branch scenarios: - **New Branches**: Created on both repositories when added to either side - **Updated Branches**: Fast-forwarded when no conflicts exist - **Deleted Branches**: Removed from both repositories when deleted from either side - **Diverged Branches**: Requires manual resolution, we **never** force push or overwrite branches. } # URL: https://docs.freestyle.sh/git.md Content: --- title: Overview 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"; ## 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, 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 [Using Git Guide](/git/git-overview) } # URL: https://docs.freestyle.sh/web/configuration.md Content: --- title: Advanced Configuration description: Learn about the advanced options for web deployments on Freestyle --- To get started with deploying a website, you should read the [Deploying a Website](/web/web) guide. This document will cover all of the advanced configuration options available for web deployments, and should be read after you've gone through the basics guide. When deploying a website to Freestyle, there are two distinct parameter groups you can configure: 1. [**Source**](#deployment-source): This defines where your website's code is coming from, such as a Git repository, raw files, or a tarball link. 2. [**Configuration**](#deployment-configuration): This includes settings like the domains to deploy to, whether to build the code, entrypoint of the application, timeout behavior, and more. ## Deployment Source There are 3 distinct types of sources you can use to deploy a website: 1. **Files**: A set of the files you want to deploy. You can encode the files in base64 or utf-8, however in most cases base64 is recommended — images seem to get corrupted when using utf-8. The format of the files is a dictionary where the keys are the file paths and the values are dictionaries with the file content and its encoding. ```json title="files.json" { "source" : { "kind": "files", "files": { "index.js": { "content": " import {createServer} from 'http'; createServer((req, res) => { res.end('Hello World\\n'); }).listen(3000); ", "encoding": "utf-8" }, } } } ``` 2. **Tar Link**: A link to a tarball that contains the files you want to deploy. This is most commonly used when your main app working with Freestyle is hosted on a serverless platform with aggressive request size limits that make uploading the files directly impractical. Instead, you allow your users to upload their files directly to a storage service (like S3) and then provide a signed link to the tarball. The format of the tarball should be a gzipped tar archive. ```json title="tar.json" { "source": { "kind": "tar", "url": "https://s3.example.com/signedurl/to/tarball.tar.gz" } } ``` 3. **Git**: A Git repository that contains the files you want to deploy. This can be a public repository, a repository with basic authentication in the url, or any repository in your account on [Freestyle Git](/git). This is our most common, and recommended way to deploy a website, because it provides observability and debugability into your deployments. The format of the Git repository is a dictionary with the kind set to `git` and the url set to the repository URL, optionally with a `branch` key to specify the branch to deploy (defaulting to the default branch when not specified), and a `dir` key to specify the directory to deploy (defaulting to the root directory when not specified). ```json title="git.json" { "source": { "kind": "git", "url": "https://git.freestyle.sh/gitrepoid", "branch": "prod", "dir": "web" } } ``` When deploying a website, its normal to start using the **files** source for debugging and iteration, but move to the **tar** or **git** sources as your needs evolve. When deploying to Freestyle you **never upload node modules**. Instead, you should include your `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lock` file in your source. Freestyle will automatically install the dependencies for you when deploying the website. This is done to ensure that your deployment is as small as possible, which makes it faster to deploy and easier to scale. ## Deployment Configuration Freestyle provides a wide range of configuration options for web deployments. ### Entrypoint The `entrypoint` is the main file of your application. This is the file that will be executed when your website is accessed. By default, Freestyle will automatically detect the entrypoint based on the source type. For example, if you are deploying a Next.js, Vite, or Expo application it will automatically find the correct entrypoint to run the application from. However, if you are using a custom setup, you can specify the entrypoint manually. ```json title="entrypoint.json" { "config": { "entrypoint": "index.js" } } ``` ### Domains You can specify the domains you want to deploy your website to. This is a list of domain names that you have verified ownership of. You can also specify subdomains of a domain you own. As long as they are pointing to Freestyle's servers and you own a parent of them, you can deploy to them. Behind the scenes, this creates [Domain Mappings](/web/domain-mapping) that map the domains to your deployment. ```json title="domains.json" { "config": { "domains": ["yourdomain.com", "subdomain.yourdomain.com"] } } ``` ### Node Modules By default, Freestyle will automatically install the dependencies for your website based on the lock file you provide in your source. However, if you want to use specific npm packages that are not in your lock file, you can specify them in the `nodeModules` field of the configuration. ```json title="nodeModules.json" { "config": { "nodeModules": { "express": "^4.17.1", "cors": "^2.8.5" } } } ``` ### Environment Variables You can specify environment variables that will be available to your website at runtime. These environment variables **are not available at build time**. Environment variables are tied directly to deployments, if you want to change them you should make a new deployment with the new environment variables. ```json title="env.json" { "config": { "envVars": { "API_KEY": "your-api-key", "ANOTHER_VAR": "another-value" } } } ``` ### Timeout You can specify the timeout for your website. This is the maximum amount of time that Freestyle will wait for your website to respond before timing out. Unlike most serverless platforms that start the timeout after the start of the last request, Freestyle starts the timeout **after the last TCP packet from the client is recieved**. This means you can have long-running websockets as long as you are pinging at a rate that is less than the timeout. ```json title="timeout.json" { "config": { "timeout": 60 } } ``` ### Build By default, Freestyle will not build your app — it will deploy the files its given as is. However, if you want to send Freestyle your unbuilt code to build and deploy, you can set the `build` field to `true`. Freestyle will automatically detect the framework and build the code for you. This is useful for frameworks like Next.js, Vite, and Expo. ```json title="build.json" { "config": { "build": true } } ``` However, sometimes you want to set your own configuration options. If you want to add **environment variables at build time** you can set the `build` field to a dictionary with the `envVars` field set to the environment variables you want to set at build time. If you don't specify a command, Freestyle will still try to automatically detect the framework and build the code for you with the specified environment variables. ```json title="build-env.json" { "config": { "build": { "envVars": { "API_KEY": "your-api-key", "ANOTHER_VAR": "another-value" } } } } ``` You can also set the `build` field to include a `command` to specify a custom build command, and `outDir` to specify the output directory of the build. This is useful if you are using a custom build tool or if your framework does not have a standard build command. ```json title="build-custom.json" { "config": { "build": { "command": "npm run build", "outDir": "dist" } } } ``` All of these options can be combined together to create a custom build configuration. For example, you can set the `build` field to include environment variables, a custom command, and an output directory. ```json title="build-combined.json" { "config": { "entrypoint": "index.js", "domains": ["yourdomain.com"], "envVars": { "API_KEY": "your-api-key", "SOME_OTHER_KEY": "some-other-value" }, "build": { "envVars": { "ANOTHER_VAR": "another-value" }, "command": "npm run build", "outDir": "dist" } } } ``` ### Next Steps When [deploying a website](/web/web), we also have specific guide for preparing common frameworks like [NextJS](/web/frameworks/next), [Vite](/web/frameworks/vite), and [Expo](/web/frameworks/expo). These guides will help you set up your website with the best practices for each framework. } # URL: https://docs.freestyle.sh/web/deploy-to-custom-domain.md Content: --- title: Deploy to a Custom Domain description: Prepare a custom domain for deployment with Freestyle --- import { Steps, Step } from "fumadocs-ui/components/steps"; import { Callout } from "fumadocs-ui/components/callout"; Once you have [verified ownership of a domain](/web/domains), you have the ability to deploy websites to it. However, verification is only the first step. You must also configure the domain to point to Freestyle's servers. ### Single APEX Domain If you want to deploy to an APEX domain (e.g. `yourdomain.com`), you need to add an A record to your domain's DNS settings. The A record should point to the IP address of the Freestyle server that will host your website. ``` Type: A NAME: @ VALUE: 35.235.84.134 ``` ### Subdomain If you want to deploy to a subdomain (e.g. `subdomain.yourdomain.com`), you need to add a A record to your domain's DNS settings. The A record should point to the Freestyle server that will host your website. ``` Type: A NAME: subdomain VALUE: 35.235.84.134 ``` ### All subdomains of a domain If you want to deploy to all subdomains of a domain (e.g. `*.yourdomain.com`), you need to add a wildcard A record to your domain's DNS settings. The A record should point to the Freestyle server that will host your website. ``` Type: A NAME: * VALUE: 35.235.84.134 ``` When dealing with DNS records its easy to set a record like `yourdomain.com.yourdomain.com` on accident. The easiest way to test if the record is set correctly is to run `dig domain.youexpect.com` and see if it shows up. } # URL: https://docs.freestyle.sh/web/domain-mapping.md Content: --- title: Mapping Domains description: Routing traffic to where it should go --- Domain Mappings are a concept in Freestyle for routing traffic to deployments. They allow you to programmatically control where traffic flows from domains to deployments. ## Semantics Freestyle deployments are **immutable**, meaning that once you create a deployment it does not change and cannot be deleted. Domain mappings are **mutable** traffic mappings that go from domain -> deployment. ### Domain Mappings on Deployments When you create a deployment with a list of domains in its configuration, Freestyle is automatically creating those domain mappings to that deployment. ```typescript title="deploy.ts" import { Freestyle } from "freestyle-sandboxes"; const freestyle = new Freestyle({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { const result = await freestyle.deployWeb( { kind: "files", files: { /*...*/ }, }, { domains: ["some.style.dev", "your.usersdomain.com"], // [!code ++] The domains here create domain mappings to the deployment } ); } ``` ### Creating Domain Mappings This code creates a simple domain mapping from `example.style.dev` to a deployment with ID `deployment-id`. If `example.style.dev` was already mapped to another deployment, it will be updated to point to the new deployment. ```typescript title="createDomainMapping.ts" import { Freestyle } from "freestyle-sandboxes"; const freestyle = new Freestyle({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function createDomainMapping() { const result = await freestyle.insertDomainMapping({ domain: "example.style.dev", deploymentId: "deployment-id", // The ID of the deployment you want to map the domain to }); console.log("Created domain mapping:", result); } ``` ### Deleting Domain Mappings To delete a domain mapping, you can use the `deleteDomainMapping` method. This will remove the mapping for the specified domain, but will not delete the deployment itself. When a domain is pointed at Freestyle, but has no domain mapping, it returns a `Site Not Found` page to visitors. ```typescript title="deleteDomainMapping.ts" import { Freestyle } from "freestyle-sandboxes"; const freestyle = new Freestyle({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function removeDomainMapping() { const result = await freestyle.removeDomainMapping("example.style.dev"); console.log("Deleted domain mapping:", result); } ``` ## Unpublishing a Website When you want to unpublish a website, rather than deleting the deployment, instead you should unmap every domain it uses. This will make the deployment inaccessible to any users and effectively inactive. While you could do this by unmapping each domain to point at nothing, a nicer approach is to remap them to a "built with us" page. You can do this by creating a deployment with whatever you want to say about yourself, saving its ID, and then remapping all the domains of deployments you want to take down to point at it. When users see a "Site not found" page, even if its their fault for not paying, they don't know that and blame you. By adding this page, its clear to them that your servers are working, and that they need to go check with you on whats going on. ## Relevant Other docs - [Deploying Websites](/web/web) - [Verifying Custom Domains](/web/domains) } # URL: https://docs.freestyle.sh/web/domains.md Content: --- title: Verify a Custom Domain description: Verify ownership of and start managing custom domains via Freestyle API --- import InstallSandboxes from "../../../src/components/installSandboxes"; import { Steps, Step } from "fumadocs-ui/components/steps"; import { CodeTabs } from "../../../src/components/code-tabs"; Domain Verification is the process for you to prove that you own a domain. It is useful not just for managing to your domains, but for managing your user's domains. Once you have verified ownership of a domain, you can deploy to it or any of its subdomains, provision certificates for it, and manage its DNS through the Freestyle API. It runs through a 3 step process: First, you create a domain verification request, this creates a request token — you (or your users) add that DNS record to their DNS, then you tell us you completed the challenge, we check it, and assuming its correct, you have the ability to use that domain. ### Install the Freestyle Sandboxes package ### Get your API key Go to the [Freestyle Dashboard](https://admin.freestyle.sh) and get your API key ### Create a Domain Verification Request { console.log("Domain verification request created @ ", result); /* The result looks like: { verificationCode: string; domain: string; } */ }); `, title: "createDomainVerification.js" }} python={{ code: ` import freestyle client = freestyle.Freestyle("YOUR_FREESTYLE_API_KEY") verification_request = client.create_domain_verification_request( domain="yourdomain.com" # this also works for subdomains ) print("Domain verification request created successfully!") print(f"Domain: {verification_request.domain}") print(f"Verification Code: {verification_request.verification_code}") print(f"Id: {verification_request.id}") `, title: "create_domain_verification.py" }} /> You can also view your domain verification request in the [Freestyle Dashboard](https://admin.freestyle.sh) ### Add the record Add the following DNS record to your domain's DNS settings: ```txt Type: TXT Name: _freestyle_custom_hostname.{yourdomain.com} Value: {verificationCode} ``` You can check if its propagated by running the following command: ```bash dig TXT _freestyle_custom_hostname.{yourdomain.com} ``` ### Verify the domain { if (result.domain) { console.log("Domain verified @ ", result.domain); } else { console.log("Domain verification failed", result.message); } }); `, title: "verifyDomain.js" }} python={{ code: ` import freestyle client = freestyle.Freestyle("YOUR_FREESTYLE_API_KEY") domain_verification_result = client.verify_domain("yourdomain.com") # this also works for subdomains if domain_verification_result.domain: print("Domain verified @ ", domain_verification_result.domain) else: print("Domain verification failed", domain_verification_result.message) `, title: "verify_domain.py" }} /> You can see if the domain is verified in the **Domains** section of the [Freestyle Dashboard](https://admin.freestyle.sh) ## Next Steps - [Deploy to a Custom Domain](/web/deploy-to-custom-domain) } # URL: https://docs.freestyle.sh/web/web.md Content: --- title: Deploying a Website description: How to deploy a website you didn't write with Freestyle --- import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock"; import { CodeBlock } from "fumadocs-ui/components/codeblock"; import InstallSandboxes from "../../../src/components/installSandboxes"; import { CodeTabs } from "../../../src/components/code-tabs"; import { Steps, Step } from "fumadocs-ui/components/steps"; import { Tab, Tabs, TabsTrigger, TabsList, TabsContent, } from "fumadocs-ui/components/tabs"; The Freestyle Web API provides a set of tools to build, deploy and manage websites. This guide will go through the fastest path to deploy a sample website on Freestyle using our SDKs. ### Install the Freestyle Sandboxes package First, install the package with your preferred package manager ### Get your API key Go to the [Freestyle Dashboard](https://admin.freestyle.sh) and get your API key ### Deploy the website Create a deploy script in the root of the repo with the following code: { console.log("Deployed website @ ", result.domains); }); `, title: "deploy.js" }} python={{ code: ` import freestyle client = freestyle.Freestyle("YOUR_FREESTYLE_API_KEY") response = client.deploy_web( src=freestyle.DeploymentSource.from_dict( { "kind": "git", "url": "https://github.com/freestyle-sh/freestyle-base-nextjs-shadcn", } ), config=freestyle.FreestyleDeployWebConfiguration( domains=["welcomepython.style.dev"], build=freestyle.DeploymentBuildOptions.from_dict(True), ), ) print( f"Deployed website @ {response.domains}" ) `, title: "deploy.py" }} /> When deploying a website to Freestyle, there are two distinct parameters: - **Source**: This is the code you want to deploy, it can be a Git repository, a set of files, or a link to a tarball. In this example, we're using a public Github repository that contains a Next.js App. - **Configuration**: This is the configuration for the deployment, it can include the domains you want to deploy to, the entrypoint of the application, whether to build the code or deploy it as is, timeout behavior and much more. We set `build`: `true` to automatically detect the framework and build the code. Then run the file to deploy the website: ## Next Steps - If you want to deploy to custom domain, first you need to [verify a domain](/web/domains) - If you want to deploy Expo Apps, check out our [Expo guide](/web/frameworks/expo) - If you want to deploy a Next.js App, check out our [Next.js guide](/web/frameworks/next) - If you need advanced configuration options, check out the [Configuration guide](/web/configuration) } # URL: https://docs.freestyle.sh/code-execution/integrations/gemini.md Content: --- title: Gemini Python SDK description: Code execution for Gemini in Python --- import { Steps, Step } from "fumadocs-ui/components/steps"; import { Callout } from "fumadocs-ui/components/callout"; ### Install the required dependencies ```bash pip install google-genai freestyle ``` Get your Freestyle API Key from the [Freestyle Dashboard](https://admin.freestyle.sh) ### Set up the Code Executor The simplest code executor looks like this: ```python import os import freestyle.gemini definition, runner = freestyle.gemini.execute_tool( os.environ.get("FREESTYLE_API_KEY"), ) ``` #### Adding Node Modules When you want your AI code execution to have access to specific node modules, you can pass them in through the configuration parameter: ```python import os import freestyle.gemini import freestyle definition, runner = freestyle.gemini.execute_tool( os.environ.get("FREESTYLE_API_KEY"), freestyle.FreestyleExecuteScriptParamsConfiguration( nodeModules={"mathjs": "14.3.1"} ), ) ``` #### Adding Environment Variables You can also pass in environment variables that your AI code execution will have access to: ```python import os import freestyle.gemini import freestyle definition, runner = freestyle.gemini.execute_tool( os.environ.get("FREESTYLE_API_KEY"), freestyle.FreestyleExecuteScriptParamsConfiguration( envVars={"RESEND_API_KEY": os.environ.get("RESEND_API_KEY")}, nodeModules={"resend": "4.1.2"} ), ) ``` #### Other Configuration Options - **timeout**: The maximum time in seconds that the code execution is allowed to run. - **networkPermissions**: A list of URLs that the code execution is allowed to access. - **peerDependencyResolution**: Configure if peer dependencies should be resolved — **do not use this unless you know what you are doing**. ## Set up the Gemini Python SDK ```python import google.genai as genai from google.genai import types import os import freestyle.gemini client = genai.Client(api_key=os.environ.get("GENERATIVEAI_API_KEY")) definition, runner = freestyle.gemini.execute_tool( os.environ.get("FREESTYLE_API_KEY"), ) chat = client.chats.create( model="gemini-2.0-flash", config=types.GenerateContentConfigDict( tools=[definition], ), history=[], ) response = chat.send_message( "What is the sum of every number from 50 to 65 divided by 17" ).candidates[0] tool_result = runner(response.content) print("Answer: ", tool_result) ``` The `definition` and `runner` variables are from the code executor setup. The `runner` is a function that takes in a Gemini model response and returns the output of the code execution if there is one. `Runner` is made to be called on every response from the Gemini model, if there is no code execution then it returns `None` and does nothing. } # URL: https://docs.freestyle.sh/code-execution/integrations/langgraph-js.md Content: --- title: Langgraph JS description: Code execution for Langgraph in JavaScript --- import { Steps, Step } from "fumadocs-ui/components/steps"; import { Callout } from "fumadocs-ui/components/callout"; ### Install the required dependencies ```bash npm install freestyle-sandboxes @langchain/langgraph @langchain/core @langchain/openai ``` This walkthrough uses `@langgraph/openai`, however these exact steps should work for any of the langgraph providers like `@langgraph/anthropic` Get your Freestyle API Key from the [Freestyle Dashboard](https://admin.freestyle.sh) ### Set up the Code Executor ```ts import { executeTool } from "freestyle-sandboxes/langgraph"; const codeExecutor = executeTool({ apiKey: process.env.FREESTYLE_API_KEY!, }); ``` You can also pass in any **nodeModules**, **environment variables**, **timeout**, or **network restrictions** you need. ```ts import { executeTool } from "freestyle-sandboxes/langgraph"; const codeExecutor = executeTool({ apiKey: process.env.FREESTYLE_API_KEY!, nodeModules: { mathjs: "14.3.1", }, envVars: { MY_SUPER_SECRET_KEY: "1234567890", }, }); ``` ### Set up the Langgraph SDK Agent ```ts const model = new ChatOpenAI({ model: "gpt-4o" }); const agent = createReactAgent({ llm: model, tools: [codeExecutor], }); ``` ### Invoke the Agent ```ts const result = await agent.invoke({ messages: [ { role: "user", content: "What is the factorial of 13 divided by 55^2" }, ], }); console.log(result.messages.at(-1)?.content); ``` 🚀 Your AI can now execute code } # URL: https://docs.freestyle.sh/code-execution/integrations/langgraph-py.md Content: --- title: Langgraph Python description: Code execution for Langgraph Python SDK --- import { Steps, Step } from "fumadocs-ui/components/steps"; ### Install the required dependencies ```bash pip install langgraph langchain_anthropic freestyle ``` Get your Freestyle API Key from the [Freestyle Dashboard](https://admin.freestyle.sh) ### Set up the Code Executor The simplest code executor looks like this ```python import os from freestyle.langgraph import execute_tool execute_tool( os.environ.get("FREESTYLE_API_KEY"), ) ``` If you want to add node modules & environment variables, you can do so like this ```python import os from freestyle.langgraph import execute_tool import freestyle (definition, runner) = freestyle.openai.execute_tool( os.environ.get("FREESTYLE_API_KEY"), freestyle.FreestyleExecuteScriptParamsConfiguration( nodeModules={"mathjs": "14.3.1"}, envVars={"SUPER_SECRET_KEY": os.environ.get("SUPER_SECRET_KEY")}, ), ) ``` ### Add it to your Agent ```python from langchain_anthropic import ChatAnthropic llm = ChatAnthropic(model="claude-3-5-sonnet-20240620") llm_with_tools = llm.bind_tools([execute_tool]) ``` } # URL: https://docs.freestyle.sh/code-execution/integrations/mastra.md Content: --- title: Mastra AI SDK description: Code execution for Mastra AI SDK Agents --- import { Steps, Step } from "fumadocs-ui/components/steps"; ### Install the required dependencies ```bash npm install @mastra/core freestyle-sandboxes ``` Get your Freestyle API Key from the [Freestyle Dashboard](https://admin.freestyle.sh) ### Set up the Code Executor The simplest code executor looks like this: ```typescript import { executeTool } from 'freestyle-sandboxes/mastra'; const codeExecutor = executeTool({ apiKey: process.env.FREESTYLE_API_KEY!, }); ``` You can also pass in any **nodeModules**, **environment variables**, **timeout**, or **network restrictions** you need. Here's an example with access to the `resend` and `octokit` node modules, and environment variables for `RESEND_API_KEY` and `GITHUB_PERSONAL_ACCESS_TOKEN` ```ts import { executeTool } from "freestyle-sandboxes/mastra"; const codeExecutor = executeTool({ apiKey: process.env.FREESTYLE_API_KEY!, nodeModules: { resend: "4.0.1", octokit: "4.1.0", }, envVars: { RESEND_API_KEY: process.env.RESEND_API_KEY!, GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN!, }, }); ``` ### Set up the Mastra AI SDK Agent ```ts import { createMastra } from "@mastra/core"; const mastra = new Mastra(); const modelConfig: ModelConfig = { provider: "OPEN_AI", name: "gpt-4", }; const llm = mastra.LLM(modelConfig); const response = await llm.generate( "Calculate the sum of every number between 13 and 19 divided by the sum of every number between 8 and 13", { tools: { codeExecutor, }, } ); ``` } # URL: https://docs.freestyle.sh/code-execution/integrations/openai.md Content: --- title: OpenAI Python SDK description: Code execution for OpenAI in Python --- import { Steps, Step } from "fumadocs-ui/components/steps"; import { Callout } from "fumadocs-ui/components/callout"; ### Install the required dependencies ```bash pip install openai freestyle ``` Get your Freestyle API Key from the [Freestyle Dashboard](https://admin.freestyle.sh) ### Set up the Code Executor The simplest code executor looks like this: ```python import os from freestyle.openai import execute_tool (definition, runner) = freestyle.openai.execute_tool( os.environ.get("FREESTYLE_API_KEY"), ) ``` #### Adding Node Modules When you want your AI code execution to have access to specific node modules, you can pass them in through the configuration parameter: ```python import os from freestyle.openai import execute_tool import freestyle (definition, runner) = freestyle.openai.execute_tool( os.environ.get("FREESTYLE_API_KEY"), freestyle.FreestyleExecuteScriptParamsConfiguration( nodeModules={"mathjs": "14.3.1"} ), ) ``` #### Adding Environment Variables You can also pass in environment variables that your AI code execution will have access to: ```python import os from freestyle.openai import execute_tool import freestyle (definition, runner) = freestyle.openai.execute_tool( os.environ.get("FREESTYLE_API_KEY"), freestyle.FreestyleExecuteScriptParamsConfiguration( envVars={"RESEND_API_KEY": os.environ.get("RESEND_API_KEY")} nodeModules={"resend":"4.1.2"} ), ) ``` #### Other Configuration Options - **timeout**: The maximum time in seconds that the code execution is allowed to run. - **networkPermissions**: A list of URLs that the code execution is allowed to access. - **peerDependencyResolution**: Configure if peer dependencies should be resolved — **do not use this unless you know what you are doing**. ## Set up the OpenAI Python SDK ```python import openai client = openai.OpenAI( api_key=os.environ.get("OPENAI_API_KEY"), ) query = "What is the sum of every number from 50 to 65 divided by 17" messages = [{"role": "user", "content": query}] res = client.chat.completions.create( model="gpt-4-turbo", messages=messages, tools=[definition] ) result = runner(res.choices[0].message) ``` The `definition` and `runner` variables are from the code executor setup. The `definition` is an OpenAI compatible tool definition, and the `runner` is a function that takes in an OpenAI Compatible Model response and returns the output of the code execution if there is one. `Runner` is made to be called on every response from the OpenAI model, if there is no code execution then it returns `None` and does nothing. } # URL: https://docs.freestyle.sh/code-execution/integrations/pipecat.md Content: --- title: PipeCat description: Code execution for PipeCat Agents --- } # URL: https://docs.freestyle.sh/code-execution/integrations/vercel.md Content: --- title: Vercel AI SDK description: Code execution for Vercel AI SDK Agents --- import { Steps, Step } from "fumadocs-ui/components/steps"; ### Install the required dependencies ```bash npm install ai @ai-sdk/openai freestyle-sandboxes ``` Get your Freestyle API Key from the [Freestyle Dashboard](https://admin.freestyle.sh) ### Set up the Code Executor The simplest code executor looks like this: ```ts import { executeTool } from "freestyle-sandboxes/ai"; const codeExecutor = executeTool({ apiKey: process.env.FREESTYLE_API_KEY!, }); ``` You can also pass in any **nodeModules**, **environment variables**, **timeout**, or **network restrictions** you need. Here's an example with access to the `resend` and `octokit` node modules, and environment variables for `RESEND_API_KEY` and `GITHUB_PERSONAL_ACCESS_TOKEN`: ```ts import { executeTool } from "freestyle-sandboxes/ai"; const codeExecutor = executeTool({ apiKey: process.env.FREESTYLE_API_KEY!, nodeModules: { resend: "4.0.1", octokit: "4.1.0", }, envVars: { RESEND_API_KEY: process.env.RESEND_API_KEY!, GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN!, }, }); ``` ### Set up the Vercel AI SDK Agent ```ts const openai = createOpenAI({ compatibility: "strict", apiKey: process.env.OPENAI_API_KEY!, }); const { text, steps } = await generateText({ model: openai("gpt-4o"), tools: { codeExecutor, }, maxSteps: 5, maxRetries: 0, prompt: "Ask the AI to do whatever you want", }); ``` ### Notes - You can actually give it multiple code executors with different node modules, different environment variables, and different network restrictions. - When the AI writes invalid code, or the code gets errors they will be returned in the response. If the AI has steps left, it will try to fix the code and continue. - If you add a node module we haven't seen before, the first time you run it it will take longer because we have to install the node module. After that, it will be cached and return to normal speed. } # URL: https://docs.freestyle.sh/guides/app-builder.md Content: --- title: Building an AI App Builder description: How to make an AI App Builder on Freestyle --- import { Mermaid } from "../../../../src/components/mermaid"; Freestyle is **the cloud for AI App Builders**. We provide all the tools you need to build, preview, deploy and manage the code your AI writes. This guide shows you how to build with Freestyle, how we think about our tools, and how to get the most out of them. ## Architecture ### Managing the Code with Git The life blood of every AI App is the code that powers it. To make the most of it, we provide a [Git](/git) API for creating and managing repositories. We recommend using Git to manage the code your AI writes because: - **Version Control**: You get a free version history, along with reverting, branching, and merging. - **Collaboration**: You can have multiple agents working on different prototypes and merging them together. - **Debugability**: Your AI's process is tracked over time, and you can clone any of its prototypes locally anytime. - **Portability**: You can sync your repo's to Github, or give your users the ability to clone them locally and work alongside your AI. - **Integration**: By working through Git, we can provide live previews based on the current versions of the code, along with automatic deployments — the same way you get with the Continuous Integration and Continuous Deployment (CI/CD) tools you're used to. ### Developing with Dev Servers As your AI writes code, it needs some form of development server to run it on. This development server has jobs like linting the code, running it (or running tests), and serving it to the browser to preview it for your users. We provide a [Dev Servers](/dev-servers/dev-servers) API for creating and managing these servers. The Dev Server API is **not a generic container API**, it is specialized for lifecycle management of **JavaScript/TypeScript** apps. Instead of making you manage the lifecycle, it runs through Git and is synced to the latest version of a given git repository. The Dev Server automatically keeps your dev server healthy, routes traffic to a given url to provide a live preview, manages npm installs, and shuts down when the code is not being used. It's also controllable via both an HTTP API and an MCP service that lets you or your agents control it. This api is not the most powerful container API in the market — this is by design. We believe AI App Builders should be able to focus on building apps, not managing container lifecycle and health. The Dev Server API is designed to take this off your plate. It is designed to be extensible, but if you are looking for a generic container API, this is not it. ### Deploying Previews Once your AI has written code, it needs to be deployed to a live server. Dev Server's are slow, expensive to run and non-scalable. We provide a [Deployments](/web/web) API for deploying your code to our production serverless infrastructure. This API is extremely customizable, you can build that app yourself, have it detect the framework and build for you, or configure your own build, then you can add any number of domains, environment variables, advanced security features like network permissions and more. It also manages the related DNS, routing and TLS Certificates without you needing to do anything. Any Freestyle user can deploy their code to any `*.style.dev` domain, and can also deploy to their own custom domains. AI App Builders should have their own `*.yourapp.com` domain that you use to deploy initial production versions of your app to. You can set this up with the [following guide](/web/domains) and point the domain at us following the [DNS Instructions](/web/deploy-to-custom-domain). This **should not be a subdomain of any domains you use** for security reasons. The simplest way to deploy if you're using [Git](/git) is to set the deployment source to your git repository itself. This way, we'll automatically pull the latest version of your code, build it and deploy it. You can do this yourself, or you can set up a Git Trigger to automatically deploy your code whenever you push to a given branch. This is the same way you would do it with any other CI/CD tool. ### Production Deployments Once your users see the live preview of the app, they'll want to deploy it to their own domains. This is where the [domains](/web/domains) API comes in. This API allows you to deploy your app to any custom domain. Domains on Freestyle are completely decoupled from deployments, allowing you to attach/detach them at will. In order to do this, you should create an API that takes your users through the [same verification process for managing your own domain](/web/domains) you went through to set up your own domain. Then tell them to point their domain at us following the [DNS Instructions](/web/deploy-to-custom-domain). Once they do that, you can deploy their app to their domain using the [Deployments](/web/web) API. ### All together All these together make up an AI App Builder built on Freestyle. Our goal is to take the pain of infrastructure off of building an AI App Builder to let you focus on everything else. If this architecture is compelling to you, check out our [example repository here](https://github.com/freestyle-sh/adorable). ## Guide This guide will take you through the process of building an AI App Builder on Freestyle. While we will use an opinionated tech stack, we've intentionally segmented the guide into different sections so you can take the parts you like and leave the rest. The goal is to give you a starting point for building your own AI App Builder. ### Tech Stack - [TypeScript](https://www.typescriptlang.org/) - This AI App Builder will be built 100% in Full Stack TypeScript. - [NextJS](https://nextjs.org/) - [Vercel AI SDK](https://sdk.vercel.ai) - We will use the Vercel AI SDK to handle our Chat UI and message streaming. - [Freestyle](https://freestyle.sh/) - We will use Freestyle to manage our Git Repositories, Dev Servers and Deployments. - [Anthropic](https://www.anthropic.com/) - We will use Anthropic's Claude to power our AI. You can use any LLM you want, but we like Claude. ### Setup #### Setting up the Project ```bash npx create-next-app@latest --ts --tailwind --yes freestyle-ai-app-builder cd freestyle-ai-app-builder npm install freestyle-sandboxes ai @ai-sdk/react @ai-sdk/anthropic @modelcontextprotocol/sdk ``` #### Environment - Create a `.env` file in the root of your project with the following contents: ``` FREESTYLE_API_KEY=your_freestyle_api_key ANTHROPIC_API_KEY=your_anthropic_api_key ``` - You can get your Freestyle API key from the [Freestyle Dashboard](https://admin.freestyle.sh). - You can get your Anthropic API key from the [Anthropic Dashboard](https://console.anthropic.com). ### Mechanics of the Chat The following sections are the pieces that we will later put together to make the AI App Builder chat. #### Setting up a Git Repository In order to start developing your AI App Builder, you'll need a Git repo to build the app in. You'll want one Git repo for every chat in your AI App Builder, this way the chat has a place to manage its code. However you store your chats, you should include a `repoId` on the chat object to refer to the code linked to it. ```ts title="setup.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; const freestyle = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY, }); const { repoId } = await freestyle.createGitRepository({ name: "Test Repository", // This will make it easy for us to clone the repo during testing. public: true, source: { url: "https://github.com/freestyle-sh/freestyle-next", //[!code highlight] can be any public git repo, or any git repo you own on Freestyle type: "git", }, }); ``` We have a series of prebuild templates for you to base your AI Apps on | Setup | Url | | ----------------- | ------------------------------------------------------------------------ | | NextJS | https://github.com/freestyle-sh/freestyle-next | | Vite + Tailwind | https://github.com/freestyle-sh/freestyle-base-vite-react-typescript-swc | | Expo (for mobile) | https://github.com/freestyle-sh/freestyle-expo | We recommend forking one of them to make your own custom one. Your template should include everything custom you might want your AI to use. For example, if you want it to process payments, you should install the SDK into your template repo and create the setup files. #### Running a the Dev Server Dev Servers exist as short lived previews and dev environments to work with your Freestyle Git Repositories. To use one, provision it like: ```ts title="dev.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; export const freestyle = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); const { ephemeralUrl, // The URL of the preview of the dev server mcpEphemeralUrl, // The URL of the mcp service for the dev server } = await freestyle.requestDevServer({ repoId: repoId, // [!code highlight] the repoId from the previous step }); ``` The `ephemeralUrl` is a URL that you can use to preview the dev server. The `mcpEphemeralUrl` is a URL that you can use to connect to the mcp service for the dev server. While we also offer a Rest API to control the dev server, we recommend using the mcp service to start, and using the Rest API when you have specific use cases that the MCP can't handle. #### Integrating with AI In order to integrate with AI, we'll use the [Vercel AI SDK](https://sdk.vercel.ai) and [Anthropic Claude](https://www.anthropic.com/) to create a simple ReAct agent that works with the Dev Server for the repository. #### Setup the AI + MCP In order to connect the AI to the Dev Server, we'll first instantiate the model and connect the MCP client to the dev server. ```ts title="mcp.ts" import { anthropic } from "@ai-sdk/anthropic"; import { experimental_createMCPClient as createMCPClient } from "ai"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; export const ANTHROPIC_MODEL = anthropic("claude-3-7-sonnet-20250219"); const devServerMcp = await createMCPClient({ transport: new StreamableHTTPClientTransport(new URL(mcpUrl)), // [!code highlight] mcpUrl is the url of the dev server }); const tools = await devServerMcp.getTools(); ``` #### Run the AI Then, we can use the `streamText` function to run the AI. This function takes a model, a prompt, and a set of tools to use. We set `steps` to 100 to give the AI lots of time to iterate, and `toolCallStreaming` to `true` to get the AI to call the tools as it goes. This is important, as it lets you see as the AI is writing a file, instead of waiting for it to be done. ```ts title="run.ts" import { streamText } from "@ai-sdk/react"; streamText({ model: ANTHROPIC_MODEL, // [!code highlight] the model from the previous step maxSteps: 100, tools: tools, // [!code highlight] the tools from the previous step toolCallStreaming: true, messages: [ { role: "system", content: ` You are an AI App Builder. The existing app is in the /template directory. Please edit the app how the user wants and commit the changes incrementally. `, }, { role: "user", content: `Make me Tic Tac Toe`, // [!code ++] Put your prompt here }, ], }); ``` Now, if you visit the `url` of the dev server, you should see the changes to your app live as the AI makes them. ### Putting the pieces together Now that we have all the pieces in place, we can create the two functions that manage the chat: 1. `createChat` - This function will create a new git repository for the chat and request a dev server for it. 2. `respond` - This function will take the user messages and run the AI with the dev server to build the app. ```ts title="lib/create-chat.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; const freestyle = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); export async function createChat() { const { repoId } = await freestyle.createGitRepository({ name: "Test Repository", public: true, source: { url: "https://github.com/freestyle-sh/freestyle-next", // [!code highlight] replace this with your own template repo type: "git", }, }); const { ephemeralUrl, mcpEphemeralUrl } = await freestyle.requestDevServer({ repoId: repoId, }); return { repoId, ephemeralUrl, }; } ``` We'll put the `respond` function at `/api/chat/route.ts`, because this is the default route the Vercel AI SDK uses to get responses in chats built on it. ```ts title="app/api/chat/route.ts" export async function POST(req: Request) { const repoId = req.headers.get("Repo-Id"); const { messages } = await req.json(); const { ephemeralUrl, mcpEphemeralUrl } = await freestyle.requestDevServer({ repoId: repoId, }); const devServerMcp = await createMCPClient({ transport: new StreamableHTTPClientTransport(new URL(mcpEphemeralUrl)), }); const tools = await devServerMcp.getTools(); const response = await streamText({ model: ANTHROPIC_MODEL, maxSteps: 100, tools: tools, toolCallStreaming: true, messages: [ { role: "system", content: ` You are an AI App Builder. The existing app is in the /template directory. Please edit the app how the user wants and commit the changes incrementally. `, }, ...messages, ], }); result.consumeStream(); // keep going even if the client disconnects return result.toDataStreamResponse(); } ``` #### Making the Homepage UI To start, we can make a homepage with a `Create App` button. This will create a new chat and redirect the user to it. ```tsx title="app/page.tsx" import { useState } from "react"; import { useRouter } from "next/navigation"; import { createChat } from "../lib/create-chat"; export default function Home() { const router = useRouter(); const [loading, setLoading] = useState(false); async function handleClick() { setLoading(true); const { repoId } = await createChat(); router.push(`/app/${repoId}`); } return (

AI App Builder

); } ``` #### Making the Chat UI Then, for each chat, we can create a page that shows the chat UI and the dev server preview. First we need to create a server action to allow our frontend to request a dev server and get it's status. We'll pass this server action to the `FreestyleDevServer` component, which will continuously ensure the dev server is healthy. ```ts title="lib/server-actions.ts" "use server"; export async function requestDevServer({ repoId }: { repoId: string }) { return await freestyle.requestDevServer({ repoId }); } ``` ```tsx title="app/apps/[repoId]/page.tsx" import { useChat } from "@ai-sdk/react"; import { FreestyleDevServer } from "freestyle-sandboxes/react/dev-server"; import { requestDevServer } from "@/lib/server-actions"; export default async function Chat( { params }: Promise<{ repoId: string }>; ) { const { repoId } = await params; // [!code highlight] the repoId from the URL const { messages, input, handleInputChange, handleSubmit } = useChat({ headers: { "Repo-Id": id, // [!code highlight] the repoId from the URL, so the API knows which chat to use } }); return (
// This is where the chat UI will be // [!code highlight] {messages.map((message) => (
{message.role === "user" ? "User: " : "AI: "} {message.parts.map((part, i) => { switch (part.type) { case "text": return
{part.text}
; default: return (
{JSON.stringify(part)}
); } })}
))}
// This is where the dev server will be previewed // [!code highlight] ;
); } ``` The builder part of the AI App Builder is now complete 🎉. You now have an AI connected to your dev server, that is able to iterate on the app, show a live preview to the user, and commit its code as it goes. ### Adding Deploys Now that the AI can build apps, your users will want to deploy and share them. This is where the [Deployments](/web/web) API comes in. In order to make deploys easy, we can deploy your code by referencing the git repository. This will automatically get the latest version of your code, build it, and deploy it to the domain of your choice. Using this technique also helps you with debugging. As you grow, **some of your deploys will fail**. This is natural, AI writes lots of bad code. By deploying through git, you're able to know the exact version of the code that was built to be deployed, and see what got messed up. ```ts title="deploy.ts" "use server"; import { FreestyleSandboxes } from "freestyle-sandboxes"; const freestyle = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY, }); export async function deployAction( repoId: string, // [!code highlight] the repoId for your app domains: string[] // [!code highlight] the domains you want to deploy to ) { const { domains } = await freestyle.deployWeb( { kind: "git", repoUrl: `https://git.freestyle.sh/${repoId}`, }, { domains, envVars: { // ... any environment variables you want to set }, } ); } ``` Now, your AI App Builder has deploys 🎉🎉🎉, stick a button on the frontend that calls this server action and you're good to go. ### Adding Domains For custom domains, you'll want to follow [this guide](/web/domains) to set up a verification process for your users to verify their domains. This allows you to deploy to their domains. ## Closing This guide shows you the simplest possible way to get going with an AI App Builder. It should serve as a good starting point and reference for making your own. The code in it is intentionally over-simplified, and will need to be modified to make something production ready. For a complete template, check out the [Adorable](https://github.com/freestyle-sh/adorable) repository. This repository not only comes with everything we went over here, but also chunking, a better Chat UI, and a bunch of other features that make it a great starting point for your own AI App Builder. If you're building a Mobile App Builder, check out the [Mobile App Builder](/guides/app-builder/mobile) guide. It goes over how to use Expo and Freestyle together to build a mobile app builder. } # URL: https://docs.freestyle.sh/guides/app-builder/mobile.md Content: --- title: Building an AI Mobile App Builder description: Shipping AI driven mobile apps with Freestyle --- Freestyle is **the platform for building AI Mobile App Builders**. There are many AI Mobile App Builder companies already on the platform, and we have specialized utilities to help you. ## Pre-requisites This guide is a follow up to the generic [AI App Builder](./) guide. Everything about managing code, dev servers and deployments for web servers applies for mobile app builders too. ## Intro This guide goes over the utilities we have for AI Mobile App Builders and best practices we've seen for building them. We recommend using [Expo](https://expo.dev/) as the base for your mobile app. Expo is a framework and platform for universal React applications. It works well for AI App Builders because: - Expo is a React based framework, AI is great at React. - Expo has hot reload, which makes iteration fast. - Expo has web support for both previews and production, this makes previewing and debugging the easy, it also makes sharing the app with your users easy. - Freestyle has thousands of Expo Apps running on it, so we know how to make it work well. ## Dev Servers When using [Dev Servers](/dev-servers/dev-servers), your users can view the web preview through the `FreestyleDevServer`. For viewing on mobile devices, you can use the `ephemeralUrl` in Expo Go Via a QR Code, or via any [Expo Developer Build Client](https://docs.expo.dev/develop/development-builds/create-a-build/). However, these URL's are `ephemeral`, so we recommend proxying them through another router server that you can control, to define a permanent URL for the Expo Client to pull from. ## Deploy Freestyle offers an Expo bundling system that makes our builds compatible with the Expo Updates standard, and visible on a website. When enabled, if you deploy to `someapp.style.dev` (or any domain), your users will be able to view the website at that domain, and if an Expo Client is pointed at that domain it will use it as the bundle source. To enable this, you should build your app on us with **Freestyle Auto Building**. You can enable this by adding `build: true` to your deployment configurations. ## Notes - Freestyle Expo Auto Building currently **does not support Android or code signing**, we're working on it. - Freestyle Expo Auto Builds currently support **static or single** web apps, we're working on supporting server mode. - We recommend using Expo + Hono/some external server rather than Expo + Expo API Routes — Expo API Routes seem to have shockingly bad performance and not work with hot reloading. This can be deployed separately from the app and used by it. } # URL: https://docs.freestyle.sh/web/frameworks/expo.md Content: --- title: Deploying Expo Projects description: How to deploy an Expo Project to Freestyle --- import { Callout } from "fumadocs-ui/components/callout"; import { Tabs, Tab } from "fumadocs-ui/components/tabs"; [Expo](https://expo.dev) is a framework for building cross-platform mobile apps. While primarily used to create iOS and Android Apps, these apps can also be compiled to static websites and hosted on Freestyle. This guide will walk you through the process of configuring and deploying an Expo app to Freestyle. This guide shows you how to deploy a `Static` or `SPA` Expo app. If you want to deploy a server-side rendered Expo app, the general same steps should apply, except for the server implementation. ## Setup If you don't have a pre-existing Expo app, run the command below to initialize it. It'll ask you where to put the app, for the purposes of this guide I'll be putting it in `my-app` ```bash npx create-expo-app@latest ``` Once you've created the app, install the dependencies necessary for shipping it to website. `react-dom` is necessary for React to render on the web, `react-native-web` is for React Native to compile its components to html, and `@expo/metro-runtime` is for Expo to be able to compile it's structures to web. ```bash npx expo install react-dom react-native-web @expo/metro-runtime ``` Now you have an expo app ready to be deployed. ## Deploying the App Get your API key from the [Freestyle dashboard](https://admin.freestyle.sh), and create a .env file with the following content: ``` FREESTYLE_API_KEY=your-api-key ``` Install the Freestyle Sandboxes Client ```bash npm i freestyle-sandboxes ``` Now you need a script to deploy the app. This is an example script that deploys the app. It first creates a `FreestyleSandboxes` client with your Freestyle API Key. then it calls `.deployWeb` with two options: `source` + `configuration`. `source` is an object of files to their file contents, you can write a custom function that takes your directory and prepares it for uploading, however we provide a series of utilities making it easy. `configuration` comes with anything other than the code you want to configure, that might be the domains, the entrypoint, the environment variables, or the network permissions. ```ts title="deploy.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils"; // Create a sandboxes client const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { await sandboxes.deployWeb(prepareDirForDeploymentSync("."), { domains: ["example.style.dev"], build: true, // This automatically detects the framework and configures/builds for you // [!code highlight] }); } deploy(); ``` Finally, to make the deploy happen, run it ```bash bun run deploy.ts ``` ## Preparing the App for Deployment We run the command below to create a production build of your app for web. ```bash npx expo export --platform web ``` Now, we slightly modify the output so that it works with freestyle module resolution by changing the dists `node_modules` folder to `modules`. Freestyle doesn't support uploading `node_modules` as we have a special carveout for caching them, however expo build outputs a directory called `dist/assets/node_modules` which we can rename to `dist/assets/modules` so it will work. This is an example way to do it ```bash find dist -type f -name "*.js" -exec sed -i '' 's/node_modules/modules/g' {} + mv dist/assets/node_modules dist/assets/modules ``` ## Creating a Server By default, [Expo](https://expo.dev) outputs only static files. In order to serve them on Freestyle, we create a simple server using [Hono](https://hono.dev/), a lightweight web framework for Deno. This server will serve the static files generated by Expo. First install `hono` ```bash npm i hono ``` Then you can use the following code to serve the files. ```ts title="main.ts" import { Hono } from "hono"; import { serveStatic } from "hono/deno"; const app = new Hono(); app.use("*", serveStatic({ root: "./dist" })); // fallback to index.html app.get("*", serveStatic({ path: "./dist/index.html" })); Deno.serve(app.fetch); ``` This setup works for both the expo output configurations of `"output": "single"` and `"output": "static"`, for `"output": "server"`, use the server entrypoint instead of creating a custom one. If you want to run expo in server mode, with support for [Expo API Routes](https://docs.expo.dev/router/reference/api-routes/), you can set the output to `server` in your `app.json` file. ```json { "expo": { "web": { "bundler": "metro", "output": "server" // [!code ++] } } } ``` Then, you'll need to create a simple entrypoint to run the server. Freestyle provides first class support for Expo server mode: ```ts title="main.ts" import { freestyleExpoServer } from "freestyle-sandboxes/expo"; freestyleExpoServer(); ``` By default, this expects the output to be in the `dist` folder, but you can override where the server and client files it pulls from are by passing the `options` object to `freestyleExpoServer`. ## Deploying the App ### CLI Install the Freestyle CLI. ```bash npm i freestyle-sh ``` Now deploy it ```bash npx freestyle deploy --domain some.style.dev --web main.ts ``` ### API You can also deploy the app using the API. Get your API key from the [Freestyle dashboard](https://admin.freestyle.sh), and create a .env file with the following content: ``` FREESTYLE_API_KEY=your-api-key ``` Install the Freestyle Sandboxes Client ```bash npm i freestyle-sandboxes ``` Now you need a script to deploy the app. This is an example script that deploys the app. It first creates a `FreestyleSandboxes` client with your Freestyle API Key. then it calls `.deployWeb` with two options: `source` + `configuration`. `source` is an object of files to their file contents, you can write a custom function that takes your directory and prepares it for uploading, however we provide a series of utilities making it easy. `configuration` comes with anything other than the code you want to configure, that might be the domains, the entrypoint, the environment variables, or the network permissions. ```ts title="deploy.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils"; // Create a sandboxes client const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { await sandboxes.deployWeb(prepareDirForDeploymentSync("."), { entrypoint: "main.ts", // put whatever domains you want here domains: ["example.style.dev"], }); } deploy(); ``` Finally, to make the deploy happen, run it ```bash bun run deploy.ts ``` ### Next Steps Now that you can deploy Expo apps to Freestyle, you'll probably want to deploy them to [custom domains](/web/domains). To deploy to your own custom domains, you can use the UI in the [Freestyle dashboard](https://admin.freestyle.sh), and to start building self serve to deploy to your users domains you should check out [this guide](/web/domains) } # URL: https://docs.freestyle.sh/web/frameworks/next.md Content: --- title: Deploying NextJS Projects description: How to deploy an NextJS Project to Freestyle --- import { Callout } from "fumadocs-ui/components/callout"; import { Tabs, Tab } from "fumadocs-ui/components/tabs"; [NextJS](https://nextjs.org) is the most popular React Framework for building web applications. We support NextJS with a small bit of configuration. ## Set NextJS to `Standalone` mode NextJS defaults to a `serverless` bundle we do not support. To make NextJS output a valid NodeJS App, you need to set the `output` to `standalone`. We also don't support binaries, and therefore don't support Sharp (NextJS's image optimization library), so we disable the image optimization features of NextJS. ```js title='next.config.mjs' import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "standalone", // [!code highlight] images: { unoptimized: true, // [!code highlight] }, }; export default nextConfig; ``` ## Deploying The Project ## Prepare a Production Build First, you need to build your NextJS project. You can do this by running the following command: ```bash npm run build ``` This guide will work with `bun`, `yarn`, `pnpm` or `npm`. However, it relies on your lockfile, so make sure to copy the correct lockfile for the package manager you are using. Then, you need to copy the package lock, public files, and static files in: ```bash cp -r public .next/standalone/public cp -r .next/static .next/standalone/.next/static # This is for use with npm, replace with any lockfile you want. cp package-lock.json .next/standalone/package-lock.json ``` ## Deploy to Freestyle ### Via the CLI First, you need to install the Freestyle CLI: ```bash npm i freestyle-sh ``` Then, you can deploy your project by running: ```bash cd .next/standalone npx freestyle deploy --web server.js --domain something.style.dev ``` ### Via the SDK First, install the Freestyle Sandboxes SDK: ```bash npm i freestyle-sandboxes ``` Then, you can deploy your project by running: ```js title='deploy.js' import { FreestyleSandboxes } from "freestyle-sandboxes"; import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils"; // Create a sandboxes client const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { await sandboxes.deployWeb(prepareDirForDeploymentSync(".next/standalone"), { entrypoint: "server.js", // put whatever domains you want here domains: ["example.style.dev"], }); } deploy(); ``` ## Build on Freestyle You can deploy NextJS projects and have them built on Freestyle by sending us the files, and enabling `"build": true` in the deploy options. ```ts title="deploy.js" import { FreestyleSandboxes } from "freestyle-sandboxes"; import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils"; // Create a sandboxes client const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { await sandboxes.deployWeb(prepareDirForDeploymentSync("."), { // put whatever domains you want here domains: ["example.style.dev"], build: true, // This automatically detects the framework and configures/builds for you }); } deploy(); ``` NextJS must be in `standalone` mode for this to work. ### Next Steps Now that you can deploy NextJS Apps to Freestyle, you'll probably want to deploy them to [custom domains](/web/domains). To deploy to your own custom domains, you can use the UI in the [Freestyle dashboard](https://admin.freestyle.sh), and to start building self serve to deploy to your users domains you should check out [this guide](/web/domains) } # URL: https://docs.freestyle.sh/web/frameworks/static.md Content: --- title: Deploying Static Assets description: How to deploy a static bundle to Freestyle --- import { Callout } from "fumadocs-ui/components/callout"; import { Tabs, Tab } from "fumadocs-ui/components/tabs"; import { CodeBlock } from "fumadocs-ui/components/codeblock"; Deploying static assets can be useful for hosting websites, or static files in general. Freestyle lets you host them with all our support for custom domains, certificates, and analytics. ## Deploying Static Assets All deploys on Freestyle are servers, so to host the static assets you need to create a simple server that serves them. You can do this with any server you like, but we recommend [hono](https://hono.dev/) for its simplicity and speed. To set it up, first, you need to install the hono package: ```bash npm init -y # Only necessary if you don't have a package.json in the root directory already # [!code highlight] npm install hono ``` Then, you can create a simple server that serves the static assets. Here is a simple example that serves the files in a `static` folder, and falls back to `static/index.html` for any requests that don't match a file: ```ts title="main.ts" import { Hono } from "hono"; import { serveStatic } from "hono/deno"; const app = new Hono(); app.use("*", serveStatic({ root: "./static" })); // fallback to index.html app.get("*", serveStatic({ path: "./static/index.html" })); Deno.serve(app.fetch); ``` ## Deploying to Freestyle ### Via the SDK First, you can get your API Key from the [Freestyle Dashboard](https://admin.freestyle.sh). Then, you need to install the Freestyle SDK: ```bash npm i freestyle-sandboxes ``` Then you can create an instance of the client in your code: ```ts title="deploy.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); ``` Then, you can deploy your app with the following code: ```ts title="deploy.ts" import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils"; import { FreestyleSandboxes } from "freestyle-sandboxes"; const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { await sandboxes.deployWeb(prepareDirForDeploymentSync("."), { entrypoint: "main.ts", // put whatever domains you want here domains: ["example.style.dev"], }); } deploy(); ``` This will upload everything in your current directory to Freestyle, and deploy it as a web server. You can use the `prepareDirForDeploymentSync` function to prepare the directory for deployment. This will copy all the files in the current directory to a temporary directory, and return the path to that directory. You can also refer to the API Reference for constructing the deployment object yourself, or deploying through a Tar or Git Repository. ### Via the CLI First, you need to install the Freestyle CLI: ```bash npm install -g freestyle-cli ``` Then, you need to login to your Freestyle account: ```bash npx freestyle login ``` Then, you can deploy your app with the following command: ```bash npx freestyle deploy --web main.ts --domain anydomain.style.dev ``` This will upload everything in your current directory to Freestyle, and deploy it as a web server. ## Next Steps Now that you can deploy static assets, you'll likely want to set up a custom domain for your server. You can do this by following the [Custom Domains](/web/domains) guide. } # URL: https://docs.freestyle.sh/web/frameworks/vite.md Content: --- title: Deploying Vite Projects description: How to deploy an Vite project to Freestyle --- [Vite](https://vitejs.dev) is a build tool that aims to provide a faster and leaner development experience for modern web projects. We support Vite with a small bit of configuration. ## Setup First, you'll need to create a Vite project. You can do this by running the following command: ```bash npm create vite@latest ``` Follow Vite's instructions relating to framework choices, installing dependencies, and CD'ing into the app. ## Build the App ```bash npm run build ``` This will create a `dist` folder with the production build of your app. This folder contains all the static files needed to run your app. ## Preparing the App for Deployment Freestyle requires a JavaScript or TypeScript entrypoint for your apps. To serve the Vite app, we need to create a server that serves the files in the `dist` folder. The simplest way to do this is to use `hono`, a lightweight web framework that I like: ```typescript title="main.ts" import { Hono } from "hono"; import { serveStatic } from "hono/deno"; const app = new Hono(); app.use("*", serveStatic({ root: "./dist" })); Deno.serve(app.fetch); ``` ## Deploying the App ### CLI Install the Freestyle CLI. ```bash npm i freestyle-sh ``` Now deploy it ```bash npx freestyle deploy --domain some.style.dev --web main.ts ``` ### API You can also deploy the app using the API. Install the Freestyle API client. ```bash npm i freestyle-sandboxes ``` Then you can use the following code to deploy the app: ```ts title="deploy.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils"; // Create a sandboxes client const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { await sandboxes.deployWeb(prepareDirForDeploymentSync("."), { entrypoint: "main.ts", // put whatever domains you want here domains: ["example.style.dev"], }); } deploy(); ``` Finally, to make the deploy happen, run it ```bash bun run deploy.ts ``` ### Next Steps - If you want to add SSR to your Vite app check [this guide out](/web/vite/ssr). - Now that you can deploy Vite apps to Freestyle, you'll probably want to deploy them to [custom domains](/web/domains). To deploy to your own custom domains, you can use the UI in the [Freestyle dashboard](https://admin.freestyle.sh), and to start building self serve to deploy to your users domains you should check out [this guide](/web/domains) } # URL: https://docs.freestyle.sh/web/frameworks/vite/ssr.md Content: --- title: Deploying Vite Projects with SSR description: How to add SSR to a Vite project to Freestyle --- ## Setup When you're setting up a Vite project for SSR, use the `vite-extra` CLI to give you all the parts you need for SSR. ```bash npm create vite-extra@latest ``` This will create a Vite project with the extra configuration you need for SSR like separate server and client entrypoints. For more information check out [Vite's Official SSR Documentation](https://vite.dev/guide/ssr.html#server-side-rendering-ssr) ## Custom Configuration This template comes with the infrastructure for SSR, but you have to add a server to handle it. On Freestyle, we recommend using `hono` for this. We recommend adding `index.js` and installing `hono` as a dependency. ```bash npm i hono ``` Then create a file called `index.js` in the root of your project. This file will be the entrypoint for your server. ```js title="index.js" import fs from "node:fs/promises"; import { Hono } from "hono"; import { serveStatic } from "hono/deno"; import { render } from "./server/entry-server.js"; const templateHtml = await fs.readFile("./client/index.html", "utf-8"); const app = new Hono(); app.get("*", serveStatic({})); app.get("*", async (c) => { try { const url = c.req.url; const template = templateHtml; const rendered = render(url); const html = template .replace(``, rendered.head ?? "") .replace(``, rendered.html ?? ""); return c.html(html); } catch (e) { console.log(e.stack); return c.text(e.stack, 500); } }); Deno.serve(app.fetch); ``` ## Deploying the App ### CLI Install the Freestyle CLI. ```bash npm i freestyle-sh ``` Now deploy it ```bash npx freestyle deploy --domain some.style.dev --web index.js ``` ### API You can also deploy the app using the API. Install the Freestyle API client. ```bash npm i freestyle-sandboxes ``` Then you can use the following code to deploy the app: ```ts title="deploy.ts" import { FreestyleSandboxes } from "freestyle-sandboxes"; import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils"; // Create a sandboxes client const sandboxes = new FreestyleSandboxes({ apiKey: process.env.FREESTYLE_API_KEY!, }); async function deploy() { await sandboxes.deployWeb(prepareDirForDeploymentSync("."), { // put whatever domains you want here domains: ["example.style.dev"], }); } deploy(); ``` Finally, to make the deploy happen, run it ```bash bun run deploy.ts ``` ### Next Steps - Now that you can deploy Vite apps to Freestyle, you'll probably want to deploy them to [custom domains](/web/domains). To deploy to your own custom domains, you can use the UI in the [Freestyle dashboard](https://admin.freestyle.sh), and to start building self serve to deploy to your users domains you should check out [this guide](/web/domains) }