Connecting to Workflow Services
The fuzzball workflow connect command provides a convenient way to interact with a service
running in a workflow. Depending on the service definition, it can either open the service’s
public endpoint in a web browser or run a client program locally that connects to the service.
$ fuzzball workflow connect WORKFLOW_ID [flags] [-- COMMAND...]Arguments:
| Argument | Required | Description |
|---|---|---|
WORKFLOW_ID | Yes | The ID of a running workflow |
COMMAND... | No | Arguments forwarded to the client program when the service is configured for client-script mode |
Flags:
| Flag | Description |
|---|---|
--name | Name of the service to connect to. If omitted and the workflow defines a single service, that service is selected automatically; otherwise the CLI prompts interactively. |
--endpoint | Name of the service endpoint to use. If omitted and the service defines a single endpoint, that endpoint is selected automatically; otherwise the CLI prompts interactively. |
The command waits for the target service to become ready (its readiness probe must pass) before opening the browser or launching the client program. While waiting, container logs for the service are streamed to the terminal.
fuzzball workflow connect supports two modes, selected automatically based on the workflow
definition:
If the service has no fuzzball.io/connect/<service-name> annotation declared on the workflow,
the command resolves the URL of the selected endpoint and opens it in the default web browser.
This is the simplest mode and is well suited to services that expose a web UI such as Jupyter
or RStudio.
If the workflow declares an annotation of the form fuzzball.io/connect/<service-name> whose
value is a command to run inside the service container, that command is executed and its
standard output is treated as a client script. The script is saved to a temporary file, made
executable, and run locally. Any extra arguments supplied after -- on the fuzzball workflow connect command line are forwarded to the script.
This mode is useful when connecting to a service requires more than just a URL — for example, to fetch a generated access token or TLS configuration and then launch a local client with the right parameters.
Connect to the only service in a workflow (auto-select service and endpoint):
$ fuzzball workflow connect <workflow-id>Connect to a specific service by name:
$ fuzzball workflow connect --name jupyter <workflow-id>Select a specific endpoint within a service:
$ fuzzball workflow connect --name api-server --endpoint grpc <workflow-id>Forward arguments to a client script (client-script mode):
$ fuzzball workflow connect --name code-server <workflow-id> -- --workspace /home/user/projectThe start commands accept a --connect flag that starts the workflow and then runs
workflow connect against the new workflow as soon as it is ready.
$ fuzzball workflow start --connect my-workflow.fuzz
$ fuzzball application start --connect <application>
$ fuzzball workflow-template start --connect <template>When invoked this way, and the selected service has a fuzzball.io/connect/<service-name>
annotation, the CLI prompts for arguments to pass to the client script.
To enable client-script mode for a service, add a workflow-level annotation whose key is
fuzzball.io/connect/<service-name> and whose value is the command to execute inside the
service container. The command must write the client script to standard output. The script
itself runs locally on the user’s machine.
When the connect command runs inside the service container, Fuzzball injects a number of environment variables that can be used to render runtime values into the generated client script. The most useful ones are:
| Variable | Description |
|---|---|
FB_WORKFLOW_ID | ID of the running workflow |
FB_ACCOUNT_ID | ID of the account that owns the workflow |
FB_JOB_NAME | Name of the service (matches the service key in the workflow) |
FB_ENDPOINT_URL | Public URL of the first endpoint of the service (e.g. https://...) |
FB_ENDPOINT_HOST | Hostname portion of the first endpoint URL |
FB_ENDPOINT_PATH | Path portion of the first endpoint URL |
FB_ENDPOINT_URL_<NAME> | Public URL of the endpoint named <NAME> (uppercased) |
FB_ENDPOINT_HOST_<NAME> | Hostname portion of the endpoint named <NAME> |
FB_ENDPOINT_PATH_<NAME> | Path portion of the endpoint named <NAME> |
Any user-defined env entries declared on the service are also available to the connect
command. Combined with files embedded via the service’s files section, this is enough to
generate a fully parameterized client script without baking values into the container image.
The example below defines a vLLM service that exposes an OpenAI-compatible API on a public
HTTP endpoint and ships a connect command that emits a shell script to launch a local coding
agent (opencode) wired up to the running model.
The service uses two embedded files:
/app/agent-script.tmpl— the template of the local script, with placeholders such as__ENDPOINT_URL__and__WORKFLOW_ID__that will be substituted at connect time./app/generate-agent-command.sh— invoked by thefuzzball.io/connect/vllmannotation, itsed-substitutes the placeholders with values taken from the runtime environment (FB_ENDPOINT_URL,FB_WORKFLOW_ID, and the user-definedVLLM_API_KEY/VLLM_MODEL) and writes the resulting script to standard output.
version: v1
volumes:
data:
reference: volume://user/ephemeral
annotations:
fuzzball.io/connect/vllm: "sh /app/generate-agent-command.sh"
services:
vllm:
image:
uri: docker://vllm/vllm-openai:latest
persist: true
network:
ports:
- name: openai-api
port: 8000
protocol: tcp
endpoints:
- name: openai-vllm
port-name: openai-api
protocol: http
type: subdomain
scope: public
mounts:
data:
location: /home/vllm
resource:
cpu:
cores: 8
memory:
size: 32GB
devices:
nvidia.com/gpu: 1
annotations:
nvidia.com/gpu.model: "H100"
env:
- HOME=/home/vllm
- VLLM_API_KEY=replace-me
- VLLM_MODEL=meta-llama/Llama-3.1-8B-Instruct
script: |
#!/bin/sh
cd $HOME && exec vllm serve "$VLLM_MODEL" \
--tensor-parallel-size 1 \
--host 0.0.0.0 \
--port 8000
readiness-probe:
tcp-socket:
port: 8000
initial-delay-seconds: 60
period-seconds: 30
failure-threshold: 60
success-threshold: 1
files:
/app/agent-script.tmpl: |-
#!/usr/bin/env bash
if [ "$#" -ne 1 ]; then
echo "project directory path is missing"
exit 1
fi
mkdir -p /tmp/__WORKFLOW_ID__
base64 -w0 <<EOF |
{
"\$schema": "https://opencode.ai/config.json",
"model": "local/__MODEL__",
"provider": {
"local": {
"npm": "@ai-sdk/openai-compatible",
"name": "LocalAI (local)",
"options": {
"baseURL": "__ENDPOINT_URL__v1",
"apiKey": "__API_KEY__",
"headers": {
"keep_alive": -1
}
},
"models": {
"__MODEL__": {
"name": "__MODEL__",
"options": {
"max_tokens": 16000
}
}
}
}
}
}
EOF
cat > /tmp/__WORKFLOW_ID__/agent.config
docker run --rm -it \
-v $1:/home/agent/workspace \
--entrypoint /bin/bash \
docker/sandbox-templates:opencode \
-c "mkdir -p /home/agent/.config/opencode && \
cat >/home/agent/.config/opencode/opencode.json \
<(echo $(cat /tmp/__WORKFLOW_ID__/agent.config) | base64 -d) && \
exec opencode"
/app/generate-agent-command.sh: |-
sed -e "s|__ENDPOINT_URL__|$FB_ENDPOINT_URL|g" \
-e "s|__API_KEY__|$VLLM_API_KEY|g" \
-e "s|__MODEL__|$VLLM_MODEL|g" \
-e "s|__WORKFLOW_ID__|$FB_WORKFLOW_ID|g" \
/app/agent-script.tmpl
A user can then start the workflow and immediately drop into the local agent, passing the project directory as the only positional argument forwarded to the script:
$ fuzzball workflow start --connect vllm.fuzz
# (prompt) Enter arguments to pass to client script: /home/user/projects/myappOr, against an already-running workflow:
$ fuzzball workflow connect --name vllm <workflow-id> -- /home/user/projects/myappIn both cases Fuzzball:
- Waits for the
vllmservice readiness probe to pass. - Runs
sh /app/generate-agent-command.shinside thevllmcontainer. - Captures the substituted script from standard output, writes it to a temporary file with
executable permissions, and runs it locally with
/home/user/projects/myappas$1.
- Declare the annotation
fuzzball.io/connect/<service-name>on the workflow. - Embed any helper files (template, substitution script, certificates, etc.) under the
service’s
filessection so they are available inside the container. - The annotation command should print the final, ready-to-run client script to standard output. It must not depend on standard input, since the connect machinery does not forward a TTY to the in-container command.
- Use
FB_*environment variables and any service-levelenventries to inject runtime values into the generated script.
Services without this annotation always use browser mode. See the
workflow syntax reference for the
full set of services and network configuration options, and the
workflow endpoints page for details
on how endpoint URLs are constructed.