Freestyle Docs

Freestyle / Docs

SSH Access

SSH into Freestyle VMs with scoped identity tokens.

Freestyle VMs accept SSH through beta-ssh.freestyle.sh. Access is controlled with Freestyle identities and tokens.

SSH Format

ssh <vm-id-or-slug>@beta-ssh.freestyle.sh
ssh <vm-id-or-slug>:<access-token>@beta-ssh.freestyle.sh
ssh <vm-id-or-slug>+<linux-user>:<access-token>@beta-ssh.freestyle.sh

If you omit the token from the SSH URL, SSH prompts for it as the password. VM ids work globally. A slug is resolved within the account that owns the access token, so the same slug can safely exist in different accounts.

Create A Token

import { Freestyle } from "freestyle";

const freestyle = new Freestyle();

const slug = "development";
const { vm, vmId } = await freestyle.vms.create({
  // Required: a VM reaches nothing it has not been allowed to.
  firewall: { rules: [{ action: "allow", source: {}, destination: { public: true } }] }, slug });

const { identity } = await freestyle.identities.create();
await identity.permissions.vm.grant({
  vmId,
});

const { token } = await identity.tokens.create();

console.log(`ssh ${slug}:${token}@beta-ssh.freestyle.sh`);

SSH As A Linux User

Linux users are normal guest OS users. Create them inside the VM, then grant an identity access to the matching username.

const slug = "developer-workspace";
const { vm, vmId } = await freestyle.vms.create({
  // Required: a VM reaches nothing it has not been allowed to.
  firewall: { rules: [{ action: "allow", source: {}, destination: { public: true } }] }, slug });

await vm.exec({
  command: `
set -e
if ! id -u developer >/dev/null 2>&1; then
  useradd --create-home --shell /bin/bash developer
fi
if getent group sudo >/dev/null 2>&1; then
  usermod --append --groups sudo developer
fi
mkdir -p /home/developer/workspace
chown -R developer:developer /home/developer
`,
});

const { identity } = await freestyle.identities.create();
await identity.permissions.vm.grant({
  vmId,
  allowedLinuxUsers: ["developer"],
});

const { token } = await identity.tokens.create();

console.log(`ssh ${slug}+developer:${token}@beta-ssh.freestyle.sh`);

No SSH key lives in your VM. When the proxy connects, it generates a throwaway keypair and authorizes the public half inside the VM for a couple of minutes, then the entry expires on its own. Any Linux account you create can use Freestyle SSH as long as it has a valid login shell.

For custom images, keep public key authentication enabled and leave the standard authorized_keys lookup in place:

grep -R "PubkeyAuthentication" /etc/ssh/sshd_config /etc/ssh/sshd_config.d

You do not need to copy Freestyle access tokens into the VM. Tokens stay outside the VM and are checked by the SSH proxy before it connects to the Linux account.

Multiple Developers

Create separate identities when different users or agents should have different VM permissions.

const { identity: alice } = await freestyle.identities.create();
await alice.permissions.vm.grant({
  vmId,
  allowedLinuxUsers: ["alice"],
});

const { identity: bob } = await freestyle.identities.create();
await bob.permissions.vm.grant({
  vmId,
  allowedLinuxUsers: ["bob"],
});

Keep your Freestyle API key server-side. Send only scoped access tokens to clients or developers.

Editor Connections

Editor connections use the same Freestyle SSH proxy and scoped access tokens as command-line SSH. Create an identity, grant it access to the VM, mint a token, then pass that token in the editor connection URL.

freestyle/busybox is too small for editor connections — the editor’s remote server needs a dynamic libc and more memory than it has. Use an Ubuntu base snapshot for IDE work; terminal SSH works everywhere.

For VS Code and Cursor use one of these URL formats:

vscode://vscode-remote/ssh-remote+<vm-id-or-slug>,<access-token>@<vm-id-or-slug>.beta-ssh.freestyle.sh?windowId=_blank
cursor://vscode-remote/ssh-remote+<vm-id-or-slug>,<access-token>@<vm-id-or-slug>.beta-ssh.freestyle.sh?windowId=_blank

For cmux, use its SSH URL format:

cmux://ssh?host=<vm-id-or-slug>.beta-ssh.freestyle.sh&user=<vm-id-or-slug>,<access-token>&name=<name>
esc