Freestyle Docs

Freestyle / Docs

SSH Access

SSH into Freestyle VMs with scoped identity tokens.

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

SSH Format

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

If you omit the token from the SSH URL, SSH prompts for it as the password.

Create A Token

import { freestyle } from "freestyle";

const { vm, vmId } = await freestyle.vms.create();

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

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

console.log(`ssh ${vmId}:${token}@vm-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 { vm, vmId } = await freestyle.vms.create();

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.vms.grant({
  vmId,
  allowedUsers: ["developer"],
});

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

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

Freestyle base images configure sshd with an AuthorizedKeysCommand that reads the current Freestyle VM SSH public key from VM metadata. Any Linux account you create can use Freestyle SSH as long as that SSH configuration remains in place and the account has a valid login shell.

For custom images or heavily modified SSH configs, keep this behavior enabled:

grep -R "AuthorizedKeysCommand /usr/local/bin/fetch-ssh-keys" /etc/ssh/sshd_config /etc/ssh/sshd_config.d
test -x /usr/local/bin/fetch-ssh-keys

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.vms.grant({
  vmId,
  allowedUsers: ["alice"],
});

const { identity: bob } = await freestyle.identities.create();
await bob.permissions.vms.grant({
  vmId,
  allowedUsers: ["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.

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

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

For cmux, use its SSH URL format:

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