workspaces.yaml
A runner’s workspaces.yaml defines the workspaces it offers. A workspace is
everything a task’s sandbox needs: the image, the setup commands that prepare
it, the agent that runs, and the MCP servers and credentials that agent gets.
The runner reads the file once, at start, from --workspaces
(~/.config/gritz/workspaces.yaml by default). Restart the runner after
changing it, and see Changing a workspace for which
tasks see the change.
workspaces: pets-workshop: description: Example Node.js pets workshop secrets: CLAUDE_CODE_OAUTH_TOKEN: ${env:CLAUDE_CODE_OAUTH_TOKEN} GH_TOKEN: ${sh:gh auth token} container: image: ghcr.io/gritzapp/workspace-debian:latest working_dir: /root commands: - git clone https://x-access-token:${GH_TOKEN}@github.com/example/pets-workshop.git agent: type: claude cwd: /root/pets-workshop prompt: | Run the tests with `npm test` before you open a pull request.gritzapp/workspaces has more
examples, for each agent, MCP servers and private repositories, and builds the
ghcr.io/gritzapp/workspace-* images they use. The images don’t contain gritz:
the runner adds its driver to each sandbox.
Workspace fields
Section titled “Workspace fields”workspaces maps each workspace’s name to its config. The name is what users
pick when they create a task.
| Field | |
|---|---|
description |
Shown next to the name in the Web UI. |
container |
The sandbox for the Docker backend. |
lambda_microvm |
The sandbox for the Lambda MicroVMs backend. |
commands |
Setup commands, run before the agent. |
agent |
The agent and its MCP servers. |
secrets |
Environment variables whose values are masked in the shipped log. See Secrets. |
capabilities |
Extra capabilities for the task’s token. |
container
Section titled “container”| Field | |
|---|---|
image |
The image to run. Required by the Docker backend. Pulled if missing. |
working_dir |
The container’s working directory, where setup commands run. |
user |
The user the driver, setup commands and agent run as. Defaults to the image’s. |
environment |
Environment variables, as a map. Put credentials under secrets: instead. |
volumes |
Bind mounts and volumes, in Docker’s source:target[:options] form. |
networks |
Docker networks to attach the container to. |
network_mode |
Docker’s network mode, such as host. |
group_add |
Extra groups for the container’s user. |
runtime |
The container runtime, such as sysbox-runc for Docker inside the sandbox. |
privileged |
Run the container privileged. |
commands
Section titled “commands”Setup commands prepare the sandbox: clone repositories, install dependencies,
configure tools. Each is run with sh -c, in order, in the sandbox’s working
directory, with the same environment as the agent.
They run once per task, on its first run. A command that fails fails the run; the next run of the task starts again at the command that failed, so the ones before it aren’t repeated.
Their output goes to the task’s log. Reference credentials by variable, as
${GH_TOKEN} rather than ${env:GH_TOKEN}: the sandbox’s shell expands it, so
the command line the driver logs carries the name, not the value.
| Field | |
|---|---|
type |
claude (the default), codex, cursor or copilot. dummy is a stand-in that runs no model, for testing a runner. |
cwd |
The directory the agent starts in. $VAR references are expanded from the sandbox’s environment. |
prompt |
Text added to the agent’s prompt on a task’s first run, after gritz’s own instructions: conventions for this repository, say. |
verbose |
Log every raw line of the agent CLI’s output instead of parsing it. Supported by claude, codex and cursor. See Debugging. |
mcp_servers |
MCP servers for the agent, by name. |
claude, codex, cursor, copilot |
Options for that agent: model, and bin, the CLI to run if not the default (claude, codex and so on). |
The agent CLI has to be installed in the image. Claude Code runs with
--model opus unless claude.model is set; the other agents use their CLI’s
default model.
agent: type: claude cwd: /root/pets-workshop claude: model: sonnetmcp_servers
Section titled “mcp_servers”Each entry is a stdio server, which the agent starts inside the sandbox, or a remote one it connects to over HTTP:
| Field | |
|---|---|
type |
stdio, http or sse. |
command, args, env |
For stdio: the command to run, its arguments and extra environment. |
url, headers |
For http and sse: the server’s URL and request headers. |
workspaces: pets-workshop: secrets: METAMCP_API_KEY: ${env:METAMCP_API_KEY} agent: mcp_servers: meta: type: http url: http://metamcp:12008/metamcp/Default/mcp headers: Authorization: Bearer ${env:METAMCP_API_KEY}gritz passes header values to the agent as written, so a credential in a header
comes from ${env:} or ${sh:}. Declare the same value under secrets:, as
above, so it is masked if it shows up in the log.
The runner adds an MCP server named gritz to every agent, with the tools an
agent uses to work with its task (see the
overview). Don’t define one with that name.
capabilities
Section titled “capabilities”Capabilities let a task’s token do more than read and update its own task. One exists:
github_token: meant to let the task get GitHub App installation tokens for its organisation. The runner accepts it and the agent gets aget_github_tokentool, but the server doesn’t issue these tokens yet: every request fails, and so do thegritz tool git-credentialandgritz tool github-mcphelpers that use them.
Give agents GitHub access with a token under secrets: instead, and reference
it by variable:
workspaces: pets-workshop: secrets: GH_TOKEN: ${env:GH_TOKEN} commands: - git config --global credential.https://github.com.helper '!f() { echo username=x-access-token; echo "password=${GH_TOKEN}"; }; f' - git clone https://github.com/example/pets-workshop.git agent: mcp_servers: github: type: http url: https://api.githubcopilot.com/mcp/ headers: Authorization: Bearer ${env:GH_TOKEN}The credential helper reads GH_TOKEN when git runs it, so the logged command
carries only the name. The MCP header uses ${env:} because header values reach
the agent as written.
Variable expansion
Section titled “Variable expansion”When it loads the file, the runner expands ${namespace:value} in every string
value:
${env:NAME}is the runner’s environment variableNAME, or empty if unset.${sh:command}is the output ofsh -c commandon the runner’s host, with surrounding whitespace trimmed. A failing command fails the load.
Any other namespace is an error. References without a namespace, such as
${GH_TOKEN} or $HOME, are left alone, for the sandbox’s shell to expand.
Environment
Section titled “Environment”The setup commands and the agent see, in order of precedence:
- The workspace’s
secrets. - The variables gritz sets:
GRITZ_TASK_ID,GRITZ_SERVERandGRITZ_TOKEN, the task’s own token. On Lambda MicroVMs, alsolambda_microvm.environment. - The sandbox’s environment: the image’s, plus
container.environmenton Docker.
Changing a workspace
Section titled “Changing a workspace”A task’s sandbox is set up from its workspace once, when it is first created and run, and keeps that setup until the task is archived. After you change a workspace and restart the runner:
container,lambda_microvm(exceptenvironment),commands,capabilitiesandagent, includingpromptandmcp_servers, apply to tasks created from then on. Existing tasks keep what they started with.secretsandlambda_microvm.environmentare sent with each run, so existing tasks get the new values from their next run.
Checking a config
Section titled “Checking a config”gritz workspaces loads a file the way the runner does and prints the result:
gritz workspaces -c ~/.config/gritz/workspaces.yamlIt checks the YAML, the agent’s MCP servers and the capabilities, but not the
container: or lambda_microvm: section, which the backend checks when a run
starts. The output is the config after expansion, so it contains your secrets’
values.